summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/accounts/ChooseTypeAndAccountActivity.java2
-rw-r--r--core/java/android/app/ActivityClient.java9
-rw-r--r--core/java/android/app/IActivityClientController.aidl6
-rw-r--r--core/java/android/app/SystemServiceRegistry.java8
-rw-r--r--core/java/android/app/WallpaperManager.java14
-rw-r--r--core/java/android/content/Context.java12
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java56
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/util/RotationUtils.java24
-rw-r--r--core/java/android/view/IWindowManager.aidl3
-rw-r--r--core/java/android/view/RemoteAnimationTarget.java29
-rw-r--r--core/java/android/view/WindowManagerImpl.java2
-rw-r--r--core/java/android/window/TransitionInfo.java68
-rw-r--r--core/java/android/window/WindowProviderService.java2
-rw-r--r--core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java392
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java56
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java136
-rw-r--r--core/java/com/android/internal/app/NoAppsAvailableEmptyStateProvider.java154
-rw-r--r--core/java/com/android/internal/app/NoCrossProfileEmptyStateProvider.java137
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java160
-rw-r--r--core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java112
-rw-r--r--core/java/com/android/internal/app/WorkProfilePausedEmptyStateProvider.java114
-rw-r--r--core/proto/android/providers/settings/secure.proto1
-rw-r--r--core/res/res/values-es/strings.xml2
-rw-r--r--core/res/res/values-gl/strings.xml6
-rw-r--r--core/res/res/values-nl/strings.xml8
-rw-r--r--core/res/res/values-or/strings.xml24
-rw-r--r--core/res/res/values-ru/strings.xml2
-rw-r--r--core/tests/coretests/src/android/util/RotationUtilsTest.java24
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java42
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java450
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java39
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java429
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java65
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java15
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java18
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java75
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java35
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java3
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java4
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java16
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java2
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java23
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java43
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java156
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java217
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java63
-rw-r--r--media/java/android/media/AudioDeviceVolumeManager.java38
-rw-r--r--media/java/android/media/AudioManager.java8
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl5
-rw-r--r--media/java/android/media/Image.java15
-rw-r--r--media/java/android/media/Ringtone.java35
-rw-r--r--media/java/android/media/VolumeInfo.java103
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java18
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml6
-rw-r--r--packages/SettingsLib/res/values/strings.xml4
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SystemUI/Android.bp76
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/animation/Android.bp23
-rw-r--r--packages/SystemUI/animation/TEST_MAPPING7
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt12
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java5
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java219
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt20
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt)0
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt)4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt)4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt)0
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt)2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt)0
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt)0
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt)0
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt)0
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt)0
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt)0
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt (renamed from packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt)2
-rw-r--r--packages/SystemUI/animation/tests/com/android/systemui/animation/InterpolatorsAndroidXTest.kt54
-rw-r--r--packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt2
-rw-r--r--packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt10
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt11
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt198
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderClient.kt479
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt)2
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordanceSlots.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/shared/model/KeyguardQuickAffordanceSlots.kt)0
-rw-r--r--packages/SystemUI/ktfmt_includes.txt2
-rw-r--r--packages/SystemUI/proguard.flags5
-rw-r--r--packages/SystemUI/res-keyguard/drawable/ic_flashlight_off.xml25
-rw-r--r--packages/SystemUI/res-keyguard/drawable/ic_flashlight_on.xml25
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml1
-rw-r--r--packages/SystemUI/res-keyguard/values-h650dp/dimens.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-nl/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values/strings.xml2
-rw-r--r--packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml21
-rw-r--r--packages/SystemUI/res/drawable/screenshare_options_spinner_background.xml35
-rw-r--r--packages/SystemUI/res/drawable/udfps_enroll_checkmark.xml4
-rw-r--r--packages/SystemUI/res/layout/activity_rear_display_education.xml63
-rw-r--r--packages/SystemUI/res/layout/activity_rear_display_education_opened.xml75
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml45
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml29
-rw-r--r--packages/SystemUI/res/layout/media_smartspace_recommendations.xml2
-rw-r--r--packages/SystemUI/res/layout/screen_record_options.xml5
-rw-r--r--packages/SystemUI/res/layout/screen_share_dialog.xml30
-rw-r--r--packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml27
-rw-r--r--packages/SystemUI/res/layout/screen_share_dialog_spinner_text.xml26
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml1
-rw-r--r--packages/SystemUI/res/layout/status_bar_no_notifications.xml27
-rw-r--r--packages/SystemUI/res/raw/rear_display_folded.json1
-rw-r--r--packages/SystemUI/res/raw/rear_display_turnaround.json1
-rw-r--r--packages/SystemUI/res/values-af/strings.xml38
-rw-r--r--packages/SystemUI/res/values-am/strings.xml44
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml44
-rw-r--r--packages/SystemUI/res/values-as/strings.xml38
-rw-r--r--packages/SystemUI/res/values-az/strings.xml38
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml38
-rw-r--r--packages/SystemUI/res/values-be/strings.xml38
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml38
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml38
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml37
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml40
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml38
-rw-r--r--packages/SystemUI/res/values-da/strings.xml38
-rw-r--r--packages/SystemUI/res/values-de/strings.xml38
-rw-r--r--packages/SystemUI/res/values-el/strings.xml35
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml26
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml28
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml26
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml26
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml26
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml38
-rw-r--r--packages/SystemUI/res/values-es/strings.xml38
-rw-r--r--packages/SystemUI/res/values-et/strings.xml38
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml38
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml38
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml38
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml37
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml38
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml40
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml38
-rw-r--r--packages/SystemUI/res/values-h700dp/dimens.xml20
-rw-r--r--packages/SystemUI/res/values-h800dp/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml44
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml35
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml38
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml38
-rw-r--r--packages/SystemUI/res/values-in/strings.xml38
-rw-r--r--packages/SystemUI/res/values-is/strings.xml38
-rw-r--r--packages/SystemUI/res/values-it/strings.xml32
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml29
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml35
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml38
-rw-r--r--packages/SystemUI/res/values-km/strings.xml38
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml32
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml38
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml38
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml38
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml32
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml38
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml29
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml44
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml35
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml38
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml35
-rw-r--r--packages/SystemUI/res/values-my/strings.xml44
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml38
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml44
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml44
-rw-r--r--packages/SystemUI/res/values-or/strings.xml39
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml38
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml38
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml44
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml26
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml44
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml35
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml38
-rw-r--r--packages/SystemUI/res/values-si/strings.xml38
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml40
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml38
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml38
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml38
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml38
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml44
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/dimens.xml5
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml44
-rw-r--r--packages/SystemUI/res/values-te/strings.xml32
-rw-r--r--packages/SystemUI/res/values-th/strings.xml26
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml35
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml38
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml44
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml38
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml35
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml38
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml40
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml32
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml38
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml38
-rw-r--r--packages/SystemUI/res/values/colors.xml7
-rw-r--r--packages/SystemUI/res/values/dimens.xml27
-rw-r--r--packages/SystemUI/res/values/strings.xml21
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt10
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt4
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java19
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java27
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmergencyButton.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java161
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/CoreStartable.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java237
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt164
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt146
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt184
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt129
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt119
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt170
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt149
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBufferHelper.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt230
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/table/TableRowLogger.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java208
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt151
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeController.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java255
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java286
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/WifiTableLog.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiActivityModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/UserModule.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java4
-rw-r--r--packages/SystemUI/tests/robolectric/config/robolectric.properties16
-rw-r--r--packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiResourceLoadingTest.java35
-rw-r--r--packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiRoboBase.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java42
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java140
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java105
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsEnrollViewTest.java50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt109
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt90
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt506
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt187
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java125
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt215
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt)40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt219
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt105
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt160
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt167
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt154
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt102
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt464
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java134
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt173
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java51
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt121
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt)5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt139
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt144
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt40
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt43
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeFlashlightController.java47
-rw-r--r--services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java4
-rw-r--r--services/core/java/com/android/server/DockObserver.java69
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java7
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java80
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java7
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java17
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java6
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java9
-rw-r--r--services/core/java/com/android/server/trust/TrustAgentWrapper.java22
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java47
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java9
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java20
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java13
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java16
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java26
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java1
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimationRunner.java1
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java55
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java43
-rw-r--r--services/core/java/com/android/server/wm/Transition.java95
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java5
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java23
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java60
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java39
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/DockObserverTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java44
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java56
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java4
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java12
605 files changed, 17250 insertions, 4496 deletions
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index f623295dee3e..6e02390bb711 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -402,7 +402,7 @@ public class ChooseTypeAndAccountActivity extends Activity
mExistingAccounts = AccountManager.get(this).getAccountsForPackage(mCallingPackage,
mCallingUid);
intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivityForResult(intent, REQUEST_ADD_ACCOUNT);
+ startActivityForResult(new Intent(intent), REQUEST_ADD_ACCOUNT);
return;
}
} catch (OperationCanceledException e) {
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 4cf48abc2ed3..324b8e7a784f 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -227,12 +227,13 @@ public class ActivityClient {
}
/**
- * Returns the windowing mode of the task that hosts the activity, or {@code -1} if task is not
- * found.
+ * Returns the {@link Configuration} of the task which hosts the Activity, or {@code null} if
+ * the task {@link Configuration} cannot be obtained.
*/
- public int getTaskWindowingMode(IBinder activityToken) {
+ @Nullable
+ public Configuration getTaskConfiguration(IBinder activityToken) {
try {
- return getActivityClientController().getTaskWindowingMode(activityToken);
+ return getActivityClientController().getTaskConfiguration(activityToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 62481ba8f251..8b655b9bf315 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -78,7 +78,11 @@ interface IActivityClientController {
boolean willActivityBeVisible(in IBinder token);
int getDisplayId(in IBinder activityToken);
int getTaskForActivity(in IBinder token, in boolean onlyRoot);
- int getTaskWindowingMode(in IBinder activityToken);
+ /**
+ * Returns the {@link Configuration} of the task which hosts the Activity, or {@code null} if
+ * the task {@link Configuration} cannot be obtained.
+ */
+ Configuration getTaskConfiguration(in IBinder activityToken);
IBinder getActivityTokenBelow(IBinder token);
ComponentName getCallingActivity(in IBinder token);
String getCallingPackage(in IBinder token);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index b6189692107e..ec100c2fa0c0 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -115,6 +115,7 @@ import android.location.CountryDetector;
import android.location.ICountryDetector;
import android.location.ILocationManager;
import android.location.LocationManager;
+import android.media.AudioDeviceVolumeManager;
import android.media.AudioManager;
import android.media.MediaFrameworkInitializer;
import android.media.MediaFrameworkPlatformInitializer;
@@ -339,6 +340,13 @@ public final class SystemServiceRegistry {
return new AudioManager(ctx);
}});
+ registerService(Context.AUDIO_DEVICE_VOLUME_SERVICE, AudioDeviceVolumeManager.class,
+ new CachedServiceFetcher<AudioDeviceVolumeManager>() {
+ @Override
+ public AudioDeviceVolumeManager createService(ContextImpl ctx) {
+ return new AudioDeviceVolumeManager(ctx);
+ }});
+
registerService(Context.MEDIA_ROUTER_SERVICE, MediaRouter.class,
new CachedServiceFetcher<MediaRouter>() {
@Override
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 14fe5226b9d9..3e6283e25aa5 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1132,12 +1132,26 @@ public class WallpaperManager {
* @return the dimensions of system wallpaper
* @hide
*/
+ @Nullable
public Rect peekBitmapDimensions() {
return sGlobals.peekWallpaperDimensions(
mContext, true /* returnDefault */, mContext.getUserId());
}
/**
+ * Peek the dimensions of given wallpaper of the user without decoding it.
+ *
+ * @param which Wallpaper type. Must be either {@link #FLAG_SYSTEM} or
+ * {@link #FLAG_LOCK}.
+ * @return the dimensions of system wallpaper
+ * @hide
+ */
+ @Nullable
+ public Rect peekBitmapDimensions(@SetWallpaperFlags int which) {
+ return peekBitmapDimensions();
+ }
+
+ /**
* Get an open, readable file descriptor to the given wallpaper image file.
* The caller is responsible for closing the file descriptor when done ingesting the file.
*
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fce23cf6819a..77ca48a8ed1d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3846,6 +3846,7 @@ public abstract class Context {
WIFI_RTT_RANGING_SERVICE,
NSD_SERVICE,
AUDIO_SERVICE,
+ AUDIO_DEVICE_VOLUME_SERVICE,
AUTH_SERVICE,
FINGERPRINT_SERVICE,
//@hide: FACE_SERVICE,
@@ -4687,6 +4688,17 @@ public abstract class Context {
public static final String AUDIO_SERVICE = "audio";
/**
+ * @hide
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.media.AudioDeviceVolumeManager} for handling management of audio device
+ * (e.g. speaker, USB headset) volume.
+ *
+ * @see #getSystemService(String)
+ * @see android.media.AudioDeviceVolumeManager
+ */
+ public static final String AUDIO_DEVICE_VOLUME_SERVICE = "audio_device_volume";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.media.MediaTranscodingManager} for transcoding media.
*
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 085bfca158c1..5c1da1112e2a 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -938,7 +938,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
public void onPointerDown(long requestId, int sensorId, int x, int y,
float minor, float major) {
if (mService == null) {
- Slog.w(TAG, "onFingerDown: no fingerprint service");
+ Slog.w(TAG, "onPointerDown: no fingerprint service");
return;
}
@@ -955,7 +955,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
public void onPointerUp(long requestId, int sensorId) {
if (mService == null) {
- Slog.w(TAG, "onFingerDown: no fingerprint service");
+ Slog.w(TAG, "onPointerUp: no fingerprint service");
return;
}
@@ -967,6 +967,58 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
+ * TODO(b/218388821): The parameter list should be replaced with PointerContext.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onPointerDown(
+ long requestId,
+ int sensorId,
+ int pointerId,
+ float x,
+ float y,
+ float minor,
+ float major,
+ float orientation,
+ long time,
+ long gestureStart,
+ boolean isAod) {
+ if (mService == null) {
+ Slog.w(TAG, "onPointerDown: no fingerprint service");
+ return;
+ }
+
+ // TODO(b/218388821): Propagate all the parameters to FingerprintService.
+ Slog.e(TAG, "onPointerDown: not implemented!");
+ }
+
+ /**
+ * TODO(b/218388821): The parameter list should be replaced with PointerContext.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onPointerUp(
+ long requestId,
+ int sensorId,
+ int pointerId,
+ float x,
+ float y,
+ float minor,
+ float major,
+ float orientation,
+ long time,
+ long gestureStart,
+ boolean isAod) {
+ if (mService == null) {
+ Slog.w(TAG, "onPointerUp: no fingerprint service");
+ return;
+ }
+
+ // TODO(b/218388821): Propagate all the parameters to FingerprintService.
+ Slog.e(TAG, "onPointerUp: not implemented!");
+ }
+
+ /**
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fd45fca53722..ed59456e2281 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7776,6 +7776,13 @@ public final class Settings {
"high_text_contrast_enabled";
/**
+ * The color contrast, float in [-1, 1], 1 being the highest contrast.
+ *
+ * @hide
+ */
+ public static final String CONTRAST_LEVEL = "contrast_level";
+
+ /**
* Setting that specifies whether the display magnification is enabled via a system-wide
* triple tap gesture. Display magnifications allows the user to zoom in the display content
* and is targeted to low vision users. The current magnification scale is controlled by
diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java
index c54d9b604ba2..3e7c67e72031 100644
--- a/core/java/android/util/RotationUtils.java
+++ b/core/java/android/util/RotationUtils.java
@@ -25,6 +25,7 @@ import android.annotation.Dimension;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
@@ -193,6 +194,29 @@ public class RotationUtils {
}
/**
+ * Same as {@link #rotatePoint}, but for float coordinates.
+ */
+ public static void rotatePointF(PointF inOutPoint, @Rotation int rotation,
+ float parentW, float parentH) {
+ float origX = inOutPoint.x;
+ switch (rotation) {
+ case ROTATION_0:
+ return;
+ case ROTATION_90:
+ inOutPoint.x = inOutPoint.y;
+ inOutPoint.y = parentW - origX;
+ return;
+ case ROTATION_180:
+ inOutPoint.x = parentW - inOutPoint.x;
+ inOutPoint.y = parentH - inOutPoint.y;
+ return;
+ case ROTATION_270:
+ inOutPoint.x = parentH - inOutPoint.y;
+ inOutPoint.y = origX;
+ }
+ }
+
+ /**
* Sets a matrix such that given a rotation, it transforms physical display
* coordinates to that rotation's logical coordinates.
*
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index bb26c46142d2..1f095ac5f40d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -727,9 +727,8 @@ interface IWindowManager
* If invoked through a package other than a launcher app, returns an empty list.
*
* @param displayId the id of the logical display
- * @param packageName the name of the calling package
*/
- List<DisplayInfo> getPossibleDisplayInfo(int displayId, String packageName);
+ List<DisplayInfo> getPossibleDisplayInfo(int displayId);
/**
* Called to show global actions.
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index e407707231ca..8816eac33cfb 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -35,6 +35,7 @@ import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import android.annotation.ColorInt;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
@@ -175,10 +176,16 @@ public class RemoteAnimationTarget implements Parcelable {
public final Rect screenSpaceBounds;
/**
- * The starting bounds of the source container in screen space coordinates. This is {@code null}
- * if the animation target isn't MODE_CHANGING. Since this is the starting bounds, it's size
- * should be equivalent to the size of the starting thumbnail. Note that sourceContainerBounds
- * is the end bounds of a change transition.
+ * The starting bounds of the source container in screen space coordinates.
+ * For {@link #MODE_OPENING}, this will be equivalent to {@link #screenSpaceBounds}.
+ * For {@link #MODE_CLOSING}, this will be equivalent to {@link #screenSpaceBounds} unless the
+ * closing container is also resizing. For example, when ActivityEmbedding split pair becomes
+ * stacked, the container on the back will be resized to fullscreen, but will also be covered
+ * (closing) by the container in the front.
+ * For {@link #MODE_CHANGING}, since this is the starting bounds, its size should be equivalent
+ * to the bounds of the starting thumbnail.
+ *
+ * Note that {@link #screenSpaceBounds} is the end bounds of a transition.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public final Rect startBounds;
@@ -247,7 +254,8 @@ public class RemoteAnimationTarget implements Parcelable {
Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
Rect localBounds, Rect screenSpaceBounds,
WindowConfiguration windowConfig, boolean isNotInRecents,
- SurfaceControl startLeash, Rect startBounds, ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl startLeash, @Nullable Rect startBounds,
+ ActivityManager.RunningTaskInfo taskInfo,
boolean allowEnterPip) {
this(taskId, mode, leash, isTranslucent, clipRect, contentInsets, prefixOrderIndex,
position, localBounds, screenSpaceBounds, windowConfig, isNotInRecents, startLeash,
@@ -258,7 +266,7 @@ public class RemoteAnimationTarget implements Parcelable {
Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
Rect localBounds, Rect screenSpaceBounds,
WindowConfiguration windowConfig, boolean isNotInRecents,
- SurfaceControl startLeash, Rect startBounds,
+ SurfaceControl startLeash, @Nullable Rect startBounds,
ActivityManager.RunningTaskInfo taskInfo, boolean allowEnterPip,
@WindowManager.LayoutParams.WindowType int windowType) {
this.mode = mode;
@@ -275,10 +283,13 @@ public class RemoteAnimationTarget implements Parcelable {
this.windowConfiguration = windowConfig;
this.isNotInRecents = isNotInRecents;
this.startLeash = startLeash;
- this.startBounds = startBounds == null ? null : new Rect(startBounds);
this.taskInfo = taskInfo;
this.allowEnterPip = allowEnterPip;
this.windowType = windowType;
+ // Same as screenSpaceBounds if the window is not resizing.
+ this.startBounds = startBounds == null
+ ? new Rect(screenSpaceBounds)
+ : new Rect(startBounds);
}
public RemoteAnimationTarget(Parcel in) {
@@ -399,9 +410,7 @@ public class RemoteAnimationTarget implements Parcelable {
if (startLeash != null) {
startLeash.dumpDebug(proto, START_LEASH);
}
- if (startBounds != null) {
- startBounds.dumpDebug(proto, START_BOUNDS);
- }
+ startBounds.dumpDebug(proto, START_BOUNDS);
proto.end(token);
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 20cdad42b4cc..baf99254f290 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -357,7 +357,7 @@ public final class WindowManagerImpl implements WindowManager {
List<DisplayInfo> possibleDisplayInfos;
try {
possibleDisplayInfos = WindowManagerGlobal.getWindowManagerService()
- .getPossibleDisplayInfo(displayId, mContext.getPackageName());
+ .getPossibleDisplayInfo(displayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 0956a71bd92d..666f316f7c1d 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -414,6 +414,53 @@ public final class TransitionInfo implements Parcelable {
return false;
}
+ /**
+ * Releases temporary-for-animation surfaces referenced by this to potentially free up memory.
+ * This includes root-leash and snapshots.
+ */
+ public void releaseAnimSurfaces() {
+ for (int i = mChanges.size() - 1; i >= 0; --i) {
+ final Change c = mChanges.get(i);
+ if (c.mSnapshot != null) {
+ c.mSnapshot.release();
+ c.mSnapshot = null;
+ }
+ }
+ if (mRootLeash != null) {
+ mRootLeash.release();
+ }
+ }
+
+ /**
+ * Releases ALL the surfaces referenced by this to potentially free up memory. Do NOT use this
+ * if the surface-controls get stored and used elsewhere in the process. To just release
+ * temporary-for-animation surfaces, use {@link #releaseAnimSurfaces}.
+ */
+ public void releaseAllSurfaces() {
+ releaseAnimSurfaces();
+ for (int i = mChanges.size() - 1; i >= 0; --i) {
+ mChanges.get(i).getLeash().release();
+ }
+ }
+
+ /**
+ * Makes a copy of this as if it were parcel'd and unparcel'd. This implies that surfacecontrol
+ * refcounts are incremented which allows the "remote" receiver to release them without breaking
+ * the caller's references. Use this only if you need to "send" this to a local function which
+ * assumes it is being called from a remote caller.
+ */
+ public TransitionInfo localRemoteCopy() {
+ final TransitionInfo out = new TransitionInfo(mType, mFlags);
+ for (int i = 0; i < mChanges.size(); ++i) {
+ out.mChanges.add(mChanges.get(i).localRemoteCopy());
+ }
+ out.mRootLeash = mRootLeash != null ? new SurfaceControl(mRootLeash, "localRemote") : null;
+ // Doesn't have any native stuff, so no need for actual copy
+ out.mOptions = mOptions;
+ out.mRootOffset.set(mRootOffset);
+ return out;
+ }
+
/** Represents the change a WindowContainer undergoes during a transition */
public static final class Change implements Parcelable {
private final WindowContainerToken mContainer;
@@ -466,6 +513,27 @@ public final class TransitionInfo implements Parcelable {
mSnapshotLuma = in.readFloat();
}
+ private Change localRemoteCopy() {
+ final Change out = new Change(mContainer, new SurfaceControl(mLeash, "localRemote"));
+ out.mParent = mParent;
+ out.mLastParent = mLastParent;
+ out.mMode = mMode;
+ out.mFlags = mFlags;
+ out.mStartAbsBounds.set(mStartAbsBounds);
+ out.mEndAbsBounds.set(mEndAbsBounds);
+ out.mEndRelOffset.set(mEndRelOffset);
+ out.mTaskInfo = mTaskInfo;
+ out.mAllowEnterPip = mAllowEnterPip;
+ out.mStartRotation = mStartRotation;
+ out.mEndRotation = mEndRotation;
+ out.mEndFixedRotation = mEndFixedRotation;
+ out.mRotationAnimation = mRotationAnimation;
+ out.mBackgroundColor = mBackgroundColor;
+ out.mSnapshot = mSnapshot != null ? new SurfaceControl(mSnapshot, "localRemote") : null;
+ out.mSnapshotLuma = mSnapshotLuma;
+ return out;
+ }
+
/** Sets the parent of this change's container. The parent must be a participant or null. */
public void setParent(@Nullable WindowContainerToken parent) {
mParent = parent;
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index fdc3e5af8d8b..f2ae973500af 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -146,7 +146,7 @@ public abstract class WindowProviderService extends Service implements WindowPro
@SuppressLint("OnNameExpected")
@Override
- public void onConfigurationChanged(@Nullable Configuration configuration) {
+ public void onConfigurationChanged(@NonNull Configuration configuration) {
// This is only called from WindowTokenClient.
mCallbacksController.dispatchConfigurationChanged(configuration);
}
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index fc526203beed..23167382ec3b 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -17,18 +17,15 @@ package com.android.internal.app;
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.AppGlobals;
-import android.app.admin.DevicePolicyEventLogger;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.AsyncTask;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.UserManager;
-import android.stats.devicepolicy.DevicePolicyEnums;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -60,73 +57,31 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
private final Context mContext;
private int mCurrentPage;
private OnProfileSelectedListener mOnProfileSelectedListener;
- private OnSwitchOnWorkSelectedListener mOnSwitchOnWorkSelectedListener;
private Set<Integer> mLoadedPages;
- private final UserHandle mPersonalProfileUserHandle;
+ private final EmptyStateProvider mEmptyStateProvider;
private final UserHandle mWorkProfileUserHandle;
- private Injector mInjector;
- private boolean mIsWaitingToEnableWorkProfile;
+ private final QuietModeManager mQuietModeManager;
AbstractMultiProfilePagerAdapter(Context context, int currentPage,
- UserHandle personalProfileUserHandle,
+ EmptyStateProvider emptyStateProvider,
+ QuietModeManager quietModeManager,
UserHandle workProfileUserHandle) {
mContext = Objects.requireNonNull(context);
mCurrentPage = currentPage;
mLoadedPages = new HashSet<>();
- mPersonalProfileUserHandle = personalProfileUserHandle;
mWorkProfileUserHandle = workProfileUserHandle;
- UserManager userManager = context.getSystemService(UserManager.class);
- mInjector = new Injector() {
- @Override
- public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
- int targetUserId) {
- return AbstractMultiProfilePagerAdapter.this
- .hasCrossProfileIntents(intents, sourceUserId, targetUserId);
- }
-
- @Override
- public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
- return userManager.isQuietModeEnabled(workProfileUserHandle);
- }
-
- @Override
- public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
- AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
- userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
- });
- mIsWaitingToEnableWorkProfile = true;
- }
- };
- }
-
- protected void markWorkProfileEnabledBroadcastReceived() {
- mIsWaitingToEnableWorkProfile = false;
- }
-
- protected boolean isWaitingToEnableWorkProfile() {
- return mIsWaitingToEnableWorkProfile;
- }
-
- /**
- * Overrides the default {@link Injector} for testing purposes.
- */
- @VisibleForTesting
- public void setInjector(Injector injector) {
- mInjector = injector;
+ mEmptyStateProvider = emptyStateProvider;
+ mQuietModeManager = quietModeManager;
}
- protected boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
- return mInjector.isQuietModeEnabled(workProfileUserHandle);
+ private boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ return mQuietModeManager.isQuietModeEnabled(workProfileUserHandle);
}
void setOnProfileSelectedListener(OnProfileSelectedListener listener) {
mOnProfileSelectedListener = listener;
}
- void setOnSwitchOnWorkSelectedListener(OnSwitchOnWorkSelectedListener listener) {
- mOnSwitchOnWorkSelectedListener = listener;
- }
-
Context getContext() {
return mContext;
}
@@ -280,8 +235,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
abstract @Nullable ViewGroup getInactiveAdapterView();
- abstract String getMetricsCategory();
-
/**
* Rebuilds the tab that is currently visible to the user.
* <p>Returns {@code true} if rebuild has completed.
@@ -317,41 +270,18 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
}
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
- if (shouldShowNoCrossProfileIntentsEmptyState(activeListAdapter)) {
+ if (shouldSkipRebuild(activeListAdapter)) {
activeListAdapter.postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ true);
return false;
}
return activeListAdapter.rebuildList(doPostProcessing);
}
- private boolean shouldShowNoCrossProfileIntentsEmptyState(
- ResolverListAdapter activeListAdapter) {
- UserHandle listUserHandle = activeListAdapter.getUserHandle();
- return UserHandle.myUserId() != listUserHandle.getIdentifier()
- && allowShowNoCrossProfileIntentsEmptyState()
- && !mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
- UserHandle.myUserId(), listUserHandle.getIdentifier());
- }
-
- boolean allowShowNoCrossProfileIntentsEmptyState() {
- return true;
+ private boolean shouldSkipRebuild(ResolverListAdapter activeListAdapter) {
+ EmptyState emptyState = mEmptyStateProvider.getEmptyState(activeListAdapter);
+ return emptyState != null && emptyState.shouldSkipDataRebuild();
}
- protected abstract void showWorkProfileOffEmptyState(
- ResolverListAdapter activeListAdapter, View.OnClickListener listener);
-
- protected abstract void showNoPersonalToWorkIntentsEmptyState(
- ResolverListAdapter activeListAdapter);
-
- protected abstract void showNoPersonalAppsAvailableEmptyState(
- ResolverListAdapter activeListAdapter);
-
- protected abstract void showNoWorkAppsAvailableEmptyState(
- ResolverListAdapter activeListAdapter);
-
- protected abstract void showNoWorkToPersonalIntentsEmptyState(
- ResolverListAdapter activeListAdapter);
-
/**
* The empty state screens are shown according to their priority:
* <ol>
@@ -366,103 +296,88 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
* anyway.
*/
void showEmptyResolverListEmptyState(ResolverListAdapter listAdapter) {
- if (maybeShowNoCrossProfileIntentsEmptyState(listAdapter)) {
+ final EmptyState emptyState = mEmptyStateProvider.getEmptyState(listAdapter);
+
+ if (emptyState == null) {
return;
}
- if (maybeShowWorkProfileOffEmptyState(listAdapter)) {
- return;
+
+ emptyState.onEmptyStateShown();
+
+ View.OnClickListener clickListener = null;
+
+ if (emptyState.getButtonClickListener() != null) {
+ clickListener = v -> emptyState.getButtonClickListener().onClick(() -> {
+ ProfileDescriptor descriptor = getItem(
+ userHandleToPageIndex(listAdapter.getUserHandle()));
+ AbstractMultiProfilePagerAdapter.this.showSpinner(descriptor.getEmptyStateView());
+ });
}
- maybeShowNoAppsAvailableEmptyState(listAdapter);
+
+ showEmptyState(listAdapter, emptyState, clickListener);
}
- private boolean maybeShowNoCrossProfileIntentsEmptyState(ResolverListAdapter listAdapter) {
- if (!shouldShowNoCrossProfileIntentsEmptyState(listAdapter)) {
- return false;
- }
- if (listAdapter.getUserHandle().equals(mPersonalProfileUserHandle)) {
- DevicePolicyEventLogger.createEvent(
- DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
- .setStrings(getMetricsCategory())
- .write();
- showNoWorkToPersonalIntentsEmptyState(listAdapter);
- } else {
- DevicePolicyEventLogger.createEvent(
- DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK)
- .setStrings(getMetricsCategory())
- .write();
- showNoPersonalToWorkIntentsEmptyState(listAdapter);
+ /**
+ * Class to get user id of the current process
+ */
+ public static class MyUserIdProvider {
+ /**
+ * @return user id of the current process
+ */
+ public int getMyUserId() {
+ return UserHandle.myUserId();
}
- return true;
}
/**
- * Returns {@code true} if the work profile off empty state screen is shown.
+ * Utility class to check if there are cross profile intents, it is in a separate class so
+ * it could be mocked in tests
*/
- private boolean maybeShowWorkProfileOffEmptyState(ResolverListAdapter listAdapter) {
- UserHandle listUserHandle = listAdapter.getUserHandle();
- if (!listUserHandle.equals(mWorkProfileUserHandle)
- || !mInjector.isQuietModeEnabled(mWorkProfileUserHandle)
- || listAdapter.getCount() == 0) {
- return false;
- }
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
- .setStrings(getMetricsCategory())
- .write();
- showWorkProfileOffEmptyState(listAdapter,
- v -> {
- ProfileDescriptor descriptor = getItem(
- userHandleToPageIndex(listAdapter.getUserHandle()));
- showSpinner(descriptor.getEmptyStateView());
- if (mOnSwitchOnWorkSelectedListener != null) {
- mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
- }
- mInjector.requestQuietModeEnabled(false, mWorkProfileUserHandle);
- });
- return true;
- }
-
- private void maybeShowNoAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- UserHandle listUserHandle = listAdapter.getUserHandle();
- if (mWorkProfileUserHandle != null
- && (UserHandle.myUserId() == listUserHandle.getIdentifier()
- || !hasAppsInOtherProfile(listAdapter))) {
- DevicePolicyEventLogger.createEvent(
- DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED)
- .setStrings(getMetricsCategory())
- .setBoolean(/*isPersonalProfile*/ listUserHandle == mPersonalProfileUserHandle)
- .write();
- if (listUserHandle == mPersonalProfileUserHandle) {
- showNoPersonalAppsAvailableEmptyState(listAdapter);
- } else {
- showNoWorkAppsAvailableEmptyState(listAdapter);
- }
- } else if (mWorkProfileUserHandle == null) {
- showConsumerUserNoAppsAvailableEmptyState(listAdapter);
+ public static class CrossProfileIntentsChecker {
+
+ private final ContentResolver mContentResolver;
+
+ public CrossProfileIntentsChecker(@NonNull ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
}
- }
- protected void showEmptyState(ResolverListAdapter activeListAdapter, String title,
- String subtitle) {
- showEmptyState(activeListAdapter, title, subtitle, /* buttonOnClick */ null);
+ /**
+ * Returns {@code true} if at least one of the provided {@code intents} can be forwarded
+ * from {@code source} (user id) to {@code target} (user id).
+ */
+ public boolean hasCrossProfileIntents(List<Intent> intents, @UserIdInt int source,
+ @UserIdInt int target) {
+ IPackageManager packageManager = AppGlobals.getPackageManager();
+
+ return intents.stream().anyMatch(intent ->
+ null != IntentForwarderActivity.canForward(intent, source, target,
+ packageManager, mContentResolver));
+ }
}
- protected void showEmptyState(ResolverListAdapter activeListAdapter,
- String title, String subtitle, View.OnClickListener buttonOnClick) {
+ protected void showEmptyState(ResolverListAdapter activeListAdapter, EmptyState emptyState,
+ View.OnClickListener buttonOnClick) {
ProfileDescriptor descriptor = getItem(
userHandleToPageIndex(activeListAdapter.getUserHandle()));
descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
ViewGroup emptyStateView = descriptor.getEmptyStateView();
- resetViewVisibilitiesForWorkProfileEmptyState(emptyStateView);
+ resetViewVisibilitiesForEmptyState(emptyStateView);
emptyStateView.setVisibility(View.VISIBLE);
View container = emptyStateView.findViewById(R.id.resolver_empty_state_container);
setupContainerPadding(container);
TextView titleView = emptyStateView.findViewById(R.id.resolver_empty_state_title);
- titleView.setText(title);
+ String title = emptyState.getTitle();
+ if (title != null) {
+ titleView.setVisibility(View.VISIBLE);
+ titleView.setText(title);
+ } else {
+ titleView.setVisibility(View.GONE);
+ }
TextView subtitleView = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
+ String subtitle = emptyState.getSubtitle();
if (subtitle != null) {
subtitleView.setVisibility(View.VISIBLE);
subtitleView.setText(subtitle);
@@ -470,6 +385,9 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
subtitleView.setVisibility(View.GONE);
}
+ View defaultEmptyText = emptyStateView.findViewById(R.id.empty);
+ defaultEmptyText.setVisibility(emptyState.useDefaultEmptyView() ? View.VISIBLE : View.GONE);
+
Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
button.setOnClickListener(buttonOnClick);
@@ -483,22 +401,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
*/
protected void setupContainerPadding(View container) {}
- private void showConsumerUserNoAppsAvailableEmptyState(ResolverListAdapter activeListAdapter) {
- ProfileDescriptor descriptor = getItem(
- userHandleToPageIndex(activeListAdapter.getUserHandle()));
- descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
- View emptyStateView = descriptor.getEmptyStateView();
- resetViewVisibilitiesForConsumerUserEmptyState(emptyStateView);
- emptyStateView.setVisibility(View.VISIBLE);
-
- activeListAdapter.markTabLoaded();
- }
-
- private boolean isSpinnerShowing(View emptyStateView) {
- return emptyStateView.findViewById(R.id.resolver_empty_state_progress).getVisibility()
- == View.VISIBLE;
- }
-
private void showSpinner(View emptyStateView) {
emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.INVISIBLE);
emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.INVISIBLE);
@@ -506,7 +408,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
emptyStateView.findViewById(R.id.empty).setVisibility(View.GONE);
}
- private void resetViewVisibilitiesForWorkProfileEmptyState(View emptyStateView) {
+ private void resetViewVisibilitiesForEmptyState(View emptyStateView) {
emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.VISIBLE);
emptyStateView.findViewById(R.id.resolver_empty_state_subtitle).setVisibility(View.VISIBLE);
emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.INVISIBLE);
@@ -514,14 +416,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
emptyStateView.findViewById(R.id.empty).setVisibility(View.GONE);
}
- private void resetViewVisibilitiesForConsumerUserEmptyState(View emptyStateView) {
- emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.GONE);
- emptyStateView.findViewById(R.id.resolver_empty_state_subtitle).setVisibility(View.GONE);
- emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.GONE);
- emptyStateView.findViewById(R.id.resolver_empty_state_progress).setVisibility(View.GONE);
- emptyStateView.findViewById(R.id.empty).setVisibility(View.VISIBLE);
- }
-
protected void showListView(ResolverListAdapter activeListAdapter) {
ProfileDescriptor descriptor = getItem(
userHandleToPageIndex(activeListAdapter.getUserHandle()));
@@ -530,33 +424,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
emptyStateView.setVisibility(View.GONE);
}
- private boolean hasCrossProfileIntents(List<Intent> intents, int source, int target) {
- IPackageManager packageManager = AppGlobals.getPackageManager();
- ContentResolver contentResolver = mContext.getContentResolver();
- for (Intent intent : intents) {
- if (IntentForwarderActivity.canForward(intent, source, target, packageManager,
- contentResolver) != null) {
- return true;
- }
- }
- return false;
- }
-
- private boolean hasAppsInOtherProfile(ResolverListAdapter adapter) {
- if (mWorkProfileUserHandle == null) {
- return false;
- }
- List<ResolverActivity.ResolvedComponentInfo> resolversForIntent =
- adapter.getResolversForUser(UserHandle.of(UserHandle.myUserId()));
- for (ResolverActivity.ResolvedComponentInfo info : resolversForIntent) {
- ResolveInfo resolveInfo = info.getResolveInfoAt(0);
- if (resolveInfo.targetUserId != UserHandle.USER_CURRENT) {
- return true;
- }
- }
- return false;
- }
-
boolean shouldShowEmptyStateScreen(ResolverListAdapter listAdapter) {
int count = listAdapter.getUnfilteredCount();
return (count == 0 && listAdapter.getPlaceholderCount() == 0)
@@ -600,6 +467,98 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
}
/**
+ * Returns an empty state to show for the current profile page (tab) if necessary.
+ * This could be used e.g. to show a blocker on a tab if device management policy doesn't
+ * allow to use it or there are no apps available.
+ */
+ public interface EmptyStateProvider {
+ /**
+ * When a non-null empty state is returned the corresponding profile page will show
+ * this empty state
+ * @param resolverListAdapter the current adapter
+ */
+ @Nullable
+ default EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
+ return null;
+ }
+ }
+
+ /**
+ * Empty state provider that combines multiple providers. Providers earlier in the list have
+ * priority, that is if there is a provider that returns non-null empty state then all further
+ * providers will be ignored.
+ */
+ public static class CompositeEmptyStateProvider implements EmptyStateProvider {
+
+ private final EmptyStateProvider[] mProviders;
+
+ public CompositeEmptyStateProvider(EmptyStateProvider... providers) {
+ mProviders = providers;
+ }
+
+ @Nullable
+ @Override
+ public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
+ for (EmptyStateProvider provider : mProviders) {
+ EmptyState emptyState = provider.getEmptyState(resolverListAdapter);
+ if (emptyState != null) {
+ return emptyState;
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Describes how the blocked empty state should look like for a profile tab
+ */
+ public interface EmptyState {
+ /**
+ * Title that will be shown on the empty state
+ */
+ @Nullable
+ default String getTitle() { return null; }
+
+ /**
+ * Subtitle that will be shown underneath the title on the empty state
+ */
+ @Nullable
+ default String getSubtitle() { return null; }
+
+ /**
+ * If non-null then a button will be shown and this listener will be called
+ * when the button is clicked
+ */
+ @Nullable
+ default ClickListener getButtonClickListener() { return null; }
+
+ /**
+ * If true then default text ('No apps can perform this action') and style for the empty
+ * state will be applied, title and subtitle will be ignored.
+ */
+ default boolean useDefaultEmptyView() { return false; }
+
+ /**
+ * Returns true if for this empty state we should skip rebuilding of the apps list
+ * for this tab.
+ */
+ default boolean shouldSkipDataRebuild() { return false; }
+
+ /**
+ * Called when empty state is shown, could be used e.g. to track analytics events
+ */
+ default void onEmptyStateShown() {}
+
+ interface ClickListener {
+ void onClick(TabControl currentTab);
+ }
+
+ interface TabControl {
+ void showSpinner();
+ }
+ }
+
+ /**
* Listener for when the user switches on the work profile from the work tab.
*/
interface OnSwitchOnWorkSelectedListener {
@@ -612,14 +571,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
/**
* Describes an injector to be used for cross profile functionality. Overridable for testing.
*/
- @VisibleForTesting
- public interface Injector {
- /**
- * Returns {@code true} if at least one of the provided {@code intents} can be forwarded
- * from {@code sourceUserId} to {@code targetUserId}.
- */
- boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId, int targetUserId);
-
+ public interface QuietModeManager {
/**
* Returns whether the given profile is in quiet mode or not.
*/
@@ -629,5 +581,15 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
* Enables or disables quiet mode for a managed profile.
*/
void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle);
+
+ /**
+ * Should be called when the work profile enabled broadcast received
+ */
+ void markWorkProfileEnabledBroadcastReceived();
+
+ /**
+ * Returns true if enabling of work profile is in progress
+ */
+ boolean isWaitingToEnableWorkProfile();
}
} \ No newline at end of file
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2676396cb997..1fcfe7dd5b6f 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -16,6 +16,14 @@
package com.android.internal.app;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
+import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
+
import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -114,6 +122,9 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
+import com.android.internal.app.NoCrossProfileEmptyStateProvider.DevicePolicyBlockerEmptyState;
import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter;
import com.android.internal.app.ResolverListAdapter.ViewHolder;
import com.android.internal.app.chooser.ChooserTargetInfo;
@@ -830,6 +841,41 @@ public class ChooserActivity extends ResolverActivity implements
return mChooserMultiProfilePagerAdapter;
}
+ @Override
+ protected EmptyStateProvider createBlockerEmptyStateProvider() {
+ final boolean isSendAction = isSendAction(getTargetIntent());
+
+ final EmptyState noWorkToPersonalEmptyState =
+ new DevicePolicyBlockerEmptyState(
+ /* context= */ this,
+ /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
+ /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked,
+ /* devicePolicyStringSubtitleId= */
+ isSendAction ? RESOLVER_CANT_SHARE_WITH_PERSONAL : RESOLVER_CANT_ACCESS_PERSONAL,
+ /* defaultSubtitleResource= */
+ isSendAction ? R.string.resolver_cant_share_with_personal_apps_explanation
+ : R.string.resolver_cant_access_personal_apps_explanation,
+ /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL,
+ /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_CHOOSER);
+
+ final EmptyState noPersonalToWorkEmptyState =
+ new DevicePolicyBlockerEmptyState(
+ /* context= */ this,
+ /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
+ /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked,
+ /* devicePolicyStringSubtitleId= */
+ isSendAction ? RESOLVER_CANT_SHARE_WITH_WORK : RESOLVER_CANT_ACCESS_WORK,
+ /* defaultSubtitleResource= */
+ isSendAction ? R.string.resolver_cant_share_with_work_apps_explanation
+ : R.string.resolver_cant_access_work_apps_explanation,
+ /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK,
+ /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_CHOOSER);
+
+ return new NoCrossProfileEmptyStateProvider(getPersonalProfileUserHandle(),
+ noWorkToPersonalEmptyState, noPersonalToWorkEmptyState,
+ createCrossProfileIntentsChecker(), createMyUserIdProvider());
+ }
+
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForOneProfile(
Intent[] initialIntents,
List<ResolveInfo> rList,
@@ -844,9 +890,10 @@ public class ChooserActivity extends ResolverActivity implements
return new ChooserMultiProfilePagerAdapter(
/* context */ this,
adapter,
- getPersonalProfileUserHandle(),
+ createEmptyStateProvider(/* workProfileUserHandle= */ null),
+ mQuietModeManager,
/* workProfileUserHandle= */ null,
- isSendAction(getTargetIntent()), mMaxTargetsPerRow);
+ mMaxTargetsPerRow);
}
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
@@ -872,10 +919,11 @@ public class ChooserActivity extends ResolverActivity implements
/* context */ this,
personalAdapter,
workAdapter,
+ createEmptyStateProvider(/* workProfileUserHandle= */ getWorkProfileUserHandle()),
+ mQuietModeManager,
selectedProfile,
- getPersonalProfileUserHandle(),
getWorkProfileUserHandle(),
- isSendAction(getTargetIntent()), mMaxTargetsPerRow);
+ mMaxTargetsPerRow);
}
private int findSelectedProfile() {
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index df1130be5ec6..0509b6754218 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -16,17 +16,7 @@
package com.android.internal.app;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
-
import android.annotation.Nullable;
-import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.UserHandle;
import android.view.LayoutInflater;
@@ -47,37 +37,37 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
private static final int SINGLE_CELL_SPAN_SIZE = 1;
private final ChooserProfileDescriptor[] mItems;
- private final boolean mIsSendAction;
private int mBottomOffset;
private int mMaxTargetsPerRow;
ChooserMultiProfilePagerAdapter(Context context,
ChooserActivity.ChooserGridAdapter adapter,
- UserHandle personalProfileUserHandle,
+ EmptyStateProvider emptyStateProvider,
+ QuietModeManager quietModeManager,
UserHandle workProfileUserHandle,
- boolean isSendAction, int maxTargetsPerRow) {
- super(context, /* currentPage */ 0, personalProfileUserHandle, workProfileUserHandle);
+ int maxTargetsPerRow) {
+ super(context, /* currentPage */ 0, emptyStateProvider, quietModeManager,
+ workProfileUserHandle);
mItems = new ChooserProfileDescriptor[] {
createProfileDescriptor(adapter)
};
- mIsSendAction = isSendAction;
mMaxTargetsPerRow = maxTargetsPerRow;
}
ChooserMultiProfilePagerAdapter(Context context,
ChooserActivity.ChooserGridAdapter personalAdapter,
ChooserActivity.ChooserGridAdapter workAdapter,
+ EmptyStateProvider emptyStateProvider,
+ QuietModeManager quietModeManager,
@Profile int defaultProfile,
- UserHandle personalProfileUserHandle,
UserHandle workProfileUserHandle,
- boolean isSendAction, int maxTargetsPerRow) {
- super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
- workProfileUserHandle);
+ int maxTargetsPerRow) {
+ super(context, /* currentPage */ defaultProfile, emptyStateProvider,
+ quietModeManager, workProfileUserHandle);
mItems = new ChooserProfileDescriptor[] {
createProfileDescriptor(personalAdapter),
createProfileDescriptor(workAdapter)
};
- mIsSendAction = isSendAction;
mMaxTargetsPerRow = maxTargetsPerRow;
}
@@ -192,112 +182,6 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
return getListViewForIndex(1 - getCurrentPage());
}
- @Override
- String getMetricsCategory() {
- return ResolverActivity.METRICS_CATEGORY_CHOOSER;
- }
-
- @Override
- protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter,
- View.OnClickListener listener) {
- showEmptyState(activeListAdapter,
- getWorkAppPausedTitle(),
- /* subtitle = */ null,
- listener);
- }
-
- @Override
- protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) {
- if (mIsSendAction) {
- showEmptyState(activeListAdapter,
- getCrossProfileBlockedTitle(),
- getCantShareWithWorkMessage());
- } else {
- showEmptyState(activeListAdapter,
- getCrossProfileBlockedTitle(),
- getCantAccessWorkMessage());
- }
- }
-
- @Override
- protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) {
- if (mIsSendAction) {
- showEmptyState(activeListAdapter,
- getCrossProfileBlockedTitle(),
- getCantShareWithPersonalMessage());
- } else {
- showEmptyState(activeListAdapter,
- getCrossProfileBlockedTitle(),
- getCantAccessPersonalMessage());
- }
- }
-
- @Override
- protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- showEmptyState(listAdapter, getNoPersonalAppsAvailableMessage(), /* subtitle= */ null);
-
- }
-
- @Override
- protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- showEmptyState(listAdapter, getNoWorkAppsAvailableMessage(), /* subtitle = */ null);
- }
-
- private String getWorkAppPausedTitle() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_WORK_PAUSED_TITLE,
- () -> getContext().getString(R.string.resolver_turn_on_work_apps));
- }
-
- private String getCrossProfileBlockedTitle() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
- () -> getContext().getString(R.string.resolver_cross_profile_blocked));
- }
-
- private String getCantShareWithWorkMessage() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_CANT_SHARE_WITH_WORK,
- () -> getContext().getString(
- R.string.resolver_cant_share_with_work_apps_explanation));
- }
-
- private String getCantShareWithPersonalMessage() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_CANT_SHARE_WITH_PERSONAL,
- () -> getContext().getString(
- R.string.resolver_cant_share_with_personal_apps_explanation));
- }
-
- private String getCantAccessWorkMessage() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_CANT_ACCESS_WORK,
- () -> getContext().getString(
- R.string.resolver_cant_access_work_apps_explanation));
- }
-
- private String getCantAccessPersonalMessage() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_CANT_ACCESS_PERSONAL,
- () -> getContext().getString(
- R.string.resolver_cant_access_personal_apps_explanation));
- }
-
- private String getNoWorkAppsAvailableMessage() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_NO_WORK_APPS,
- () -> getContext().getString(
- R.string.resolver_no_work_apps_available));
- }
-
- private String getNoPersonalAppsAvailableMessage() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_NO_PERSONAL_APPS,
- () -> getContext().getString(
- R.string.resolver_no_personal_apps_available));
- }
-
-
void setEmptyStateBottomOffset(int bottomOffset) {
mBottomOffset = bottomOffset;
}
diff --git a/core/java/com/android/internal/app/NoAppsAvailableEmptyStateProvider.java b/core/java/com/android/internal/app/NoAppsAvailableEmptyStateProvider.java
new file mode 100644
index 000000000000..34249f2457c7
--- /dev/null
+++ b/core/java/com/android/internal/app/NoAppsAvailableEmptyStateProvider.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.stats.devicepolicy.nano.DevicePolicyEnums;
+
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
+import com.android.internal.R;
+
+import java.util.List;
+
+/**
+ * Chooser/ResolverActivity empty state provider that returns empty state which is shown when
+ * there are no apps available.
+ */
+public class NoAppsAvailableEmptyStateProvider implements EmptyStateProvider {
+
+ @NonNull
+ private final Context mContext;
+ @Nullable
+ private final UserHandle mWorkProfileUserHandle;
+ @Nullable
+ private final UserHandle mPersonalProfileUserHandle;
+ @NonNull
+ private final String mMetricsCategory;
+ @NonNull
+ private final MyUserIdProvider mMyUserIdProvider;
+
+ public NoAppsAvailableEmptyStateProvider(Context context, UserHandle workProfileUserHandle,
+ UserHandle personalProfileUserHandle, String metricsCategory,
+ MyUserIdProvider myUserIdProvider) {
+ mContext = context;
+ mWorkProfileUserHandle = workProfileUserHandle;
+ mPersonalProfileUserHandle = personalProfileUserHandle;
+ mMetricsCategory = metricsCategory;
+ mMyUserIdProvider = myUserIdProvider;
+ }
+
+ @Nullable
+ @Override
+ @SuppressWarnings("ReferenceEquality")
+ public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
+ UserHandle listUserHandle = resolverListAdapter.getUserHandle();
+
+ if (mWorkProfileUserHandle != null
+ && (mMyUserIdProvider.getMyUserId() == listUserHandle.getIdentifier()
+ || !hasAppsInOtherProfile(resolverListAdapter))) {
+
+ String title;
+ if (listUserHandle == mPersonalProfileUserHandle) {
+ title = mContext.getSystemService(
+ DevicePolicyManager.class).getResources().getString(
+ RESOLVER_NO_PERSONAL_APPS,
+ () -> mContext.getString(R.string.resolver_no_personal_apps_available));
+ } else {
+ title = mContext.getSystemService(
+ DevicePolicyManager.class).getResources().getString(
+ RESOLVER_NO_WORK_APPS,
+ () -> mContext.getString(R.string.resolver_no_work_apps_available));
+ }
+
+ return new NoAppsAvailableEmptyState(
+ title, mMetricsCategory,
+ /* isPersonalProfile= */ listUserHandle == mPersonalProfileUserHandle
+ );
+ } else if (mWorkProfileUserHandle == null) {
+ // Return default empty state without tracking
+ return new DefaultEmptyState();
+ }
+
+ return null;
+ }
+
+ private boolean hasAppsInOtherProfile(ResolverListAdapter adapter) {
+ if (mWorkProfileUserHandle == null) {
+ return false;
+ }
+ List<ResolverActivity.ResolvedComponentInfo> resolversForIntent =
+ adapter.getResolversForUser(UserHandle.of(mMyUserIdProvider.getMyUserId()));
+ for (ResolverActivity.ResolvedComponentInfo info : resolversForIntent) {
+ ResolveInfo resolveInfo = info.getResolveInfoAt(0);
+ if (resolveInfo.targetUserId != UserHandle.USER_CURRENT) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static class DefaultEmptyState implements EmptyState {
+ @Override
+ public boolean useDefaultEmptyView() {
+ return true;
+ }
+ }
+
+ public static class NoAppsAvailableEmptyState implements EmptyState {
+
+ @NonNull
+ private String mTitle;
+
+ @NonNull
+ private String mMetricsCategory;
+
+ private boolean mIsPersonalProfile;
+
+ public NoAppsAvailableEmptyState(String title, String metricsCategory,
+ boolean isPersonalProfile) {
+ mTitle = title;
+ mMetricsCategory = metricsCategory;
+ mIsPersonalProfile = isPersonalProfile;
+ }
+
+ @Nullable
+ @Override
+ public String getTitle() {
+ return mTitle;
+ }
+
+ @Override
+ public void onEmptyStateShown() {
+ DevicePolicyEventLogger.createEvent(
+ DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED)
+ .setStrings(mMetricsCategory)
+ .setBoolean(/*isPersonalProfile*/ mIsPersonalProfile)
+ .write();
+ }
+ }
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/app/NoCrossProfileEmptyStateProvider.java b/core/java/com/android/internal/app/NoCrossProfileEmptyStateProvider.java
new file mode 100644
index 000000000000..2e7d5bf00e27
--- /dev/null
+++ b/core/java/com/android/internal/app/NoCrossProfileEmptyStateProvider.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.UserHandle;
+
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
+
+/**
+ * Empty state provider that does not allow cross profile sharing, it will return a blocker
+ * in case if the profile of the current tab is not the same as the profile of the calling app.
+ */
+public class NoCrossProfileEmptyStateProvider implements EmptyStateProvider {
+
+ private final UserHandle mPersonalProfileUserHandle;
+ private final EmptyState mNoWorkToPersonalEmptyState;
+ private final EmptyState mNoPersonalToWorkEmptyState;
+ private final CrossProfileIntentsChecker mCrossProfileIntentsChecker;
+ private final MyUserIdProvider mUserIdProvider;
+
+ public NoCrossProfileEmptyStateProvider(UserHandle personalUserHandle,
+ EmptyState noWorkToPersonalEmptyState,
+ EmptyState noPersonalToWorkEmptyState,
+ CrossProfileIntentsChecker crossProfileIntentsChecker,
+ MyUserIdProvider myUserIdProvider) {
+ mPersonalProfileUserHandle = personalUserHandle;
+ mNoWorkToPersonalEmptyState = noWorkToPersonalEmptyState;
+ mNoPersonalToWorkEmptyState = noPersonalToWorkEmptyState;
+ mCrossProfileIntentsChecker = crossProfileIntentsChecker;
+ mUserIdProvider = myUserIdProvider;
+ }
+
+ @Nullable
+ @Override
+ public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
+ boolean shouldShowBlocker =
+ mUserIdProvider.getMyUserId() != resolverListAdapter.getUserHandle().getIdentifier()
+ && !mCrossProfileIntentsChecker
+ .hasCrossProfileIntents(resolverListAdapter.getIntents(),
+ mUserIdProvider.getMyUserId(),
+ resolverListAdapter.getUserHandle().getIdentifier());
+
+ if (!shouldShowBlocker) {
+ return null;
+ }
+
+ if (resolverListAdapter.getUserHandle().equals(mPersonalProfileUserHandle)) {
+ return mNoWorkToPersonalEmptyState;
+ } else {
+ return mNoPersonalToWorkEmptyState;
+ }
+ }
+
+
+ /**
+ * Empty state that gets strings from the device policy manager and tracks events into
+ * event logger of the device policy events.
+ */
+ public static class DevicePolicyBlockerEmptyState implements EmptyState {
+
+ @NonNull
+ private final Context mContext;
+ private final String mDevicePolicyStringTitleId;
+ @StringRes
+ private final int mDefaultTitleResource;
+ private final String mDevicePolicyStringSubtitleId;
+ @StringRes
+ private final int mDefaultSubtitleResource;
+ private final int mEventId;
+ @NonNull
+ private final String mEventCategory;
+
+ public DevicePolicyBlockerEmptyState(Context context, String devicePolicyStringTitleId,
+ @StringRes int defaultTitleResource, String devicePolicyStringSubtitleId,
+ @StringRes int defaultSubtitleResource,
+ int devicePolicyEventId, String devicePolicyEventCategory) {
+ mContext = context;
+ mDevicePolicyStringTitleId = devicePolicyStringTitleId;
+ mDefaultTitleResource = defaultTitleResource;
+ mDevicePolicyStringSubtitleId = devicePolicyStringSubtitleId;
+ mDefaultSubtitleResource = defaultSubtitleResource;
+ mEventId = devicePolicyEventId;
+ mEventCategory = devicePolicyEventCategory;
+ }
+
+ @Nullable
+ @Override
+ public String getTitle() {
+ return mContext.getSystemService(DevicePolicyManager.class).getResources().getString(
+ mDevicePolicyStringTitleId,
+ () -> mContext.getString(mDefaultTitleResource));
+ }
+
+ @Nullable
+ @Override
+ public String getSubtitle() {
+ return mContext.getSystemService(DevicePolicyManager.class).getResources().getString(
+ mDevicePolicyStringSubtitleId,
+ () -> mContext.getString(mDefaultSubtitleResource));
+ }
+
+ @Override
+ public void onEmptyStateShown() {
+ DevicePolicyEventLogger.createEvent(mEventId)
+ .setStrings(mEventCategory)
+ .write();
+ }
+
+ @Override
+ public boolean shouldSkipDataRebuild() {
+ return true;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 5dfabcdb2af6..f8b764be582b 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -19,6 +19,9 @@ package com.android.internal.app;
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB;
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY;
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
@@ -26,6 +29,8 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.PermissionChecker.PID_UNKNOWN;
+import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
+import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.annotation.Nullable;
@@ -57,6 +62,7 @@ import android.content.res.TypedArray;
import android.graphics.Insets;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
@@ -93,7 +99,14 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.CompositeEmptyStateProvider;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.OnSwitchOnWorkSelectedListener;
import com.android.internal.app.AbstractMultiProfilePagerAdapter.Profile;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.QuietModeManager;
+import com.android.internal.app.NoCrossProfileEmptyStateProvider.DevicePolicyBlockerEmptyState;
import com.android.internal.app.chooser.ChooserTargetInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.app.chooser.TargetInfo;
@@ -186,6 +199,8 @@ public class ResolverActivity extends Activity implements
@VisibleForTesting
protected AbstractMultiProfilePagerAdapter mMultiProfilePagerAdapter;
+ protected QuietModeManager mQuietModeManager;
+
// Intent extra for connected audio devices
public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
@@ -217,6 +232,9 @@ public class ResolverActivity extends Activity implements
private UserHandle mWorkProfileUserHandle;
+ @Nullable
+ private OnSwitchOnWorkSelectedListener mOnSwitchOnWorkSelectedListener;
+
protected final LatencyTracker mLatencyTracker = getLatencyTracker();
private LatencyTracker getLatencyTracker() {
@@ -375,6 +393,8 @@ public class ResolverActivity extends Activity implements
setTheme(appliedThemeResId());
super.onCreate(savedInstanceState);
+ mQuietModeManager = createQuietModeManager();
+
// Determine whether we should show that intent is forwarded
// from managed profile to owner or other way around.
setProfileSwitchMessage(intent.getContentUserHint());
@@ -475,6 +495,111 @@ public class ResolverActivity extends Activity implements
return resolverMultiProfilePagerAdapter;
}
+ @VisibleForTesting
+ protected MyUserIdProvider createMyUserIdProvider() {
+ return new MyUserIdProvider();
+ }
+
+ @VisibleForTesting
+ protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
+ return new CrossProfileIntentsChecker(getContentResolver());
+ }
+
+ @VisibleForTesting
+ protected QuietModeManager createQuietModeManager() {
+ UserManager userManager = getSystemService(UserManager.class);
+ return new QuietModeManager() {
+
+ private boolean mIsWaitingToEnableWorkProfile = false;
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ return userManager.isQuietModeEnabled(workProfileUserHandle);
+ }
+
+ @Override
+ public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+ });
+ mIsWaitingToEnableWorkProfile = true;
+ }
+
+ @Override
+ public void markWorkProfileEnabledBroadcastReceived() {
+ mIsWaitingToEnableWorkProfile = false;
+ }
+
+ @Override
+ public boolean isWaitingToEnableWorkProfile() {
+ return mIsWaitingToEnableWorkProfile;
+ }
+ };
+ }
+
+ protected EmptyStateProvider createBlockerEmptyStateProvider() {
+ final boolean shouldShowNoCrossProfileIntentsEmptyState = getUser().equals(getIntentUser());
+
+ if (!shouldShowNoCrossProfileIntentsEmptyState) {
+ // Implementation that doesn't show any blockers
+ return new EmptyStateProvider() {};
+ }
+
+ final AbstractMultiProfilePagerAdapter.EmptyState
+ noWorkToPersonalEmptyState =
+ new DevicePolicyBlockerEmptyState(/* context= */ this,
+ /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
+ /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked,
+ /* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_PERSONAL,
+ /* defaultSubtitleResource= */
+ R.string.resolver_cant_access_personal_apps_explanation,
+ /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL,
+ /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_RESOLVER);
+
+ final AbstractMultiProfilePagerAdapter.EmptyState noPersonalToWorkEmptyState =
+ new DevicePolicyBlockerEmptyState(/* context= */ this,
+ /* devicePolicyStringTitleId= */ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
+ /* defaultTitleResource= */ R.string.resolver_cross_profile_blocked,
+ /* devicePolicyStringSubtitleId= */ RESOLVER_CANT_ACCESS_WORK,
+ /* defaultSubtitleResource= */
+ R.string.resolver_cant_access_work_apps_explanation,
+ /* devicePolicyEventId= */ RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK,
+ /* devicePolicyEventCategory= */ ResolverActivity.METRICS_CATEGORY_RESOLVER);
+
+ return new NoCrossProfileEmptyStateProvider(getPersonalProfileUserHandle(),
+ noWorkToPersonalEmptyState, noPersonalToWorkEmptyState,
+ createCrossProfileIntentsChecker(), createMyUserIdProvider());
+ }
+
+ protected EmptyStateProvider createEmptyStateProvider(
+ @Nullable UserHandle workProfileUserHandle) {
+ final EmptyStateProvider blockerEmptyStateProvider = createBlockerEmptyStateProvider();
+
+ final EmptyStateProvider workProfileOffEmptyStateProvider =
+ new WorkProfilePausedEmptyStateProvider(this, workProfileUserHandle,
+ mQuietModeManager,
+ /* onSwitchOnWorkSelectedListener= */
+ () -> { if (mOnSwitchOnWorkSelectedListener != null) {
+ mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
+ }},
+ getMetricsCategory());
+
+ final EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider(
+ this,
+ workProfileUserHandle,
+ getPersonalProfileUserHandle(),
+ getMetricsCategory(),
+ createMyUserIdProvider()
+ );
+
+ // Return composite provider, the order matters (the higher, the more priority)
+ return new CompositeEmptyStateProvider(
+ blockerEmptyStateProvider,
+ workProfileOffEmptyStateProvider,
+ noAppsEmptyStateProvider
+ );
+ }
+
private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile(
Intent[] initialIntents,
List<ResolveInfo> rList, boolean filterLastUsed) {
@@ -485,10 +610,12 @@ public class ResolverActivity extends Activity implements
rList,
filterLastUsed,
/* userHandle */ UserHandle.of(UserHandle.myUserId()));
+ QuietModeManager quietModeManager = createQuietModeManager();
return new ResolverMultiProfilePagerAdapter(
/* context */ this,
adapter,
- getPersonalProfileUserHandle(),
+ createEmptyStateProvider(/* workProfileUserHandle= */ null),
+ quietModeManager,
/* workProfileUserHandle= */ null);
}
@@ -539,14 +666,15 @@ public class ResolverActivity extends Activity implements
(filterLastUsed && UserHandle.myUserId()
== workProfileUserHandle.getIdentifier()),
/* userHandle */ workProfileUserHandle);
+ QuietModeManager quietModeManager = createQuietModeManager();
return new ResolverMultiProfilePagerAdapter(
/* context */ this,
personalAdapter,
workAdapter,
+ createEmptyStateProvider(getWorkProfileUserHandle()),
+ quietModeManager,
selectedProfile,
- getPersonalProfileUserHandle(),
- getWorkProfileUserHandle(),
- /* shouldShowNoCrossProfileIntentsEmptyState= */ getUser().equals(intentUser));
+ getWorkProfileUserHandle());
}
protected int appliedThemeResId() {
@@ -853,9 +981,9 @@ public class ResolverActivity extends Activity implements
}
mRegistered = true;
}
- if (shouldShowTabs() && mMultiProfilePagerAdapter.isWaitingToEnableWorkProfile()) {
- if (mMultiProfilePagerAdapter.isQuietModeEnabled(getWorkProfileUserHandle())) {
- mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
+ if (shouldShowTabs() && mQuietModeManager.isWaitingToEnableWorkProfile()) {
+ if (mQuietModeManager.isQuietModeEnabled(getWorkProfileUserHandle())) {
+ mQuietModeManager.markWorkProfileEnabledBroadcastReceived();
}
}
mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
@@ -1815,13 +1943,12 @@ public class ResolverActivity extends Activity implements
onHorizontalSwipeStateChanged(state);
}
});
- mMultiProfilePagerAdapter.setOnSwitchOnWorkSelectedListener(
- () -> {
- final View workTab = tabHost.getTabWidget().getChildAt(1);
- workTab.setFocusable(true);
- workTab.setFocusableInTouchMode(true);
- workTab.requestFocus();
- });
+ mOnSwitchOnWorkSelectedListener = () -> {
+ final View workTab = tabHost.getTabWidget().getChildAt(1);
+ workTab.setFocusable(true);
+ workTab.setFocusableInTouchMode(true);
+ workTab.requestFocus();
+ };
}
private String getPersonalTabLabel() {
@@ -2082,7 +2209,7 @@ public class ResolverActivity extends Activity implements
public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
if (listAdapter.getUserHandle().equals(getWorkProfileUserHandle())
- && mMultiProfilePagerAdapter.isWaitingToEnableWorkProfile()) {
+ && mQuietModeManager.isWaitingToEnableWorkProfile()) {
// We have just turned on the work profile and entered the pass code to start it,
// now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no
// point in reloading the list now, since the work profile user is still
@@ -2134,7 +2261,7 @@ public class ResolverActivity extends Activity implements
}
mWorkProfileHasBeenEnabled = true;
- mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
+ mQuietModeManager.markWorkProfileEnabledBroadcastReceived();
} else {
// Must be an UNAVAILABLE broadcast, so we watch for the next availability
mWorkProfileHasBeenEnabled = false;
@@ -2150,7 +2277,6 @@ public class ResolverActivity extends Activity implements
};
}
- @VisibleForTesting
public static final class ResolvedComponentInfo {
public final ComponentName name;
private final List<Intent> mIntents = new ArrayList<>();
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 0b33501fd875..9922051c1b0b 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -16,15 +16,7 @@
package com.android.internal.app;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
-
import android.annotation.Nullable;
-import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.UserHandle;
import android.view.LayoutInflater;
@@ -43,34 +35,33 @@ import com.android.internal.widget.PagerAdapter;
public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
private final ResolverProfileDescriptor[] mItems;
- private final boolean mShouldShowNoCrossProfileIntentsEmptyState;
private boolean mUseLayoutWithDefault;
ResolverMultiProfilePagerAdapter(Context context,
ResolverListAdapter adapter,
- UserHandle personalProfileUserHandle,
+ EmptyStateProvider emptyStateProvider,
+ QuietModeManager quietModeManager,
UserHandle workProfileUserHandle) {
- super(context, /* currentPage */ 0, personalProfileUserHandle, workProfileUserHandle);
+ super(context, /* currentPage */ 0, emptyStateProvider, quietModeManager,
+ workProfileUserHandle);
mItems = new ResolverProfileDescriptor[] {
createProfileDescriptor(adapter)
};
- mShouldShowNoCrossProfileIntentsEmptyState = true;
}
ResolverMultiProfilePagerAdapter(Context context,
ResolverListAdapter personalAdapter,
ResolverListAdapter workAdapter,
+ EmptyStateProvider emptyStateProvider,
+ QuietModeManager quietModeManager,
@Profile int defaultProfile,
- UserHandle personalProfileUserHandle,
- UserHandle workProfileUserHandle,
- boolean shouldShowNoCrossProfileIntentsEmptyState) {
- super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
+ UserHandle workProfileUserHandle) {
+ super(context, /* currentPage */ defaultProfile, emptyStateProvider, quietModeManager,
workProfileUserHandle);
mItems = new ResolverProfileDescriptor[] {
createProfileDescriptor(personalAdapter),
createProfileDescriptor(workAdapter)
};
- mShouldShowNoCrossProfileIntentsEmptyState = shouldShowNoCrossProfileIntentsEmptyState;
}
private ResolverProfileDescriptor createProfileDescriptor(
@@ -170,93 +161,6 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
return getListViewForIndex(1 - getCurrentPage());
}
- @Override
- String getMetricsCategory() {
- return ResolverActivity.METRICS_CATEGORY_RESOLVER;
- }
-
- @Override
- boolean allowShowNoCrossProfileIntentsEmptyState() {
- return mShouldShowNoCrossProfileIntentsEmptyState;
- }
-
- @Override
- protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter,
- View.OnClickListener listener) {
- showEmptyState(activeListAdapter,
- getWorkAppPausedTitle(),
- /* subtitle = */ null,
- listener);
- }
-
- @Override
- protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) {
- showEmptyState(activeListAdapter,
- getCrossProfileBlockedTitle(),
- getCantAccessWorkMessage());
- }
-
- @Override
- protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) {
- showEmptyState(activeListAdapter,
- getCrossProfileBlockedTitle(),
- getCantAccessPersonalMessage());
- }
-
- @Override
- protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- showEmptyState(listAdapter,
- getNoPersonalAppsAvailableMessage(),
- /* subtitle = */ null);
- }
-
- @Override
- protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- showEmptyState(listAdapter,
- getNoWorkAppsAvailableMessage(),
- /* subtitle= */ null);
- }
-
- private String getWorkAppPausedTitle() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_WORK_PAUSED_TITLE,
- () -> getContext().getString(R.string.resolver_turn_on_work_apps));
- }
-
- private String getCrossProfileBlockedTitle() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
- () -> getContext().getString(R.string.resolver_cross_profile_blocked));
- }
-
- private String getCantAccessWorkMessage() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_CANT_ACCESS_WORK,
- () -> getContext().getString(
- R.string.resolver_cant_access_work_apps_explanation));
- }
-
- private String getCantAccessPersonalMessage() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_CANT_ACCESS_PERSONAL,
- () -> getContext().getString(
- R.string.resolver_cant_access_personal_apps_explanation));
- }
-
- private String getNoWorkAppsAvailableMessage() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_NO_WORK_APPS,
- () -> getContext().getString(
- R.string.resolver_no_work_apps_available));
- }
-
- private String getNoPersonalAppsAvailableMessage() {
- return getContext().getSystemService(DevicePolicyManager.class).getResources().getString(
- RESOLVER_NO_PERSONAL_APPS,
- () -> getContext().getString(
- R.string.resolver_no_personal_apps_available));
- }
-
void setUseLayoutWithDefault(boolean useLayoutWithDefault) {
mUseLayoutWithDefault = useLayoutWithDefault;
}
diff --git a/core/java/com/android/internal/app/WorkProfilePausedEmptyStateProvider.java b/core/java/com/android/internal/app/WorkProfilePausedEmptyStateProvider.java
new file mode 100644
index 000000000000..6a88557663b1
--- /dev/null
+++ b/core/java/com/android/internal/app/WorkProfilePausedEmptyStateProvider.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.UserHandle;
+import android.stats.devicepolicy.nano.DevicePolicyEnums;
+
+import com.android.internal.R;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.OnSwitchOnWorkSelectedListener;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.QuietModeManager;
+
+/**
+ * Chooser/ResolverActivity empty state provider that returns empty state which is shown when
+ * work profile is paused and we need to show a button to enable it.
+ */
+public class WorkProfilePausedEmptyStateProvider implements EmptyStateProvider {
+
+ private final UserHandle mWorkProfileUserHandle;
+ private final QuietModeManager mQuietModeManager;
+ private final String mMetricsCategory;
+ private final OnSwitchOnWorkSelectedListener mOnSwitchOnWorkSelectedListener;
+ private final Context mContext;
+
+ public WorkProfilePausedEmptyStateProvider(@NonNull Context context,
+ @Nullable UserHandle workProfileUserHandle,
+ @NonNull QuietModeManager quietModeManager,
+ @Nullable OnSwitchOnWorkSelectedListener onSwitchOnWorkSelectedListener,
+ @NonNull String metricsCategory) {
+ mContext = context;
+ mWorkProfileUserHandle = workProfileUserHandle;
+ mQuietModeManager = quietModeManager;
+ mMetricsCategory = metricsCategory;
+ mOnSwitchOnWorkSelectedListener = onSwitchOnWorkSelectedListener;
+ }
+
+ @Nullable
+ @Override
+ public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
+ if (!resolverListAdapter.getUserHandle().equals(mWorkProfileUserHandle)
+ || !mQuietModeManager.isQuietModeEnabled(mWorkProfileUserHandle)
+ || resolverListAdapter.getCount() == 0) {
+ return null;
+ }
+
+ final String title = mContext.getSystemService(DevicePolicyManager.class)
+ .getResources().getString(RESOLVER_WORK_PAUSED_TITLE,
+ () -> mContext.getString(R.string.resolver_turn_on_work_apps));
+
+ return new WorkProfileOffEmptyState(title, (tab) -> {
+ tab.showSpinner();
+ if (mOnSwitchOnWorkSelectedListener != null) {
+ mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
+ }
+ mQuietModeManager.requestQuietModeEnabled(false, mWorkProfileUserHandle);
+ }, mMetricsCategory);
+ }
+
+ public static class WorkProfileOffEmptyState implements EmptyState {
+
+ private final String mTitle;
+ private final ClickListener mOnClick;
+ private final String mMetricsCategory;
+
+ public WorkProfileOffEmptyState(String title, @NonNull ClickListener onClick,
+ @NonNull String metricsCategory) {
+ mTitle = title;
+ mOnClick = onClick;
+ mMetricsCategory = metricsCategory;
+ }
+
+ @Nullable
+ @Override
+ public String getTitle() {
+ return mTitle;
+ }
+
+ @Nullable
+ @Override
+ public ClickListener getButtonClickListener() {
+ return mOnClick;
+ }
+
+ @Override
+ public void onEmptyStateShown() {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
+ .setStrings(mMetricsCategory)
+ .write();
+ }
+ }
+}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 285258a979e4..556636ddd210 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -88,6 +88,7 @@ message SecureSettingsProto {
optional SettingProto odi_captions_volume_ui_enabled = 42 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Setting for accessibility magnification for following typing.
optional SettingProto accessibility_magnification_follow_typing_enabled = 43 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto contrast_level = 44 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index fcbe2b8b62b3..d956e9ac2f9c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -91,7 +91,7 @@
<string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertas"</string>
<string name="notification_channel_call_forward" msgid="8230490317314272406">"Desvío de llamadas"</string>
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"Modo de devolución de llamada de emergencia"</string>
- <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Estado de los datos móviles"</string>
+ <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Estado de datos móviles"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"Mensajes SMS"</string>
<string name="notification_channel_voice_mail" msgid="8457433203106654172">"Mensajes de voz"</string>
<string name="notification_channel_wfc" msgid="9048240466765169038">"Llamada por Wi-Fi"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 9b6818660662..57dcc9062099 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1949,13 +1949,13 @@
<string name="app_streaming_blocked_title_for_settings_dialog" product="tv" msgid="196994247017450357">"A configuración de Android TV non está dispoñible"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="tablet" msgid="8222710146267948647">"A configuración da tableta non está dispoñible"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"A configuración do teléfono non está dispoñible"</string>
- <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Nestes momentos, non podes acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde o dispositivo con Android TV."</string>
+ <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Nestes momentos, non podes acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde o dispositivo Android TV."</string>
<string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Nestes momentos, non podes acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde a tableta."</string>
<string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Nestes momentos, non podes acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde o teléfono."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Esta aplicación solicita seguranza adicional. Proba a facelo desde o dispositivo con Android TV."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Esta aplicación solicita seguranza adicional. Proba a facelo desde o dispositivo Android TV."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Esta aplicación solicita seguranza adicional. Proba a facelo desde a tableta."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Esta aplicación solicita seguranza adicional. Proba a facelo desde o teléfono."</string>
- <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Non se puido acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde o dispositivo con Android TV."</string>
+ <string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Non se puido acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde o dispositivo Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Non se puido acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde a tableta."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Non se puido acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde o teléfono."</string>
<string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Esta aplicación deseñouse para unha versión anterior de Android e quizais non funcione correctamente. Proba a buscar actualizacións ou contacta co programador."</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 251a53a85765..6b80aa3355a4 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -260,7 +260,7 @@
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Stille modus"</string>
<string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"Geluid is UIT"</string>
<string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"Geluid is AAN"</string>
- <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"Vliegtuigmodus"</string>
+ <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"Vliegtuig­modus"</string>
<string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"Vliegtuigmodus is AAN"</string>
<string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"Vliegtuigmodus is UIT"</string>
<string name="global_action_settings" msgid="4671878836947494217">"Instellingen"</string>
@@ -990,7 +990,7 @@
<string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"Gebruikersselectie"</string>
<string name="keyguard_accessibility_status" msgid="6792745049712397237">"Status"</string>
<string name="keyguard_accessibility_camera" msgid="7862557559464986528">"Camera"</string>
- <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Mediabediening"</string>
+ <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Media­bediening"</string>
<string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"Opnieuw indelen van widget gestart."</string>
<string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Opnieuw indelen van widget beëindigd."</string>
<string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Widget <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> verwijderd."</string>
@@ -1482,7 +1482,7 @@
<string name="forward_intent_to_work" msgid="3620262405636021151">"U gebruikt deze app in je werkprofiel"</string>
<string name="input_method_binding_label" msgid="1166731601721983656">"Invoermethode"</string>
<string name="sync_binding_label" msgid="469249309424662147">"Synchroniseren"</string>
- <string name="accessibility_binding_label" msgid="1974602776545801715">"Toegankelijkheid"</string>
+ <string name="accessibility_binding_label" msgid="1974602776545801715">"Toe­gankelijk­heid"</string>
<string name="wallpaper_binding_label" msgid="1197440498000786738">"Achtergrond"</string>
<string name="chooser_wallpaper" msgid="3082405680079923708">"Achtergrond wijzigen"</string>
<string name="notification_listener_binding_label" msgid="2702165274471499713">"Listener voor meldingen"</string>
@@ -1986,7 +1986,7 @@
<string name="app_category_news" msgid="1172762719574964544">"Nieuws en tijdschriften"</string>
<string name="app_category_maps" msgid="6395725487922533156">"Maps en navigatie"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Productiviteit"</string>
- <string name="app_category_accessibility" msgid="6643521607848547683">"Toegankelijkheid"</string>
+ <string name="app_category_accessibility" msgid="6643521607848547683">"Toe­gankelijk­heid"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Apparaatopslag"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB-foutopsporing"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"uur"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 26bfeb4acec2..dfb64e2c0621 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -90,7 +90,7 @@
<string name="notification_channel_network_alert" msgid="4788053066033851841">"ଆଲର୍ଟ"</string>
<string name="notification_channel_call_forward" msgid="8230490317314272406">"କଲ୍‌ ଫରୱାର୍ଡିଂ"</string>
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"ଜରୁରୀକାଳୀନ କଲବ୍ୟାକ୍‍ ମୋଡ୍‍"</string>
- <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"ମୋବାଇଲ୍‍ ଡାଟା ଷ୍ଟାଟସ୍‌"</string>
+ <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"ମୋବାଇଲ ଡାଟା ଷ୍ଟାଟସ"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"SMS ମେସେଜ୍‌"</string>
<string name="notification_channel_voice_mail" msgid="8457433203106654172">"ଭଏସମେଲ୍‍ ମେସେଜ୍‍"</string>
<string name="notification_channel_wfc" msgid="9048240466765169038">"ୱାଇ-ଫାଇ କଲିଙ୍ଗ"</string>
@@ -799,30 +799,30 @@
<item msgid="8150904584178569699">"ୱାର୍କ ଫ୍ୟାକ୍ସ"</item>
<item msgid="4537253139152229577">"ହୋମ ଫାକ୍ସ"</item>
<item msgid="6751245029698664340">"ପେଜର୍"</item>
- <item msgid="1692790665884224905">"ଅନ୍ୟାନ୍ୟ"</item>
+ <item msgid="1692790665884224905">"ଅନ୍ୟ"</item>
<item msgid="6216981255272016212">"କଷ୍ଟମ୍‌"</item>
</string-array>
<string-array name="emailAddressTypes">
<item msgid="7786349763648997741">"ହୋମ"</item>
<item msgid="435564470865989199">"ୱାର୍କ"</item>
- <item msgid="4199433197875490373">"ଅନ୍ୟାନ୍ୟ"</item>
+ <item msgid="4199433197875490373">"ଅନ୍ୟ"</item>
<item msgid="3233938986670468328">"କଷ୍ଟମ୍‌"</item>
</string-array>
<string-array name="postalAddressTypes">
<item msgid="3861463339764243038">"ହୋମ"</item>
<item msgid="5472578890164979109">"ୱାର୍କ"</item>
- <item msgid="5718921296646594739">"ଅନ୍ୟାନ୍ୟ"</item>
+ <item msgid="5718921296646594739">"ଅନ୍ୟ"</item>
<item msgid="5523122236731783179">"କଷ୍ଟମ୍‌"</item>
</string-array>
<string-array name="imAddressTypes">
<item msgid="588088543406993772">"ହୋମ"</item>
<item msgid="5503060422020476757">"ୱାର୍କ"</item>
- <item msgid="2530391194653760297">"ଅନ୍ୟାନ୍ୟ"</item>
+ <item msgid="2530391194653760297">"ଅନ୍ୟ"</item>
<item msgid="7640927178025203330">"କଷ୍ଟମ୍‌"</item>
</string-array>
<string-array name="organizationTypes">
<item msgid="6144047813304847762">"ୱାର୍କ"</item>
- <item msgid="7402720230065674193">"ଅନ୍ୟାନ୍ୟ"</item>
+ <item msgid="7402720230065674193">"ଅନ୍ୟ"</item>
<item msgid="808230403067569648">"କଷ୍ଟମ୍‌"</item>
</string-array>
<string-array name="imProtocols">
@@ -842,7 +842,7 @@
<string name="phoneTypeFaxWork" msgid="6757519896109439123">"ୱାର୍କ ଫାକ୍ସ"</string>
<string name="phoneTypeFaxHome" msgid="6678559953115904345">"ହୋମ ଫାକ୍ସ"</string>
<string name="phoneTypePager" msgid="576402072263522767">"ପେଜର୍"</string>
- <string name="phoneTypeOther" msgid="6918196243648754715">"ଅନ୍ୟାନ୍ୟ"</string>
+ <string name="phoneTypeOther" msgid="6918196243648754715">"ଅନ୍ୟ"</string>
<string name="phoneTypeCallback" msgid="3455781500844157767">"କଲବ୍ୟାକ୍"</string>
<string name="phoneTypeCar" msgid="4604775148963129195">"କାର୍"</string>
<string name="phoneTypeCompanyMain" msgid="4482773154536455441">"କମ୍ପାନୀର ମୁଖ୍ୟ"</string>
@@ -863,16 +863,16 @@
<string name="emailTypeCustom" msgid="1809435350482181786">"କଷ୍ଟମ୍‌"</string>
<string name="emailTypeHome" msgid="1597116303154775999">"ହୋମ"</string>
<string name="emailTypeWork" msgid="2020095414401882111">"ୱାର୍କ"</string>
- <string name="emailTypeOther" msgid="5131130857030897465">"ଅନ୍ୟାନ୍ୟ"</string>
+ <string name="emailTypeOther" msgid="5131130857030897465">"ଅନ୍ୟ"</string>
<string name="emailTypeMobile" msgid="787155077375364230">"ମୋବାଇଲ୍‍"</string>
<string name="postalTypeCustom" msgid="5645590470242939129">"କଷ୍ଟମ୍‌"</string>
<string name="postalTypeHome" msgid="7562272480949727912">"ହୋମ"</string>
<string name="postalTypeWork" msgid="8553425424652012826">"ୱାର୍କ"</string>
- <string name="postalTypeOther" msgid="7094245413678857420">"ଅନ୍ୟାନ୍ୟ"</string>
+ <string name="postalTypeOther" msgid="7094245413678857420">"ଅନ୍ୟ"</string>
<string name="imTypeCustom" msgid="5653384545085765570">"କଷ୍ଟମ୍‌"</string>
<string name="imTypeHome" msgid="6996507981044278216">"ହୋମ"</string>
<string name="imTypeWork" msgid="2099668940169903123">"ୱାର୍କ"</string>
- <string name="imTypeOther" msgid="8068447383276219810">"ଅନ୍ୟାନ୍ୟ"</string>
+ <string name="imTypeOther" msgid="8068447383276219810">"ଅନ୍ୟ"</string>
<string name="imProtocolCustom" msgid="4437878287653764692">"କଷ୍ଟମ୍‌"</string>
<string name="imProtocolAim" msgid="4050198236506604378">"AIM"</string>
<string name="imProtocolMsn" msgid="2257148557766499232">"Windows Live"</string>
@@ -884,7 +884,7 @@
<string name="imProtocolJabber" msgid="7919269388889582015">"Jabber"</string>
<string name="imProtocolNetMeeting" msgid="4985002408136148256">"NetMeeting"</string>
<string name="orgTypeWork" msgid="8684458700669564172">"ୱାର୍କ"</string>
- <string name="orgTypeOther" msgid="5450675258408005553">"ଅନ୍ୟାନ୍ୟ"</string>
+ <string name="orgTypeOther" msgid="5450675258408005553">"ଅନ୍ୟ"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"କଷ୍ଟମ୍‌"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"କଷ୍ଟମ୍‌"</string>
<string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
@@ -904,7 +904,7 @@
<string name="sipAddressTypeCustom" msgid="6283889809842649336">"କଷ୍ଟମ୍‌"</string>
<string name="sipAddressTypeHome" msgid="5918441930656878367">"ହୋମ"</string>
<string name="sipAddressTypeWork" msgid="7873967986701216770">"ୱାର୍କ"</string>
- <string name="sipAddressTypeOther" msgid="6317012577345187275">"ଅନ୍ୟାନ୍ୟ"</string>
+ <string name="sipAddressTypeOther" msgid="6317012577345187275">"ଅନ୍ୟ"</string>
<string name="quick_contacts_not_available" msgid="1262709196045052223">"ଏହି କଣ୍ଟାକ୍ଟ ଦେଖିବାକୁ କୌଣସି ଆପ୍ଲିକେସନ ମିଳିଲା ନାହିଁ।"</string>
<string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"PIN କୋଡ୍‍ ଟାଇପ୍‍ କରନ୍ତୁ"</string>
<string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"PUK ଓ ନୂଆ PIN କୋଡ୍‍ ଟାଇପ୍‍ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index c2fe6ddd103a..318971dced42 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -92,7 +92,7 @@
<string name="notification_channel_network_alert" msgid="4788053066033851841">"Оповещения"</string>
<string name="notification_channel_call_forward" msgid="8230490317314272406">"Переадресация вызовов"</string>
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"Режим экстренных обратных вызовов"</string>
- <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Состояние мобильного Интернета"</string>
+ <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Состояние мобильного интернета"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"SMS"</string>
<string name="notification_channel_voice_mail" msgid="8457433203106654172">"Голосовые сообщения"</string>
<string name="notification_channel_wfc" msgid="9048240466765169038">"Звонки по Wi-Fi"</string>
diff --git a/core/tests/coretests/src/android/util/RotationUtilsTest.java b/core/tests/coretests/src/android/util/RotationUtilsTest.java
index 826eb3070c7e..1b1ee4fb1a1c 100644
--- a/core/tests/coretests/src/android/util/RotationUtilsTest.java
+++ b/core/tests/coretests/src/android/util/RotationUtilsTest.java
@@ -18,6 +18,7 @@ package android.util;
import static android.util.RotationUtils.rotateBounds;
import static android.util.RotationUtils.rotatePoint;
+import static android.util.RotationUtils.rotatePointF;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -25,6 +26,7 @@ import static android.view.Surface.ROTATION_90;
import static org.junit.Assert.assertEquals;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -79,4 +81,26 @@ public class RotationUtilsTest {
rotatePoint(testResult, ROTATION_270, parentW, parentH);
assertEquals(new Point(560, 60), testResult);
}
+
+ @Test
+ public void testRotatePointF() {
+ float parentW = 1000f;
+ float parentH = 600f;
+ PointF testPt = new PointF(60f, 40f);
+
+ PointF testResult = new PointF(testPt);
+ rotatePointF(testResult, ROTATION_90, parentW, parentH);
+ assertEquals(40f, testResult.x, .1f);
+ assertEquals(940f, testResult.y, .1f);
+
+ testResult.set(testPt.x, testPt.y);
+ rotatePointF(testResult, ROTATION_180, parentW, parentH);
+ assertEquals(940f, testResult.x, .1f);
+ assertEquals(560f, testResult.y, .1f);
+
+ testResult.set(testPt.x, testPt.y);
+ rotatePointF(testResult, ROTATION_270, parentW, parentH);
+ assertEquals(560f, testResult.x, .1f);
+ assertEquals(60f, testResult.y, .1f);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
index 875cd0bb4e16..eead4edb6f90 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
@@ -16,9 +16,11 @@
package com.android.internal.app;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
@@ -26,10 +28,12 @@ import android.graphics.Bitmap;
import android.os.UserHandle;
import android.util.Pair;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.QuietModeManager;
import com.android.internal.app.chooser.TargetInfo;
import com.android.internal.logging.MetricsLogger;
-import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -71,7 +75,10 @@ public class ChooserActivityOverrideData {
public boolean isQuietModeEnabled;
public boolean isWorkProfileUserRunning;
public boolean isWorkProfileUserUnlocked;
- public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
+ public Integer myUserId;
+ public QuietModeManager mQuietModeManager;
+ public MyUserIdProvider mMyUserIdProvider;
+ public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
public PackageManager packageManager;
public void reset() {
@@ -95,14 +102,9 @@ public class ChooserActivityOverrideData {
isQuietModeEnabled = false;
isWorkProfileUserRunning = true;
isWorkProfileUserUnlocked = true;
+ myUserId = null;
packageManager = null;
- multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
- @Override
- public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
- int targetUserId) {
- return hasCrossProfileIntents;
- }
-
+ mQuietModeManager = new QuietModeManager() {
@Override
public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
return isQuietModeEnabled;
@@ -113,7 +115,27 @@ public class ChooserActivityOverrideData {
UserHandle workProfileUserHandle) {
isQuietModeEnabled = enabled;
}
+
+ @Override
+ public void markWorkProfileEnabledBroadcastReceived() {
+ }
+
+ @Override
+ public boolean isWaitingToEnableWorkProfile() {
+ return false;
+ }
};
+
+ mMyUserIdProvider = new MyUserIdProvider() {
+ @Override
+ public int getMyUserId() {
+ return myUserId != null ? myUserId : UserHandle.myUserId();
+ }
+ };
+
+ mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
+ when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
+ .thenAnswer(invocation -> hasCrossProfileIntents);
}
private ChooserActivityOverrideData() {}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
new file mode 100644
index 000000000000..c6537c0fbfb3
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.NO_BLOCKER;
+import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_ACCESS_BLOCKER;
+import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_SHARE_BLOCKER;
+import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_ACCESS_BLOCKER;
+import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_SHARE_BLOCKER;
+import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.Tab.PERSONAL;
+import static com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.Tab.WORK;
+import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.companion.DeviceFilter;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.internal.R;
+import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+import com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.Tab;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+@DeviceFilter.MediumType
+@RunWith(Parameterized.class)
+public class ChooserActivityWorkProfileTest {
+
+ private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
+ .getInstrumentation().getTargetContext().getUser();
+ private static final UserHandle WORK_USER_HANDLE = UserHandle.of(10);
+
+ @Rule
+ public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
+ new ActivityTestRule<>(ChooserWrapperActivity.class, false,
+ false);
+ private final TestCase mTestCase;
+
+ public ChooserActivityWorkProfileTest(TestCase testCase) {
+ mTestCase = testCase;
+ }
+
+ @Before
+ public void cleanOverrideData() {
+ sOverrides.reset();
+ }
+
+ @Test
+ public void testBlocker() {
+ setUpPersonalAndWorkComponentInfos();
+ sOverrides.hasCrossProfileIntents = mTestCase.hasCrossProfileIntents();
+ sOverrides.myUserId = mTestCase.getMyUserHandle().getIdentifier();
+
+ launchActivity(mTestCase.getIsSendAction());
+ switchToTab(mTestCase.getTab());
+
+ switch (mTestCase.getExpectedBlocker()) {
+ case NO_BLOCKER:
+ assertNoBlockerDisplayed();
+ break;
+ case PERSONAL_PROFILE_SHARE_BLOCKER:
+ assertCantSharePersonalAppsBlockerDisplayed();
+ break;
+ case WORK_PROFILE_SHARE_BLOCKER:
+ assertCantShareWorkAppsBlockerDisplayed();
+ break;
+ case PERSONAL_PROFILE_ACCESS_BLOCKER:
+ assertCantAccessPersonalAppsBlockerDisplayed();
+ break;
+ case WORK_PROFILE_ACCESS_BLOCKER:
+ assertCantAccessWorkAppsBlockerDisplayed();
+ break;
+ }
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection tests() {
+ return Arrays.asList(
+ new TestCase(
+ /* isSendAction= */ true,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+// TODO(b/256869196) ChooserActivity goes into requestLayout loop
+// new TestCase(
+// /* isSendAction= */ true,
+// /* hasCrossProfileIntents= */ false,
+// /* myUserHandle= */ WORK_USER_HANDLE,
+// /* tab= */ WORK,
+// /* expectedBlocker= */ NO_BLOCKER
+// ),
+ new TestCase(
+ /* isSendAction= */ true,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ true,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ WORK_PROFILE_SHARE_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ true,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+// TODO(b/256869196) ChooserActivity goes into requestLayout loop
+// new TestCase(
+// /* isSendAction= */ true,
+// /* hasCrossProfileIntents= */ false,
+// /* myUserHandle= */ WORK_USER_HANDLE,
+// /* tab= */ PERSONAL,
+// /* expectedBlocker= */ PERSONAL_PROFILE_SHARE_BLOCKER
+// ),
+ new TestCase(
+ /* isSendAction= */ true,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ true,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ false,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ false,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ false,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ false,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ WORK_PROFILE_ACCESS_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ false,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ false,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ PERSONAL_PROFILE_ACCESS_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ false,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* isSendAction= */ false,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ )
+ );
+ }
+
+ private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
+ int numberOfResults, int userId) {
+ List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+ for (int i = 0; i < numberOfResults; i++) {
+ infoList.add(
+ ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId));
+ }
+ return infoList;
+ }
+
+ private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
+ List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+ for (int i = 0; i < numberOfResults; i++) {
+ infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
+ }
+ return infoList;
+ }
+
+ private void setUpPersonalAndWorkComponentInfos() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3,
+ /* userId */ WORK_USER_HANDLE.getIdentifier());
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ }
+
+ private void setupResolverControllers(
+ List<ResolvedComponentInfo> personalResolvedComponentInfos,
+ List<ResolvedComponentInfo> workResolvedComponentInfos) {
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ }
+
+ private void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ private void markWorkProfileUserAvailable() {
+ ChooserWrapperActivity.sOverrides.workProfileUserHandle = WORK_USER_HANDLE;
+ }
+
+ private void assertCantAccessWorkAppsBlockerDisplayed() {
+ onView(withText(R.string.resolver_cross_profile_blocked))
+ .check(matches(isDisplayed()));
+ onView(withText(R.string.resolver_cant_access_work_apps_explanation))
+ .check(matches(isDisplayed()));
+ }
+
+ private void assertCantAccessPersonalAppsBlockerDisplayed() {
+ onView(withText(R.string.resolver_cross_profile_blocked))
+ .check(matches(isDisplayed()));
+ onView(withText(R.string.resolver_cant_access_personal_apps_explanation))
+ .check(matches(isDisplayed()));
+ }
+
+ private void assertCantShareWorkAppsBlockerDisplayed() {
+ onView(withText(R.string.resolver_cross_profile_blocked))
+ .check(matches(isDisplayed()));
+ onView(withText(R.string.resolver_cant_share_with_work_apps_explanation))
+ .check(matches(isDisplayed()));
+ }
+
+ private void assertCantSharePersonalAppsBlockerDisplayed() {
+ onView(withText(R.string.resolver_cross_profile_blocked))
+ .check(matches(isDisplayed()));
+ onView(withText(R.string.resolver_cant_share_with_personal_apps_explanation))
+ .check(matches(isDisplayed()));
+ }
+
+ private void assertNoBlockerDisplayed() {
+ try {
+ onView(withText(R.string.resolver_cross_profile_blocked))
+ .check(matches(not(isDisplayed())));
+ } catch (NoMatchingViewException ignored) {
+ }
+ }
+
+ private void switchToTab(Tab tab) {
+ final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
+ : R.string.resolver_personal_tab;
+
+ onView(withText(stringId)).perform(click());
+ waitForIdle();
+
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ waitForIdle();
+ }
+
+ private Intent createTextIntent(boolean isSendAction) {
+ Intent sendIntent = new Intent();
+ if (isSendAction) {
+ sendIntent.setAction(Intent.ACTION_SEND);
+ }
+ sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
+ sendIntent.setType("text/plain");
+ return sendIntent;
+ }
+
+ private void launchActivity(boolean isSendAction) {
+ Intent sendIntent = createTextIntent(isSendAction);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Test"));
+ waitForIdle();
+ }
+
+ public static class TestCase {
+ private final boolean mIsSendAction;
+ private final boolean mHasCrossProfileIntents;
+ private final UserHandle mMyUserHandle;
+ private final Tab mTab;
+ private final ExpectedBlocker mExpectedBlocker;
+
+ public enum ExpectedBlocker {
+ NO_BLOCKER,
+ PERSONAL_PROFILE_SHARE_BLOCKER,
+ WORK_PROFILE_SHARE_BLOCKER,
+ PERSONAL_PROFILE_ACCESS_BLOCKER,
+ WORK_PROFILE_ACCESS_BLOCKER
+ }
+
+ public enum Tab {
+ WORK,
+ PERSONAL
+ }
+
+ public TestCase(boolean isSendAction, boolean hasCrossProfileIntents,
+ UserHandle myUserHandle, Tab tab, ExpectedBlocker expectedBlocker) {
+ mIsSendAction = isSendAction;
+ mHasCrossProfileIntents = hasCrossProfileIntents;
+ mMyUserHandle = myUserHandle;
+ mTab = tab;
+ mExpectedBlocker = expectedBlocker;
+ }
+
+ public boolean getIsSendAction() {
+ return mIsSendAction;
+ }
+
+ public boolean hasCrossProfileIntents() {
+ return mHasCrossProfileIntents;
+ }
+
+ public UserHandle getMyUserHandle() {
+ return mMyUserHandle;
+ }
+
+ public Tab getTab() {
+ return mTab;
+ }
+
+ public ExpectedBlocker getExpectedBlocker() {
+ return mExpectedBlocker;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder("test");
+
+ if (mTab == WORK) {
+ result.append("WorkTab_");
+ } else {
+ result.append("PersonalTab_");
+ }
+
+ if (mIsSendAction) {
+ result.append("sendAction_");
+ } else {
+ result.append("notSendAction_");
+ }
+
+ if (mHasCrossProfileIntents) {
+ result.append("hasCrossProfileIntents_");
+ } else {
+ result.append("doesNotHaveCrossProfileIntents_");
+ }
+
+ if (mMyUserHandle.equals(PERSONAL_USER_HANDLE)) {
+ result.append("myUserIsPersonal_");
+ } else {
+ result.append("myUserIsWork_");
+ }
+
+ if (mExpectedBlocker == ExpectedBlocker.NO_BLOCKER) {
+ result.append("thenNoBlocker");
+ } else if (mExpectedBlocker == PERSONAL_PROFILE_ACCESS_BLOCKER) {
+ result.append("thenAccessBlockerOnPersonalProfile");
+ } else if (mExpectedBlocker == PERSONAL_PROFILE_SHARE_BLOCKER) {
+ result.append("thenShareBlockerOnPersonalProfile");
+ } else if (mExpectedBlocker == WORK_PROFILE_ACCESS_BLOCKER) {
+ result.append("thenAccessBlockerOnWorkProfile");
+ } else if (mExpectedBlocker == WORK_PROFILE_SHARE_BLOCKER) {
+ result.append("thenShareBlockerOnWorkProfile");
+ }
+
+ return result.toString();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 4c3235ca8ac0..5dc0c8b24218 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -16,6 +16,10 @@
package com.android.internal.app;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
@@ -34,6 +38,8 @@ import android.os.UserHandle;
import android.util.Pair;
import android.util.Size;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.app.chooser.TargetInfo;
@@ -60,15 +66,6 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
}
@Override
- protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
- Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed) {
- AbstractMultiProfilePagerAdapter multiProfilePagerAdapter =
- super.createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
- multiProfilePagerAdapter.setInjector(sOverrides.multiPagerAdapterInjector);
- return multiProfilePagerAdapter;
- }
-
- @Override
public ChooserListAdapter createChooserListAdapter(Context context, List<Intent> payloadIntents,
Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed,
ResolverListController resolverListController) {
@@ -135,6 +132,30 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
}
@Override
+ protected MyUserIdProvider createMyUserIdProvider() {
+ if (sOverrides.mMyUserIdProvider != null) {
+ return sOverrides.mMyUserIdProvider;
+ }
+ return super.createMyUserIdProvider();
+ }
+
+ @Override
+ protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
+ if (sOverrides.mCrossProfileIntentsChecker != null) {
+ return sOverrides.mCrossProfileIntentsChecker;
+ }
+ return super.createCrossProfileIntentsChecker();
+ }
+
+ @Override
+ protected AbstractMultiProfilePagerAdapter.QuietModeManager createQuietModeManager() {
+ if (sOverrides.mQuietModeManager != null) {
+ return sOverrides.mQuietModeManager;
+ }
+ return super.createQuietModeManager();
+ }
+
+ @Override
public void safelyStartActivity(com.android.internal.app.chooser.TargetInfo cti) {
if (sOverrides.onSafelyStartCallback != null &&
sOverrides.onSafelyStartCallback.apply(cti)) {
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java
new file mode 100644
index 000000000000..ce68906a5bff
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.ExpectedBlocker.NO_BLOCKER;
+import static com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_BLOCKER;
+import static com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_BLOCKER;
+import static com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.Tab.PERSONAL;
+import static com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.Tab.WORK;
+import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.annotation.Nullable;
+import android.companion.DeviceFilter;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.internal.R;
+import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+import com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.Tab;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+@DeviceFilter.MediumType
+@RunWith(Parameterized.class)
+public class ResolverActivityWorkProfileTest {
+
+ private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
+ .getInstrumentation().getTargetContext().getUser();
+ private static final UserHandle WORK_USER_HANDLE = UserHandle.of(10);
+
+ @Rule
+ public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
+ new ActivityTestRule<>(ResolverWrapperActivity.class, false,
+ false);
+ private final TestCase mTestCase;
+
+ public ResolverActivityWorkProfileTest(TestCase testCase) {
+ mTestCase = testCase;
+ }
+
+ @Before
+ public void cleanOverrideData() {
+ sOverrides.reset();
+ }
+
+ @Test
+ public void testBlocker() {
+ setUpPersonalAndWorkComponentInfos();
+ sOverrides.hasCrossProfileIntents = mTestCase.hasCrossProfileIntents();
+ sOverrides.myUserId = mTestCase.getMyUserHandle().getIdentifier();
+
+ launchActivity(/* callingUser= */ mTestCase.getExtraCallingUser());
+ switchToTab(mTestCase.getTab());
+
+ switch (mTestCase.getExpectedBlocker()) {
+ case NO_BLOCKER:
+ assertNoBlockerDisplayed();
+ break;
+ case PERSONAL_PROFILE_BLOCKER:
+ assertCantAccessPersonalAppsBlockerDisplayed();
+ break;
+ case WORK_PROFILE_BLOCKER:
+ assertCantAccessWorkAppsBlockerDisplayed();
+ break;
+ }
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection tests() {
+ return Arrays.asList(
+ new TestCase(
+ /* extraCallingUser= */ null,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ null,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ null,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ null,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ WORK_PROFILE_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ null,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ null,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ PERSONAL_PROFILE_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ null,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ null,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+
+ new TestCase(
+ /* extraCallingUser= */ WORK_USER_HANDLE,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ WORK_USER_HANDLE,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ WORK_USER_HANDLE,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ WORK_USER_HANDLE,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ WORK,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ WORK_USER_HANDLE,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ WORK_USER_HANDLE,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ WORK_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ WORK_USER_HANDLE,
+ /* hasCrossProfileIntents= */ true,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ ),
+ new TestCase(
+ /* extraCallingUser= */ WORK_USER_HANDLE,
+ /* hasCrossProfileIntents= */ false,
+ /* myUserHandle= */ PERSONAL_USER_HANDLE,
+ /* tab= */ PERSONAL,
+ /* expectedBlocker= */ NO_BLOCKER
+ )
+ );
+ }
+
+ private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
+ int numberOfResults, int userId) {
+ List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+ for (int i = 0; i < numberOfResults; i++) {
+ infoList.add(
+ ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId));
+ }
+ return infoList;
+ }
+
+ private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
+ List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+ for (int i = 0; i < numberOfResults; i++) {
+ infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
+ }
+ return infoList;
+ }
+
+ private void setUpPersonalAndWorkComponentInfos() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3,
+ /* userId */ WORK_USER_HANDLE.getIdentifier());
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ }
+
+ private void setupResolverControllers(
+ List<ResolvedComponentInfo> personalResolvedComponentInfos,
+ List<ResolvedComponentInfo> workResolvedComponentInfos) {
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ }
+
+ private void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ private void markWorkProfileUserAvailable() {
+ ResolverWrapperActivity.sOverrides.workProfileUserHandle = WORK_USER_HANDLE;
+ }
+
+ private void assertCantAccessWorkAppsBlockerDisplayed() {
+ onView(withText(R.string.resolver_cross_profile_blocked))
+ .check(matches(isDisplayed()));
+ onView(withText(R.string.resolver_cant_access_work_apps_explanation))
+ .check(matches(isDisplayed()));
+ }
+
+ private void assertCantAccessPersonalAppsBlockerDisplayed() {
+ onView(withText(R.string.resolver_cross_profile_blocked))
+ .check(matches(isDisplayed()));
+ onView(withText(R.string.resolver_cant_access_personal_apps_explanation))
+ .check(matches(isDisplayed()));
+ }
+
+ private void assertNoBlockerDisplayed() {
+ try {
+ onView(withText(R.string.resolver_cross_profile_blocked))
+ .check(matches(not(isDisplayed())));
+ } catch (NoMatchingViewException ignored) {
+ }
+ }
+
+ private void switchToTab(Tab tab) {
+ final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
+ : R.string.resolver_personal_tab;
+
+ onView(withText(stringId)).perform(click());
+ waitForIdle();
+
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ waitForIdle();
+ }
+
+ private Intent createSendImageIntent() {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
+ sendIntent.setType("image/jpeg");
+ return sendIntent;
+ }
+
+ private void launchActivity(UserHandle callingUser) {
+ Intent sendIntent = createSendImageIntent();
+ sendIntent.setType("TestType");
+
+ if (callingUser != null) {
+ sendIntent.putExtra(ResolverActivity.EXTRA_CALLING_USER, callingUser);
+ }
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ }
+
+ public static class TestCase {
+ @Nullable
+ private final UserHandle mExtraCallingUser;
+ private final boolean mHasCrossProfileIntents;
+ private final UserHandle mMyUserHandle;
+ private final Tab mTab;
+ private final ExpectedBlocker mExpectedBlocker;
+
+ public enum ExpectedBlocker {
+ NO_BLOCKER,
+ PERSONAL_PROFILE_BLOCKER,
+ WORK_PROFILE_BLOCKER
+ }
+
+ public enum Tab {
+ WORK,
+ PERSONAL
+ }
+
+ public TestCase(@Nullable UserHandle extraCallingUser, boolean hasCrossProfileIntents,
+ UserHandle myUserHandle, Tab tab, ExpectedBlocker expectedBlocker) {
+ mExtraCallingUser = extraCallingUser;
+ mHasCrossProfileIntents = hasCrossProfileIntents;
+ mMyUserHandle = myUserHandle;
+ mTab = tab;
+ mExpectedBlocker = expectedBlocker;
+ }
+
+ @Nullable
+ public UserHandle getExtraCallingUser() {
+ return mExtraCallingUser;
+ }
+
+ public boolean hasCrossProfileIntents() {
+ return mHasCrossProfileIntents;
+ }
+
+ public UserHandle getMyUserHandle() {
+ return mMyUserHandle;
+ }
+
+ public Tab getTab() {
+ return mTab;
+ }
+
+ public ExpectedBlocker getExpectedBlocker() {
+ return mExpectedBlocker;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder("test");
+
+ if (mTab == WORK) {
+ result.append("WorkTab_");
+ } else {
+ result.append("PersonalTab_");
+ }
+
+ if (mExtraCallingUser != null
+ && !mExtraCallingUser.equals(PERSONAL_USER_HANDLE)) {
+ result.append("callingUserIsNonPersonal_");
+ } else {
+ result.append("callingUserIsPersonal_");
+ }
+
+ if (mHasCrossProfileIntents) {
+ result.append("hasCrossProfileIntents_");
+ } else {
+ result.append("doesNotHaveCrossProfileIntents_");
+ }
+
+ if (mMyUserHandle.equals(PERSONAL_USER_HANDLE)) {
+ result.append("myUserIsPersonal_");
+ } else {
+ result.append("myUserIsWork_");
+ }
+
+ if (mExpectedBlocker == ExpectedBlocker.NO_BLOCKER) {
+ result.append("thenNoBlocker");
+ } else if (mExpectedBlocker == ExpectedBlocker.PERSONAL_PROFILE_BLOCKER) {
+ result.append("thenBlockerOnPersonalProfile");
+ } else if (mExpectedBlocker == WORK_PROFILE_BLOCKER) {
+ result.append("thenBlockerOnWorkProfile");
+ }
+
+ return result.toString();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 4cf9c3fe75b9..c778dfeaf376 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -16,6 +16,8 @@
package com.android.internal.app;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -27,6 +29,9 @@ import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.UserHandle;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.QuietModeManager;
import com.android.internal.app.chooser.TargetInfo;
import java.util.List;
@@ -52,12 +57,27 @@ public class ResolverWrapperActivity extends ResolverActivity {
}
@Override
- protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
- Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed) {
- AbstractMultiProfilePagerAdapter multiProfilePagerAdapter =
- super.createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
- multiProfilePagerAdapter.setInjector(sOverrides.multiPagerAdapterInjector);
- return multiProfilePagerAdapter;
+ protected MyUserIdProvider createMyUserIdProvider() {
+ if (sOverrides.mMyUserIdProvider != null) {
+ return sOverrides.mMyUserIdProvider;
+ }
+ return super.createMyUserIdProvider();
+ }
+
+ @Override
+ protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
+ if (sOverrides.mCrossProfileIntentsChecker != null) {
+ return sOverrides.mCrossProfileIntentsChecker;
+ }
+ return super.createCrossProfileIntentsChecker();
+ }
+
+ @Override
+ protected QuietModeManager createQuietModeManager() {
+ if (sOverrides.mQuietModeManager != null) {
+ return sOverrides.mQuietModeManager;
+ }
+ return super.createQuietModeManager();
}
ResolverWrapperAdapter getAdapter() {
@@ -137,9 +157,12 @@ public class ResolverWrapperActivity extends ResolverActivity {
public ResolverListController workResolverListController;
public Boolean isVoiceInteraction;
public UserHandle workProfileUserHandle;
+ public Integer myUserId;
public boolean hasCrossProfileIntents;
public boolean isQuietModeEnabled;
- public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
+ public QuietModeManager mQuietModeManager;
+ public MyUserIdProvider mMyUserIdProvider;
+ public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
public void reset() {
onSafelyStartCallback = null;
@@ -148,15 +171,11 @@ public class ResolverWrapperActivity extends ResolverActivity {
resolverListController = mock(ResolverListController.class);
workResolverListController = mock(ResolverListController.class);
workProfileUserHandle = null;
+ myUserId = null;
hasCrossProfileIntents = true;
isQuietModeEnabled = false;
- multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
- @Override
- public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
- int targetUserId) {
- return hasCrossProfileIntents;
- }
+ mQuietModeManager = new QuietModeManager() {
@Override
public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
return isQuietModeEnabled;
@@ -167,7 +186,27 @@ public class ResolverWrapperActivity extends ResolverActivity {
UserHandle workProfileUserHandle) {
isQuietModeEnabled = enabled;
}
+
+ @Override
+ public void markWorkProfileEnabledBroadcastReceived() {
+ }
+
+ @Override
+ public boolean isWaitingToEnableWorkProfile() {
+ return false;
+ }
};
+
+ mMyUserIdProvider = new MyUserIdProvider() {
+ @Override
+ public int getMyUserId() {
+ return myUserId != null ? myUserId : UserHandle.myUserId();
+ }
+ };
+
+ mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
+ when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
+ .thenAnswer(invocation -> hasCrossProfileIntents);
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index c06548ad81a1..4df2d7d82f6a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -41,7 +41,6 @@ import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAs
import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPAND_FAILED_NO_TF_INFO;
import static androidx.window.extensions.embedding.SplitPresenter.getActivityIntentMinDimensionsPair;
-import static androidx.window.extensions.embedding.SplitPresenter.getNonEmbeddedActivityBounds;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
import android.app.Activity;
@@ -464,7 +463,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// parentInfo#isVisibleRequested is true.
return;
}
- onTaskContainerInfoChanged(taskContainer, parentInfo.getConfiguration());
if (isInPictureInPicture(parentInfo.getConfiguration())) {
// No need to update presentation in PIP until the Task exit PIP.
return;
@@ -614,12 +612,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
- @GuardedBy("mLock")
- private void onTaskContainerInfoChanged(@NonNull TaskContainer taskContainer,
- @NonNull Configuration config) {
- taskContainer.setTaskBounds(config.windowConfiguration.getBounds());
- }
-
/** Returns whether the given {@link TaskContainer} may show in split. */
// Suppress GuardedBy warning because lint asks to mark this method as
// @GuardedBy(mPresenter.mController.mLock), which is mLock itself
@@ -1235,13 +1227,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final TaskContainer taskContainer = mTaskContainers.get(taskId);
final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
pendingAppearedIntent, taskContainer, this);
- if (!taskContainer.isTaskBoundsInitialized()) {
- // Get the initial bounds before the TaskFragment has appeared.
- final Rect taskBounds = getNonEmbeddedActivityBounds(activityInTask);
- if (!taskContainer.setTaskBounds(taskBounds)) {
- Log.w(TAG, "Can't find bounds from activity=" + activityInTask);
- }
- }
return container;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index f494b32cff14..5395fb2ef5ed 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -932,11 +932,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
if (taskContainer != null) {
return taskContainer.getTaskProperties();
}
- // Use a copy of configuration because activity's configuration may be updated later,
- // or we may get unexpected TaskContainer's configuration if Activity's configuration is
- // updated. An example is Activity is going to be in split.
- return new TaskProperties(activity.getDisplayId(),
- new Configuration(activity.getResources().getConfiguration()));
+ return TaskProperties.getTaskPropertiesFromActivity(activity);
}
@NonNull
@@ -950,16 +946,4 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// TODO(b/190433398): Supply correct insets.
return new WindowMetrics(taskBounds, WindowInsets.CONSUMED);
}
-
- /** Obtains the bounds from a non-embedded Activity. */
- @NonNull
- static Rect getNonEmbeddedActivityBounds(@NonNull Activity activity) {
- final WindowConfiguration windowConfiguration =
- activity.getResources().getConfiguration().windowConfiguration;
- if (!activity.isInMultiWindowMode()) {
- // In fullscreen mode the max bounds should correspond to the task bounds.
- return windowConfiguration.getMaxBounds();
- }
- return windowConfiguration.getBounds();
- }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 231da0542e95..dba5a7a1cf3c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -20,14 +20,17 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.inMultiWindowMode;
import android.app.Activity;
+import android.app.ActivityClient;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArraySet;
+import android.util.Log;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentParentInfo;
import android.window.WindowContainerTransaction;
@@ -41,14 +44,11 @@ import java.util.Set;
/** Represents TaskFragments and split pairs below a Task. */
class TaskContainer {
+ private static final String TAG = TaskContainer.class.getSimpleName();
/** The unique task id. */
private final int mTaskId;
- // TODO(b/240219484): consolidate to mConfiguration
- /** Available window bounds of this Task. */
- private final Rect mTaskBounds = new Rect();
-
/** Active TaskFragments in this Task. */
@NonNull
final List<TaskFragmentContainer> mContainers = new ArrayList<>();
@@ -86,10 +86,10 @@ class TaskContainer {
throw new IllegalArgumentException("Invalid Task id");
}
mTaskId = taskId;
- // Make a copy in case the activity's config is updated, and updates the TaskContainer's
- // config unexpectedly.
- mConfiguration = new Configuration(activityInTask.getResources().getConfiguration());
- mDisplayId = activityInTask.getDisplayId();
+ final TaskProperties taskProperties = TaskProperties
+ .getTaskPropertiesFromActivity(activityInTask);
+ mConfiguration = taskProperties.getConfiguration();
+ mDisplayId = taskProperties.getDisplayId();
// Note that it is always called when there's a new Activity is started, which implies
// the host task is visible.
mIsVisible = true;
@@ -108,25 +108,6 @@ class TaskContainer {
}
@NonNull
- Rect getTaskBounds() {
- return mTaskBounds;
- }
-
- /** Returns {@code true} if the bounds is changed. */
- boolean setTaskBounds(@NonNull Rect taskBounds) {
- if (!taskBounds.isEmpty() && !mTaskBounds.equals(taskBounds)) {
- mTaskBounds.set(taskBounds);
- return true;
- }
- return false;
- }
-
- /** Whether the Task bounds has been initialized. */
- boolean isTaskBoundsInitialized() {
- return !mTaskBounds.isEmpty();
- }
-
- @NonNull
Configuration getConfiguration() {
// Make a copy in case the config is updated unexpectedly.
return new Configuration(mConfiguration);
@@ -261,5 +242,45 @@ class TaskContainer {
Configuration getConfiguration() {
return mConfiguration;
}
+
+ /**
+ * Obtains the {@link TaskProperties} for the task that the provided {@link Activity} is
+ * associated with.
+ * <p>
+ * Note that for most case, caller should use
+ * {@link SplitPresenter#getTaskProperties(Activity)} instead. This method is used before
+ * the {@code activity} goes into split.
+ * </p><p>
+ * If the {@link Activity} is in fullscreen, override
+ * {@link WindowConfiguration#getBounds()} with {@link WindowConfiguration#getMaxBounds()}
+ * in case the {@link Activity} is letterboxed. Otherwise, get the Task
+ * {@link Configuration} from the server side or use {@link Activity}'s
+ * {@link Configuration} as a fallback if the Task {@link Configuration} cannot be obtained.
+ */
+ @NonNull
+ static TaskProperties getTaskPropertiesFromActivity(@NonNull Activity activity) {
+ final int displayId = activity.getDisplayId();
+ // Use a copy of configuration because activity's configuration may be updated later,
+ // or we may get unexpected TaskContainer's configuration if Activity's configuration is
+ // updated. An example is Activity is going to be in split.
+ final Configuration activityConfig = new Configuration(
+ activity.getResources().getConfiguration());
+ final WindowConfiguration windowConfiguration = activityConfig.windowConfiguration;
+ final int windowingMode = windowConfiguration.getWindowingMode();
+ if (!inMultiWindowMode(windowingMode)) {
+ // Use the max bounds in fullscreen in case the Activity is letterboxed.
+ windowConfiguration.setBounds(windowConfiguration.getMaxBounds());
+ return new TaskProperties(displayId, activityConfig);
+ }
+ final Configuration taskConfig = ActivityClient.getInstance()
+ .getTaskConfiguration(activity.getActivityToken());
+ if (taskConfig == null) {
+ Log.w(TAG, "Could not obtain task configuration for activity:" + activity);
+ // Still report activity config if task config cannot be obtained from the server
+ // side.
+ return new TaskProperties(displayId, activityConfig);
+ }
+ return new TaskProperties(displayId, taskConfig);
+ }
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
index af5d8c561874..33220c44a3b5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -18,7 +18,9 @@ package androidx.window.extensions.embedding;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import android.graphics.Point;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.RemoteAnimationTarget;
@@ -49,6 +51,16 @@ class TaskFragmentAnimationAdapter {
/** Area in absolute coordinate that the animation surface shouldn't go beyond. */
@NonNull
private final Rect mWholeAnimationBounds = new Rect();
+ /**
+ * Area in absolute coordinate that should represent all the content to show for this window.
+ * This should be the end bounds for opening window, and start bounds for closing window in case
+ * the window is resizing during the open/close transition.
+ */
+ @NonNull
+ private final Rect mContentBounds = new Rect();
+ /** Offset relative to the window parent surface for {@link #mContentBounds}. */
+ @NonNull
+ private final Point mContentRelOffset = new Point();
@NonNull
final Transformation mTransformation = new Transformation();
@@ -78,6 +90,21 @@ class TaskFragmentAnimationAdapter {
mTarget = target;
mLeash = leash;
mWholeAnimationBounds.set(wholeAnimationBounds);
+ if (target.mode == MODE_CLOSING) {
+ // When it is closing, we want to show the content at the start position in case the
+ // window is resizing as well. For example, when the activities is changing from split
+ // to stack, the bottom TaskFragment will be resized to fullscreen when hiding.
+ final Rect startBounds = target.startBounds;
+ final Rect endBounds = target.screenSpaceBounds;
+ mContentBounds.set(startBounds);
+ mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
+ mContentRelOffset.offset(
+ startBounds.left - endBounds.left,
+ startBounds.top - endBounds.top);
+ } else {
+ mContentBounds.set(target.screenSpaceBounds);
+ mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
+ }
}
/**
@@ -108,8 +135,7 @@ class TaskFragmentAnimationAdapter {
/** To be overridden by subclasses to adjust the animation surface change. */
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
// Update the surface position and alpha.
- mTransformation.getMatrix().postTranslate(
- mTarget.localBounds.left, mTarget.localBounds.top);
+ mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
@@ -117,9 +143,8 @@ class TaskFragmentAnimationAdapter {
// positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
final int positionX = Math.round(mMatrix[MTRANS_X]);
final int positionY = Math.round(mMatrix[MTRANS_Y]);
- final Rect cropRect = new Rect(mTarget.screenSpaceBounds);
- final Rect localBounds = mTarget.localBounds;
- cropRect.offset(positionX - localBounds.left, positionY - localBounds.top);
+ final Rect cropRect = new Rect(mContentBounds);
+ cropRect.offset(positionX - mContentRelOffset.x, positionY - mContentRelOffset.y);
// Store the current offset of the surface top left from (0,0) in absolute coordinate.
final int offsetX = cropRect.left;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
index 0e13c59e593c..322f85449450 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -17,6 +17,7 @@
package androidx.window.extensions.embedding;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_CHANGING;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
@@ -254,7 +255,7 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
@NonNull RemoteAnimationTarget[] targets) {
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
for (RemoteAnimationTarget target : targets) {
- if (target.startBounds != null) {
+ if (target.mode == MODE_CHANGING) {
// This is the target with bounds change.
final Animation[] animations =
mAnimationSpec.createChangeBoundsChangeAnimations(target);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
index 13afa4910ae1..1f866c3b99c9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -114,8 +114,8 @@ class TaskFragmentAnimationSpec {
@NonNull
Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) {
final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
- // TODO(b/258126915): we want to keep track of the closing start bounds
- final Rect bounds = target.screenSpaceBounds;
+ // Use startBounds if the window is closing in case it may also resize.
+ final Rect bounds = target.startBounds;
final int endTop;
final int endLeft;
if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 2192b5ca219c..b70b320eee3c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -35,7 +35,6 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.window.WindowContext;
import android.window.WindowProvider;
import androidx.annotation.NonNull;
@@ -310,20 +309,21 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
final int windowingMode;
if (context instanceof Activity) {
- windowingMode = ActivityClient.getInstance().getTaskWindowingMode(
+ final Configuration taskConfig = ActivityClient.getInstance().getTaskConfiguration(
context.getActivityToken());
+ if (taskConfig == null) {
+ // If we cannot determine the task configuration for any reason, it is likely that
+ // we won't be able to determine its position correctly as well. DisplayFeatures'
+ // bounds in this case can't be computed correctly, so we should skip.
+ return false;
+ }
+ windowingMode = taskConfig.windowConfiguration.getWindowingMode();
} else {
// TODO(b/242674941): use task windowing mode for window context that associates with
// activity.
windowingMode = context.getResources().getConfiguration().windowConfiguration
.getWindowingMode();
}
- if (windowingMode == -1) {
- // If we cannot determine the task windowing mode for any reason, it is likely that we
- // won't be able to determine its position correctly as well. DisplayFeatures' bounds
- // in this case can't be computed correctly, so we should skip.
- return false;
- }
// It is recommended not to report any display features in multi-window mode, since it
// won't be possible to synchronize the display feature positions with window movement.
return !WindowConfiguration.inMultiWindowMode(windowingMode);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 87d027899eb4..8c1b87a650e0 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -261,7 +261,7 @@ public class SplitControllerTest {
assertNotNull(tf);
assertNotNull(taskContainer);
- assertEquals(TASK_BOUNDS, taskContainer.getTaskBounds());
+ assertEquals(TASK_BOUNDS, taskContainer.getConfiguration().windowConfiguration.getBounds());
}
@Test
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index af9c6ba5c162..95328ce700e3 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
-import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static org.junit.Assert.assertEquals;
@@ -68,28 +67,6 @@ public class TaskContainerTest {
}
@Test
- public void testIsTaskBoundsInitialized() {
- final TaskContainer taskContainer = createTestTaskContainer();
-
- assertFalse(taskContainer.isTaskBoundsInitialized());
-
- taskContainer.setTaskBounds(TASK_BOUNDS);
-
- assertTrue(taskContainer.isTaskBoundsInitialized());
- }
-
- @Test
- public void testSetTaskBounds() {
- final TaskContainer taskContainer = createTestTaskContainer();
-
- assertFalse(taskContainer.setTaskBounds(new Rect()));
-
- assertTrue(taskContainer.setTaskBounds(TASK_BOUNDS));
-
- assertFalse(taskContainer.setTaskBounds(TASK_BOUNDS));
- }
-
- @Test
public void testGetWindowingModeForSplitTaskFragment() {
final TaskContainer taskContainer = createTestTaskContainer();
final Rect splitBounds = new Rect(0, 0, 500, 1000);
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index dfce50174bda..3d50d2262bb8 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Laat los"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Program sal dalk nie met verdeelde skerm werk nie."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Program steun nie verdeelde skerm nie."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Hierdie app kan net in 1 venster oopgemaak word."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Program sal dalk nie op \'n sekondêre skerm werk nie."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Program steun nie begin op sekondêre skerms nie."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Skermverdeler"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 655e53f64254..a0213f42b125 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"দেখুৱাওক"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"এপ্‌টোৱে বিভাজিত স্ক্ৰীনৰ সৈতে কাম নকৰিব পাৰে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপ্‌টোৱে বিভাজিত স্ক্ৰীন সমৰ্থন নকৰে।"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"এই এপ্‌টো কেৱল ১ খন ৱিণ্ড’ত খুলিব পাৰি।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"গৌণ ডিছপ্লেত এপ্ লঞ্চ কৰিব নোৱাৰি।"</string>
<string name="accessibility_divider" msgid="703810061635792791">"স্প্লিট স্ক্ৰীনৰ বিভাজক"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index d3d20587cb68..f842bfe13efc 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Güvənli məkandan çıxarın"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Tətbiq bölünmüş ekran ilə işləməyə bilər."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Tətbiq ekran bölünməsini dəstəkləmir."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Bu tətbiq yalnız 1 pəncərədə açıla bilər."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Tətbiq ikinci ekranda işləməyə bilər."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Tətbiq ikinci ekranda başlamağı dəstəkləmir."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Bölünmüş ekran ayırıcısı"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 839ecb60e395..540ae7ce6953 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Uklonite iz tajne memorije"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi sa podeljenim ekranom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podeljeni ekran."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ova aplikacija može da se otvori samo u jednom prozoru."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Razdelnik podeljenog ekrana"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index b227cf12a1b0..bea753837b7b 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Паказаць"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Праграма можа не працаваць у рэжыме падзеленага экрана."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Праграма не падтрымлівае функцыю дзялення экрана."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Гэту праграму можна адкрыць толькі ў адным акне."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Праграма можа не працаваць на дадатковых экранах."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Праграма не падтрымлівае запуск на дадатковых экранах."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Раздзяляльнік падзеленага экрана"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 20f40387665e..59915e6b2a6e 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Отмяна на съхраняването"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Приложението може да не работи в режим на разделен екран."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложението не поддържа разделен екран."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Това приложение може да се отвори само в 1 прозорец."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Възможно е приложението да не работи на алтернативни дисплеи."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Приложението не поддържа използването на алтернативни дисплеи."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Разделител в режима за разделен екран"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 4add02eec96d..63c9684070b6 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"আনস্ট্যাস করুন"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"অ্যাপটি স্প্লিট স্ক্রিনে কাজ নাও করতে পারে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"অ্যাপ্লিকেশান বিভক্ত-স্ক্রিন সমর্থন করে না৷"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"এই অ্যাপটি শুধু ১টি উইন্ডোয় খোলা যেতে পারে।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"সেকেন্ডারি ডিসপ্লেতে অ্যাপ লঞ্চ করা যাবে না।"</string>
<string name="accessibility_divider" msgid="703810061635792791">"বিভক্ত-স্ক্রিন বিভাজক"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 1444b89e2504..b725efea6e48 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vađenje iz stasha"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi na podijeljenom ekranu."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava dijeljenje ekrana."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ova aplikacija se može otvoriti samo u 1 prozoru."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Razdjelnik podijeljenog ekrana"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index a0bec428499e..4383916f6597 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Deixa d\'amagar"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"És possible que l\'aplicació no funcioni amb la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'aplicació no admet la pantalla dividida."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aquesta aplicació només pot obrir-se en 1 finestra."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'aplicació no es pot obrir en pantalles secundàries."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalles"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 332e9568b6d8..e5cb26f3e3b4 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušit uložení"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikace v režimu rozdělené obrazovky nemusí fungovat."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikace nepodporuje režim rozdělené obrazovky."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Tuto aplikaci lze otevřít jen na jednom okně."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikace na sekundárním displeji nemusí fungovat."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikace nepodporuje spuštění na sekundárních displejích."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Čára rozdělující obrazovku"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index a7fb175b69f9..46f7c6985ec2 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vis"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Appen fungerer muligvis ikke i opdelt skærm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen understøtter ikke opdelt skærm."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Denne app kan kun åbnes i 1 vindue."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer muligvis ikke på sekundære skærme."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke åbnes på sekundære skærme."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Adskiller til opdelt skærm"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 8d69eb4c9daf..1269d362903d 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Aus Stash entfernen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Die App funktioniert unter Umständen im Modus für geteilten Bildschirm nicht."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Das Teilen des Bildschirms wird in dieser App nicht unterstützt."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Diese App kann nur in einem einzigen Fenster geöffnet werden."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Die App unterstützt den Start auf sekundären Displays nicht."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Bildschirmteiler"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 613e5b075e0f..f8a69ef796b9 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Κατάργηση απόκρυψης"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Η εφαρμογή ενδέχεται να μην λειτουργεί με διαχωρισμό οθόνης."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Αυτή η εφαρμογή μπορεί να ανοιχθεί μόνο σε 1 παράθυρο."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Η εφαρμογή ίσως να μην λειτουργήσει σε δευτερεύουσα οθόνη."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Η εφαρμογή δεν υποστηρίζει την εκκίνηση σε δευτερεύουσες οθόνες."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Διαχωριστικό οθόνης"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 5619617c9f76..8e46c3e0999e 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in one window."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 9f156697f5c7..7cbbf64991cd 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in 1 window."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Split-screen divider"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 5619617c9f76..8e46c3e0999e 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in one window."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 5619617c9f76..8e46c3e0999e 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"This app can only be opened in one window."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 2b474db99f60..b2720be3d8a0 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎Unstash‎‏‎‎‏‎"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎App may not work with split-screen.‎‏‎‎‏‎"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎App does not support split-screen.‎‏‎‎‏‎"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎This app can only be opened in 1 window.‎‏‎‎‏‎"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎App may not work on a secondary display.‎‏‎‎‏‎"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‎App does not support launch on secondary displays.‎‏‎‎‏‎"</string>
<string name="accessibility_divider" msgid="703810061635792791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎Split-screen divider‎‏‎‎‏‎"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 67386c4f31ea..47445a7a0488 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Dejar de almacenar de manera segura"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la app no funcione en el modo de pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La app no es compatible con la función de pantalla dividida."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta app solo puede estar abierta en 1 ventana."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la app no funcione en una pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La app no puede iniciarse en pantallas secundarias."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalla dividida"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index d4a6b6ce7347..6c45231c3261 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"No esconder"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la aplicación no funcione con la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La aplicación no admite la pantalla dividida."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta aplicación solo puede abrirse en una ventana."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La aplicación no se puede abrir en pantallas secundarias."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Dividir la pantalla"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index df84e7c923de..a8dc08cbbd27 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Eemalda hoidlast"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Rakendus ei pruugi poolitatud ekraaniga töötada."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Rakendus ei toeta jagatud ekraani."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Selle rakenduse saab avada ainult ühes aknas."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Rakendus ei toeta teisestel ekraanidel käivitamist."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Ekraanijagaja"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index c062b928c2ec..e7cb5f41a3ac 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"لغو مخفی‌سازی"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن است برنامه با «صفحهٔ دونیمه» کار نکند."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"برنامه از تقسیم صفحه پشتیبانی نمی‌کند."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"این برنامه فقط در ۱ پنجره می‌تواند باز شود."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن است برنامه در نمایشگر ثانویه کار نکند."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"برنامه از راه‌اندازی در نمایشگرهای ثانویه پشتیبانی نمی‌کند."</string>
<string name="accessibility_divider" msgid="703810061635792791">"تقسیم‌کننده صفحه"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 6efb770d2727..86199f3cf092 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poista turvasäilytyksestä"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Sovellus ei ehkä toimi jaetulla näytöllä."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Sovellus ei tue jaetun näytön tilaa."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Tämän sovelluksen voi avata vain yhdessä ikkunassa."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Sovellus ei tue käynnistämistä toissijaisilla näytöillä."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Näytön jakaja"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 0c2e1b1298ca..1f3ac9ecbcca 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Retirer de la réserve"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'application n\'est pas compatible avec l\'écran partagé."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Cette application ne peut être ouverte que dans une fenêtre."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Séparateur d\'écran partagé"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index b7147180126f..f1dbb35dc7a7 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Application incompatible avec l\'écran partagé."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Cette appli ne peut être ouverte que dans 1 fenêtre."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Séparateur d\'écran partagé"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 952d5329bcd0..6e215a1f5b81 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Non esconder"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Pode que a aplicación non funcione coa pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A aplicación non é compatible coa función de pantalla dividida."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta aplicación só se pode abrir en 1 ventá."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A aplicación non se pode iniciar en pantallas secundarias."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalla dividida"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 67488f994468..1446e70ecdfa 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poništite sakrivanje"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće funkcionirati s podijeljenim zaslonom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podijeljeni zaslon."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ova se aplikacija može otvoriti samo u jednom prozoru."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim zaslonima."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Razdjelnik podijeljenog zaslona"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 41d2d3176994..221c329020a0 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Félretevés megszüntetése"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Lehet, hogy az alkalmazás nem működik osztott képernyős nézetben."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Az alkalmazás nem támogatja az osztott képernyős nézetet."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ez az alkalmazás csak egy ablakban nyitható meg."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Az alkalmazást nem lehet másodlagos kijelzőn elindítani."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Elválasztó az osztott nézetben"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 502bfb15c932..7be9941d2a5e 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ցուցադրել"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Հավելվածը չի կարող աշխատել տրոհված էկրանի ռեժիմում։"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Հավելվածը չի աջակցում էկրանի տրոհումը:"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Այս հավելվածը հնարավոր է բացել միայն մեկ պատուհանում։"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Հավելվածը կարող է չաշխատել լրացուցիչ էկրանի վրա"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Հավելվածը չի աջակցում գործարկումը լրացուցիչ էկրանների վրա"</string>
<string name="accessibility_divider" msgid="703810061635792791">"Տրոհված էկրանի բաժանիչ"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 561df7269bcf..4e760ef6450c 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Batalkan stash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikasi mungkin tidak berfungsi dengan layar terpisah."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App tidak mendukung layar terpisah."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aplikasi ini hanya dapat dibuka di 1 jendela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikasi tidak mendukung peluncuran pada layar sekunder."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Pembagi layar terpisah"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 355968bfb6eb..50d4ee7faed6 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Taka úr geymslu"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Hugsanlega virkar forritið ekki með skjáskiptingu."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Forritið styður ekki að skjánum sé skipt."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aðeins er hægt að opna þetta forrit í 1 glugga."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Forrit styður ekki opnun á öðrum skjá."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Skjáskipting"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 1417d0021658..d2595f7682d2 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Annulla accantonamento"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"L\'app potrebbe non funzionare con lo schermo diviso."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'app non supporta la modalità Schermo diviso."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Questa app può essere aperta soltanto in 1 finestra."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"L\'app potrebbe non funzionare su un display secondario."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'app non supporta l\'avvio su display secondari."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Strumento per schermo diviso"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index b3c0b655ccb1..883596ebcd6f 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ביטול ההסתרה הזמנית"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ייתכן שהאפליקציה לא תפעל במסך מפוצל."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"האפליקציה אינה תומכת במסך מפוצל."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ניתן לפתוח את האפליקציה הזו רק בחלון אחד."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"האפליקציה אינה תומכת בהפעלה במסכים משניים."</string>
<string name="accessibility_divider" msgid="703810061635792791">"מחלק מסך מפוצל"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index a5b0f21882f7..6bb22a29e79b 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"表示"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"アプリは分割画面では動作しないことがあります。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"アプリで分割画面がサポートされていません。"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"このアプリはウィンドウが 1 つの場合のみ開くことができます。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"アプリはセカンダリ ディスプレイでは動作しないことがあります。"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"アプリはセカンダリ ディスプレイでの起動に対応していません。"</string>
<string name="accessibility_divider" msgid="703810061635792791">"分割画面の分割線"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index a85efb433360..6cf7d7827ee9 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"გადანახვის გაუქმება"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"აპმა შეიძლება არ იმუშაოს გაყოფილი ეკრანის რეჟიმში."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ამ აპის გახსნა შესაძლებელია მხოლოდ 1 ფანჯარაში."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"აპმა შეიძლება არ იმუშაოს მეორეულ ეკრანზე."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"აპს არ გააჩნია მეორეული ეკრანის მხარდაჭერა."</string>
<string name="accessibility_divider" msgid="703810061635792791">"გაყოფილი ეკრანის რეჟიმის გამყოფი"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 9c0c15cd6665..216619a232fc 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Көрсету"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Қолданба экранды бөлу режимінде жұмыс істемеуі мүмкін."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Қодланба бөлінген экранды қолдамайды."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Бұл қолданбаны тек 1 терезеден ашуға болады."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Қолданба қосымша дисплейде жұмыс істемеуі мүмкін."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Қолданба қосымша дисплейлерде іске қосуды қолдамайды."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Бөлінген экран бөлгіші"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index d56d6a5c5a78..79aca620f348 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ឈប់លាក់ជាបណ្ដោះអាសន្ន"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"កម្មវិធី​អាចនឹងមិន​ដំណើរការ​ជាមួយ​មុខងារបំបែកអេក្រង់​ទេ។"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"កម្មវិធីមិនគាំទ្រអេក្រង់បំបែកជាពីរទេ"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"កម្មវិធីនេះអាចបើកនៅក្នុងវិនដូតែ 1 ប៉ុណ្ណោះ។"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"កម្មវិធីនេះ​ប្រហែល​ជាមិនដំណើរការ​នៅលើ​អេក្រង់បន្ទាប់បន្សំទេ។"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"កម្មវិធី​នេះមិន​អាច​ចាប់ផ្តើម​នៅលើ​អេក្រង់បន្ទាប់បន្សំបានទេ។"</string>
<string name="accessibility_divider" msgid="703810061635792791">"កម្មវិធីចែកអេក្រង់បំបែក"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 1b31a6f883bb..9e9333ea422c 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ಅನ್‌ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ವಿಭಜಿಸಿದ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಆ್ಯಪ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ಈ ಆ್ಯಪ್ ಅನ್ನು 1 ವಿಂಡೋದಲ್ಲಿ ಮಾತ್ರ ತೆರೆಯಬಹುದು."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್‌ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾರಂಭಿಸುವಿಕೆಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
<string name="accessibility_divider" msgid="703810061635792791">"ಸ್ಪ್ಲಿಟ್-ಪರದೆ ಡಿವೈಡರ್"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 563db5b7e7c2..f9b495a38f4f 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"숨기기 취소"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"앱이 분할 화면에서 작동하지 않을 수 있습니다."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"앱이 화면 분할을 지원하지 않습니다."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"이 앱은 창 1개에서만 열 수 있습니다."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"앱이 보조 디스플레이에서 작동하지 않을 수도 있습니다."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"앱이 보조 디스플레이에서의 실행을 지원하지 않습니다."</string>
<string name="accessibility_divider" msgid="703810061635792791">"화면 분할기"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 0497d15671ed..a57f9ae9689f 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Сейфтен чыгаруу"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Колдонмодо экран бөлүнбөшү мүмкүн."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Колдонмодо экран бөлүнбөйт."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Бул колдонмону 1 терезеде гана ачууга болот."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Колдонмо кошумча экранда иштебей коюшу мүмкүн."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Колдонмону кошумча экрандарда иштетүүгө болбойт."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Экранды бөлгүч"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 35aecaaada6c..8e42aa346669 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ເອົາອອກຈາກບ່ອນເກັບສ່ວນຕົວ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ແອັບອາດໃຊ້ບໍ່ໄດ້ກັບການແບ່ງໜ້າຈໍ."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ແອັບບໍ່ຮອງຮັບໜ້າຈໍແບບແຍກກັນ."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ແອັບນີ້ສາມາດເປີດໄດ້ໃນ 1 ໜ້າຈໍເທົ່ານັ້ນ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ແອັບອາດບໍ່ສາມາດໃຊ້ໄດ້ໃນໜ້າຈໍທີສອງ."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ແອັບບໍ່ຮອງຮັບການເປີດໃນໜ້າຈໍທີສອງ."</string>
<string name="accessibility_divider" msgid="703810061635792791">"ຕົວຂັ້ນການແບ່ງໜ້າຈໍ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index f4ed192f2fa8..dc9969095cf5 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Nebeslėpti"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Programa gali neveikti naudojant išskaidyto ekrano režimą."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programoje nepalaikomas skaidytas ekranas."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Šią programą galima atidaryti tik viename lange."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Programa gali neveikti antriniame ekrane."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Programa nepalaiko paleisties antriniuose ekranuose."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Skaidyto ekrano daliklis"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index ffb9ddb9f4b0..bd2eef42690b 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Rādīt"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Iespējams, lietotne nedarbosies ekrāna sadalīšanas režīmā."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Lietotnē netiek atbalstīta ekrāna sadalīšana."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Šo lietotni var atvērt tikai vienā logā."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Lietotnē netiek atbalstīta palaišana sekundārajos displejos."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Ekrāna sadalītājs"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index cf6b9d5bf627..d133654ba777 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Прикажете"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апликацијата може да не работи со поделен екран."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликацијата не поддржува поделен екран."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Апликацијава може да се отвори само во еден прозорец."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликацијата може да не функционира на друг екран."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Апликацијата не поддржува стартување на други екрани."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Разделник на поделен екран"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 54513d1866ee..264f9a0a4db9 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ил гаргах"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апп хуваагдсан дэлгэц дээр ажиллахгүй байж болзошгүй."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Энэ апп нь дэлгэц хуваах тохиргоог дэмждэггүй."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Энэ аппыг зөвхөн 1 цонхонд нээх боломжтой."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апп хоёрдогч дэлгэцэд ажиллахгүй."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Аппыг хоёрдогч дэлгэцэд эхлүүлэх боломжгүй."</string>
<string name="accessibility_divider" msgid="703810061635792791">"\"Дэлгэц хуваах\" хуваагч"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 283348028979..7a475edcd964 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्टॅश करा"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"अ‍ॅप कदाचित स्प्लिट स्क्रीनसह काम करू शकत नाही."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अ‍ॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"हे अ‍ॅप फक्त एका विंडोमध्ये उघडले जाऊ शकते."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"दुसऱ्या डिस्प्लेवर अ‍ॅप कदाचित चालणार नाही."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"दुसऱ्या डिस्प्लेवर अ‍ॅप लाँच होणार नाही."</string>
<string name="accessibility_divider" msgid="703810061635792791">"विभाजित-स्क्रीन विभाजक"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 20f8ff69ebc1..be1dc24ccfcb 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Tunjukkan"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Apl mungkin tidak berfungsi dengan skrin pisah."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Apl tidak menyokong skrin pisah."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Apl ini hanya boleh dibuka dalam 1 tetingkap."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Apl tidak menyokong pelancaran pada paparan kedua."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Pembahagi skrin pisah"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index dd2750b339a2..3e18b4968464 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Avslutt oppbevaring"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Det kan hende at appen ikke fungerer med delt skjerm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen støtter ikke delt skjerm."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Denne appen kan bare åpnes i ett vindu."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke kjøres på sekundære skjermer."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Skilleelement for delt skjerm"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 054f05f4bb8d..b056483e3b2d 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Niet meer verbergen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"De app werkt mogelijk niet met gesplitst scherm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App biedt geen ondersteuning voor gesplitst scherm."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Deze app kan slechts in 1 venster worden geopend."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App werkt mogelijk niet op een secundair scherm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App kan niet op secundaire displays worden gestart."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Scheiding voor gesplitst scherm"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index d0971a35e622..5fd81f44bf0d 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ଦେଖାନ୍ତୁ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ସ୍ପ୍ଲିଟ୍-ସ୍କ୍ରିନରେ ଆପ୍ କାମ କରିନପାରେ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ଆପ୍‍ ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନକୁ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ଏହି ଆପକୁ କେବଳ 1ଟି ୱିଣ୍ଡୋରେ ଖୋଲାଯାଇପାରିବ।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ କାମ ନକରିପାରେ।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ ଲଞ୍ଚ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
<string name="accessibility_divider" msgid="703810061635792791">"ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନ ବିଭାଜକ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 989f8fce4f50..ada79d195df2 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ਅਣਸਟੈਸ਼"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ।"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ਇਹ ਐਪ ਸਿਰਫ਼ 1 ਵਿੰਡੋ ਵਿੱਚ ਖੋਲ੍ਹੀ ਜਾ ਸਕਦੀ ਹੈ।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇ \'ਤੇ ਕੰਮ ਨਾ ਕਰੇ।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇਆਂ \'ਤੇ ਲਾਂਚ ਕਰਨ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
<string name="accessibility_divider" msgid="703810061635792791">"ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਡਿਵਾਈਡਰ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 3458f261dfdd..a97fd5c9ddb0 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zabierz ze schowka"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacja może nie działać przy podzielonym ekranie."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacja nie obsługuje dzielonego ekranu."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ta aplikacja może być otwarta tylko w 1 oknie."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacja może nie działać na dodatkowym ekranie."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacja nie obsługuje uruchamiania na dodatkowych ekranach."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Linia dzielenia ekranu"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index aeb7f9a296b3..e0636d42d980 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Remover do armazenamento"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"A app pode não funcionar com o ecrã dividido."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A app não é compatível com o ecrã dividido."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Esta app só pode ser aberta em 1 janela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"A app pode não funcionar num ecrã secundário."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A app não é compatível com o início em ecrãs secundários."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divisor do ecrã dividido"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 23b209940490..9227216ffc1f 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Anulează stocarea"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Este posibil ca aplicația să nu funcționeze cu ecranul împărțit."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplicația nu acceptă ecranul împărțit."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Aplicația poate fi deschisă într-o singură fereastră."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Este posibil ca aplicația să nu funcționeze pe un ecran secundar."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplicația nu acceptă lansare pe ecrane secundare."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Separator pentru ecranul împărțit"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index c1dfb19162fe..f5fa1a475886 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показать"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"В режиме разделения экрана приложение может работать нестабильно."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложение не поддерживает разделение экрана."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Это приложение можно открыть только в одном окне."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Приложение может не работать на дополнительном экране"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Приложение не поддерживает запуск на дополнительных экранах"</string>
<string name="accessibility_divider" msgid="703810061635792791">"Разделитель экрана"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 5e51003e4429..53e52f28adcf 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"සඟවා තැබීම ඉවත් කරන්න"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"යෙදුම බෙදුම් තිරය සමග ක්‍රියා නොකළ හැකිය"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"යෙදුම බෙදුණු-තිරය සඳහා සහාය නොදක්වයි."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"මෙම යෙදුම විවෘත කළ හැක්කේ 1 කවුළුවක පමණයි."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"යෙදුම ද්විතියික සංදර්ශකයක ක්‍රියා නොකළ හැකිය."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"යෙදුම ද්විතීයික සංදර්ශක මත දියත් කිරීම සඳහා සහාය නොදක්වයි."</string>
<string name="accessibility_divider" msgid="703810061635792791">"බෙදුම්-තිර වෙන්කරණය"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index baf03241dcc6..f004fd472adf 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušiť skrytie"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikácia nemusí fungovať s rozdelenou obrazovkou."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikácia nepodporuje rozdelenú obrazovku."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Táto aplikácia môže byť otvorená iba v jednom okne."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikácia nemusí fungovať na sekundárnej obrazovke."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikácia nepodporuje spúšťanie na sekundárnych obrazovkách."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Rozdeľovač obrazovky"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 2a12a64b6076..c4808059a0c4 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Razkrij"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija morda ne deluje v načinu razdeljenega zaslona."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podpira načina razdeljenega zaslona."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"To aplikacijo je mogoče odpreti samo v enem oknu."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija morda ne bo delovala na sekundarnem zaslonu."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podpira zagona na sekundarnih zaslonih."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Razdelilnik zaslonov"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index eb0199f55eed..b59d4db6413e 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Mos e fshih"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacioni mund të mos funksionojë me ekranin e ndarë."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacioni nuk mbështet ekranin e ndarë."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ky aplikacion mund të hapet vetëm në 1 dritare."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacioni mund të mos funksionojë në një ekran dytësor."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacioni nuk mbështet nisjen në ekrane dytësore."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Ndarësi i ekranit të ndarë"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 6e6019c160fe..78d74d74ac3f 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Уклоните из тајне меморије"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апликација можда неће радити са подељеним екраном."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликација не подржава подељени екран."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ова апликација може да се отвори само у једном прозору."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликација можда неће функционисати на секундарном екрану."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Апликација не подржава покретање на секундарним екранима."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Разделник подељеног екрана"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 419c31d3fb83..cda3040dc056 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Återställ stash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Appen kanske inte fungerar med delad skärm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen har inte stöd för delad skärm."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Denna app kan bara vara öppen i ett fönster."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen kanske inte fungerar på en sekundär skärm."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan inte köras på en sekundär skärm."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Avdelare för delad skärm"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 0c9b96d5e36c..f0c8be5c5957 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్‌స్టాచ్"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"స్క్రీన్ విభజనతో యాప్‌ పని చేయకపోవచ్చు."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"యాప్‌లో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"ఈ యాప్‌ను 1 విండోలో మాత్రమే తెరవవచ్చు."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్‌ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ప్రత్యామ్నాయ డిస్‌ప్లేల్లో ప్రారంభానికి యాప్ మద్దతు లేదు."</string>
<string name="accessibility_divider" msgid="703810061635792791">"విభజన స్క్రీన్ విభాగిని"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 94abe5b97845..2437e0377780 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"เอาออกจากที่เก็บส่วนตัว"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"แอปอาจใช้ไม่ได้กับโหมดแบ่งหน้าจอ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"แอปไม่สนับสนุนการแยกหน้าจอ"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"แอปนี้เปิดได้ใน 1 หน้าต่างเท่านั้น"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"แอปอาจไม่ทำงานในจอแสดงผลรอง"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"แอปไม่รองรับการเรียกใช้ในจอแสดงผลรอง"</string>
<string name="accessibility_divider" msgid="703810061635792791">"เส้นแบ่งหน้าจอ"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 589bedd023c5..86ef75718b77 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"I-unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Posibleng hindi gumana ang app sa split screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Hindi sinusuportahan ng app ang split-screen."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Sa 1 window lang puwedeng buksan ang app na ito."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Maaaring hindi gumana ang app sa pangalawang display."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Hindi sinusuportahan ng app ang paglulunsad sa mga pangalawang display."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Divider ng split-screen"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 6b73f2f7ed7b..c4060cc795ab 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Depolama"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Uygulama bölünmüş ekranda çalışmayabilir."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uygulama bölünmüş ekranı desteklemiyor."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Bu uygulama yalnızca 1 pencerede açılabilir."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uygulama ikincil ekranda çalışmayabilir."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Uygulama ikincil ekranlarda başlatılmayı desteklemiyor."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Bölünmüş ekran ayırıcı"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 50908cc24fbd..ca6a93714cdd 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن ہے کہ ایپ اسپلٹ اسکرین کے ساتھ کام نہ کرے۔"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"یہ ایپ صرف 1 ونڈو میں کھولی جا سکتی ہے۔"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ایپ ثانوی ڈسپلیز پر شروعات کا تعاون نہیں کرتی۔"</string>
<string name="accessibility_divider" msgid="703810061635792791">"سپلٹ اسکرین تقسیم کار"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index e0b802e6b0b9..8f173d5d1e4b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Chiqarish"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Bu ilova ekranni ikkiga ajratish rejimini dastaklamaydi."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Bu ilova ekranni bo‘lish xususiyatini qo‘llab-quvvatlamaydi."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Bu ilovani faqat 1 ta oynada ochish mumkin."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Bu ilova qo‘shimcha ekranda ishlamasligi mumkin."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Bu ilova qo‘shimcha ekranlarda ishga tushmaydi."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Ekranni ikkiga bo‘lish chizig‘i"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 71c3632714f1..1d5b9d63ee5a 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Hiện"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Ứng dụng có thể không hoạt động với tính năng chia đôi màn hình."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Ứng dụng không hỗ trợ chia đôi màn hình."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Ứng dụng này chỉ có thể mở 1 cửa sổ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Ứng dụng có thể không hoạt động trên màn hình phụ."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Ứng dụng không hỗ trợ khởi chạy trên màn hình phụ."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Bộ chia chia đôi màn hình"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 35ff2f22be03..87f2973aa618 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消隐藏"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"应用可能无法在分屏模式下正常运行。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"应用不支持分屏。"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"此应用只能在 1 个窗口中打开。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"应用可能无法在辅显示屏上正常运行。"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"应用不支持在辅显示屏上启动。"</string>
<string name="accessibility_divider" msgid="703810061635792791">"分屏分隔线"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 9c17f64b881f..f9b22d226c4f 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消保護"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"應用程式不支援分割畫面。"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"此應用程式只可在 1 個視窗中開啟"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示屏上運作。"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"應用程式無法在次要顯示屏上啟動。"</string>
<string name="accessibility_divider" msgid="703810061635792791">"分割畫面分隔線"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index c4d9ca467a82..1438e52ccb4a 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消暫時隱藏"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"這個應用程式不支援分割畫面。"</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"這個應用程式只能在 1 個視窗中開啟。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示器上運作。"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"應用程式無法在次要顯示器上啟動。"</string>
<string name="accessibility_divider" msgid="703810061635792791">"分割畫面分隔線"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 4d8f7e33d47e..e9238dc0833a 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -33,8 +33,7 @@
<string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Susa isiteshi"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Izinhlelo zokusebenza kungenzeka zingasebenzi ngesikrini esihlukanisiwe."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uhlelo lokusebenza alusekeli isikrini esihlukanisiwe."</string>
- <!-- no translation found for dock_multi_instances_not_supported_text (5242868470666346929) -->
- <skip />
+ <string name="dock_multi_instances_not_supported_text" msgid="5242868470666346929">"Le-app ingavulwa kuphela ewindini eli-1."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uhlelo lokusebenza kungenzeka lungasebenzi kusibonisi sesibili."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Uhlelo lokusebenza alusekeli ukuqalisa kuzibonisi zesibili."</string>
<string name="accessibility_divider" msgid="703810061635792791">"Isihlukanisi sokuhlukanisa isikrini"</string>
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 bec6844ae863..dd8afff0df2c 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
@@ -475,8 +475,13 @@ public class BubbleController implements ConfigurationChangeListener {
@VisibleForTesting
public void onStatusBarStateChanged(boolean isShade) {
+ boolean didChange = mIsStatusBarShade != isShade;
+ if (DEBUG_BUBBLE_CONTROLLER) {
+ Log.d(TAG, "onStatusBarStateChanged isShade=" + isShade + " didChange=" + didChange);
+ }
mIsStatusBarShade = isShade;
- if (!mIsStatusBarShade) {
+ if (!mIsStatusBarShade && didChange) {
+ // Only collapse stack on change
collapseStack();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 609ee084f23f..2b7600cbfe53 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1956,6 +1956,7 @@ public class BubbleStackView extends FrameLayout
if (wasExpanded) {
stopMonitoringSwipeUpGesture();
animateCollapse();
+ showManageMenu(false);
logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
} else {
animateExpansion();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index e8b0f0265394..214b304df07c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -219,7 +219,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
// Only insets the divider bar with task bar when it's expanded so that the rounded corners
// will be drawn against task bar.
- if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
+ // But there is no need to do it when IME showing because there are no rounded corners at
+ // the bottom. This also avoids the problem of task bar height not changing when IME
+ // floating.
+ if (!insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME)
+ && taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
mTempRect.inset(taskBarInsetsSource.calculateVisibleInsets(mTempRect));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index ae496165028f..45b234a6398a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -122,6 +122,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private int mDensity;
private final boolean mDimNonImeSide;
+ private ValueAnimator mDividerFlingAnimator;
public SplitLayout(String windowName, Context context, Configuration configuration,
SplitLayoutHandler splitLayoutHandler,
@@ -395,6 +396,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mSplitWindowManager.release(t);
mDisplayImeController.removePositionProcessor(mImePositionProcessor);
mImePositionProcessor.reset();
+ if (mDividerFlingAnimator != null) {
+ mDividerFlingAnimator.cancel();
+ }
+ resetDividerPosition();
}
public void release() {
@@ -577,13 +582,18 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
CUJ_SPLIT_SCREEN_RESIZE);
return;
}
- ValueAnimator animator = ValueAnimator
+
+ if (mDividerFlingAnimator != null) {
+ mDividerFlingAnimator.cancel();
+ }
+
+ mDividerFlingAnimator = ValueAnimator
.ofInt(from, to)
.setDuration(duration);
- animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- animator.addUpdateListener(
+ mDividerFlingAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mDividerFlingAnimator.addUpdateListener(
animation -> updateDivideBounds((int) animation.getAnimatedValue()));
- animator.addListener(new AnimatorListenerAdapter() {
+ mDividerFlingAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (flingFinishedCallback != null) {
@@ -591,14 +601,15 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
InteractionJankMonitorUtils.endTracing(
CUJ_SPLIT_SCREEN_RESIZE);
+ mDividerFlingAnimator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
- setDividePosition(to, true /* applyLayoutChange */);
+ mDividerFlingAnimator = null;
}
});
- animator.start();
+ mDividerFlingAnimator.start();
}
/** Switch both surface position with animation. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 90b35a5a55e1..44bcdb2d5de5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -82,7 +82,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
mTasks.put(taskInfo.taskId, state);
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
+ mWindowDecorationViewModel.onTaskOpening(taskInfo, leash, t, t);
t.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 168f6d79a390..60e5ff27cab9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -90,7 +90,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
// This logic relies on 2 assumptions: 1 is that child tasks will be visited before
// parents (due to how z-order works). 2 is that no non-tasks are interleaved
// between tasks (hierarchically).
- taskParents.add(change.getContainer());
+ taskParents.add(change.getParent());
}
if (taskParents.contains(change.getContainer())) {
continue;
@@ -120,7 +120,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- mWindowDecorViewModel.createWindowDecoration(
+ mWindowDecorViewModel.onTaskOpening(
change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@@ -128,31 +128,23 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- mWindowDecorViewModel.setupWindowDecorationForTransition(
- change.getTaskInfo(), startT, finishT);
+ mWindowDecorViewModel.onTaskClosing(change.getTaskInfo(), startT, finishT);
}
private void onChangeTransitionReady(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- mWindowDecorViewModel.setupWindowDecorationForTransition(
- change.getTaskInfo(), startT, finishT);
+ mWindowDecorViewModel.onTaskChanging(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
private void onToFrontTransitionReady(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- boolean exists = mWindowDecorViewModel.setupWindowDecorationForTransition(
- change.getTaskInfo(),
- startT,
- finishT);
- if (!exists) {
- // Window caption does not exist, create it
- mWindowDecorViewModel.createWindowDecoration(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
- }
+ mWindowDecorViewModel.onTaskChanging(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@Override
@@ -188,4 +180,4 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
mWindowDecorViewModel.destroyWindowDecoration(taskInfo.get(i));
}
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 75a4091c7d78..6623f5ca84ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -103,7 +103,7 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
if (mWindowDecorViewModelOptional.isPresent()) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
createdWindowDecor = mWindowDecorViewModelOptional.get()
- .createWindowDecoration(taskInfo, leash, t, t);
+ .onTaskOpening(taskInfo, leash, t, t);
t.apply();
}
if (!createdWindowDecor) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 17d7f5d0d567..5376ae372de2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -97,6 +97,8 @@ public class PipBoundsState {
private int mShelfHeight;
/** Whether the user has resized the PIP manually. */
private boolean mHasUserResizedPip;
+ /** Whether the user has moved the PIP manually. */
+ private boolean mHasUserMovedPip;
/**
* Areas defined by currently visible apps that they prefer to keep clear from overlays such as
* the PiP. Restricted areas may only move the PiP a limited amount from its anchor position.
@@ -279,6 +281,7 @@ public class PipBoundsState {
if (changed) {
clearReentryState();
setHasUserResizedPip(false);
+ setHasUserMovedPip(false);
}
}
@@ -442,6 +445,16 @@ public class PipBoundsState {
mHasUserResizedPip = hasUserResizedPip;
}
+ /** Returns whether the user has moved the PIP. */
+ public boolean hasUserMovedPip() {
+ return mHasUserMovedPip;
+ }
+
+ /** Set whether the user has moved the PIP. */
+ public void setHasUserMovedPip(boolean hasUserMovedPip) {
+ mHasUserMovedPip = hasUserMovedPip;
+ }
+
/**
* Registers a callback when the minimal size of PIP that is set by the app changes.
*/
@@ -577,6 +590,8 @@ public class PipBoundsState {
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
+ pw.println(innerPrefix + "mHasUserMovedPip=" + mHasUserMovedPip);
+ pw.println(innerPrefix + "mHasUserResizedPip=" + mHasUserResizedPip);
if (mPipReentryState == null) {
pw.println(innerPrefix + "mPipReentryState=null");
} else {
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 f170e774739f..8ba2583757cd 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
@@ -63,6 +63,7 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
+import android.view.Choreographer;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -179,8 +180,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// This is necessary in case there was a resize animation ongoing when exit PIP
// started, in which case the first resize will be skipped to let the exit
// operation handle the final resize out of PIP mode. See b/185306679.
- finishResize(tx, destinationBounds, direction, animationType);
- sendOnPipTransitionFinished(direction);
+ finishResizeDelayedIfNeeded(() -> {
+ finishResize(tx, destinationBounds, direction, animationType);
+ sendOnPipTransitionFinished(direction);
+ });
}
}
@@ -196,6 +199,39 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
};
+ /**
+ * Finishes resizing the PiP, delaying the operation if it has to be synced with the PiP menu.
+ *
+ * This is done to avoid a race condition between the last transaction applied in
+ * onPipAnimationUpdate and the finishResize in onPipAnimationEnd. The transaction in
+ * onPipAnimationUpdate is applied directly from WmShell, while onPipAnimationEnd creates a
+ * WindowContainerTransaction in finishResize, which is to be applied by WmCore later. Normally,
+ * the WCT should be the last transaction to finish the animation. However, it may happen that
+ * it gets applied *before* the transaction created by the last onPipAnimationUpdate. This
+ * happens only when the PiP surface transaction has to be synced with the PiP menu due to the
+ * necessity for a delay when syncing the PiP surface animation with the PiP menu surface
+ * animation and redrawing the PiP menu contents. As a result, the PiP surface gets scaled after
+ * the new bounds are applied by WmCore, which makes the PiP surface have unexpected bounds.
+ *
+ * To avoid this, we delay the finishResize operation until
+ * the next frame. This aligns the last onAnimationUpdate transaction with the WCT application.
+ */
+ private void finishResizeDelayedIfNeeded(Runnable finishResizeRunnable) {
+ if (!shouldSyncPipTransactionWithMenu()) {
+ finishResizeRunnable.run();
+ return;
+ }
+
+ // Delay the finishResize to the next frame
+ Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
+ mMainExecutor.execute(finishResizeRunnable);
+ }, null);
+ }
+
+ private boolean shouldSyncPipTransactionWithMenu() {
+ return mPipMenuController.isMenuVisible();
+ }
+
@VisibleForTesting
final PipTransitionController.PipTransitionCallback mPipTransitionCallback =
new PipTransitionController.PipTransitionCallback() {
@@ -221,7 +257,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override
public boolean handlePipTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, Rect destinationBounds) {
- if (mPipMenuController.isMenuVisible()) {
+ if (shouldSyncPipTransactionWithMenu()) {
mPipMenuController.movePipMenu(leash, tx, destinationBounds);
return true;
}
@@ -1223,7 +1259,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.crop(tx, mLeash, toBounds)
.round(tx, mLeash, mPipTransitionState.isInPip());
- if (mPipMenuController.isMenuVisible()) {
+ if (shouldSyncPipTransactionWithMenu()) {
mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
} else {
tx.apply();
@@ -1265,7 +1301,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.scale(tx, mLeash, startBounds, toBounds, degrees)
.round(tx, mLeash, startBounds, toBounds);
- if (mPipMenuController.isMenuVisible()) {
+ if (shouldSyncPipTransactionWithMenu()) {
mPipMenuController.movePipMenu(mLeash, tx, toBounds);
} else {
tx.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
index 84071e08d472..690505e03fce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.pip.phone;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.SystemProperties;
import android.util.ArraySet;
import android.view.Gravity;
@@ -34,6 +35,10 @@ import java.util.Set;
*/
public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithm {
+ private boolean mKeepClearAreaGravityEnabled =
+ SystemProperties.getBoolean(
+ "persist.wm.debug.enable_pip_keep_clear_algorithm_gravity", false);
+
protected int mKeepClearAreasPadding;
public PhonePipKeepClearAlgorithm(Context context) {
@@ -53,31 +58,36 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithm {
Rect startingBounds = pipBoundsState.getBounds().isEmpty()
? pipBoundsAlgorithm.getEntryDestinationBoundsIgnoringKeepClearAreas()
: pipBoundsState.getBounds();
- float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
- int verticalGravity = Gravity.BOTTOM;
- int horizontalGravity;
- if (snapFraction >= 0.5f && snapFraction < 2.5f) {
- horizontalGravity = Gravity.RIGHT;
- } else {
- horizontalGravity = Gravity.LEFT;
- }
- // push the bounds based on the gravity
Rect insets = new Rect();
pipBoundsAlgorithm.getInsetBounds(insets);
if (pipBoundsState.isImeShowing()) {
insets.bottom -= pipBoundsState.getImeHeight();
}
- Rect pushedBounds = new Rect(startingBounds);
- if (verticalGravity == Gravity.BOTTOM) {
- pushedBounds.offsetTo(pushedBounds.left,
- insets.bottom - pushedBounds.height());
- }
- if (horizontalGravity == Gravity.RIGHT) {
- pushedBounds.offsetTo(insets.right - pushedBounds.width(), pushedBounds.top);
- } else {
- pushedBounds.offsetTo(insets.left, pushedBounds.top);
+ Rect pipBounds = new Rect(startingBounds);
+
+ // move PiP towards corner if user hasn't moved it manually or the flag is on
+ if (mKeepClearAreaGravityEnabled
+ || (!pipBoundsState.hasUserMovedPip() && !pipBoundsState.hasUserResizedPip())) {
+ float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
+ int verticalGravity = Gravity.BOTTOM;
+ int horizontalGravity;
+ if (snapFraction >= 0.5f && snapFraction < 2.5f) {
+ horizontalGravity = Gravity.RIGHT;
+ } else {
+ horizontalGravity = Gravity.LEFT;
+ }
+ if (verticalGravity == Gravity.BOTTOM) {
+ pipBounds.offsetTo(pipBounds.left,
+ insets.bottom - pipBounds.height());
+ }
+ if (horizontalGravity == Gravity.RIGHT) {
+ pipBounds.offsetTo(insets.right - pipBounds.width(), pipBounds.top);
+ } else {
+ pipBounds.offsetTo(insets.left, pipBounds.top);
+ }
}
- return findUnoccludedPosition(pushedBounds, pipBoundsState.getRestrictedKeepClearAreas(),
+
+ return findUnoccludedPosition(pipBounds, pipBoundsState.getRestrictedKeepClearAreas(),
pipBoundsState.getUnrestrictedKeepClearAreas(), insets);
}
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 d28a9f3cf8ff..efe938f0a274 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
@@ -612,12 +612,21 @@ public class PipController implements PipTransitionController.PipTransitionCallb
new DisplayInsetsController.OnInsetsChangedListener() {
@Override
public void insetsChanged(InsetsState insetsState) {
+ DisplayLayout pendingLayout =
+ mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId());
+ if (mIsInFixedRotation
+ || pendingLayout.rotation()
+ != mPipBoundsState.getDisplayLayout().rotation()) {
+ // bail out if there is a pending rotation or fixed rotation change
+ return;
+ }
int oldMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
onDisplayChanged(
mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId()),
false /* saveRestoreSnapFraction */);
int newMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
if (!mEnablePipKeepClearAlgorithm) {
+ // offset PiP to adjust for bottom inset change
int pipTop = mPipBoundsState.getBounds().top;
int diff = newMaxMovementBound - oldMaxMovementBound;
if (diff < 0 && pipTop > newMaxMovementBound) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index a9a97beb9180..83bc7c0e6e7d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -875,6 +875,8 @@ public class PipTouchHandler {
}
if (touchState.isDragging()) {
+ mPipBoundsState.setHasUserMovedPip(true);
+
// Move the pinned stack freely
final PointF lastDelta = touchState.getLastTouchDelta();
float lastX = mStartPosition.x + mDelta.x;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index acb71a80ee8a..4cb76230606f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -669,6 +669,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.init();
mSplitLayout.setDivideRatio(splitRatio);
+ // Apply surface bounds before animation start.
+ SurfaceControl.Transaction startT = mTransactionPool.acquire();
+ updateSurfaceBounds(mSplitLayout, startT, false /* applyResizingOffset */);
+ startT.apply();
+ mTransactionPool.release(startT);
+
// Set false to avoid record new bounds with old task still on top;
mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = true;
@@ -742,7 +748,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
setDividerVisibility(true, t);
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
});
setEnterInstanceId(instanceId);
@@ -1035,7 +1040,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mIsDividerRemoteAnimating = false;
mSplitLayout.getInvisibleBounds(mTempRect1);
- if (childrenToTop == null) {
+ if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
mSideStage.removeAllTasks(wct, false /* toTop */);
mMainStage.deactivate(wct, false /* toTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
@@ -1294,13 +1299,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- private void onStageChildTaskEnterPip() {
- // When the exit split-screen is caused by one of the task enters auto pip,
- // we want both tasks to be put to bottom instead of top, otherwise it will end up
- // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
- exitSplitScreen(null, EXIT_REASON_CHILD_TASK_ENTER_PIP);
- }
-
private void updateRecentTasksSplitPair() {
if (!mShouldUpdateRecents) {
return;
@@ -2063,7 +2061,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Update divider state after animation so that it is still around and positioned
// properly for the animation itself.
mSplitLayout.release();
- mSplitLayout.resetDividerPosition();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
}
}
@@ -2340,11 +2337,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@Override
- public void onChildTaskEnterPip() {
- StageCoordinator.this.onStageChildTaskEnterPip();
- }
-
- @Override
public void onRootTaskVanished() {
reset();
StageCoordinator.this.onRootTaskVanished();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index bcf900b99c69..358f712f76b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -18,7 +18,6 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
@@ -74,8 +73,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
- void onChildTaskEnterPip();
-
void onRootTaskVanished();
void onNoLongerSupportMultiWindow();
@@ -257,9 +254,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
// Status is managed/synchronized by the transition lifecycle.
return;
}
- if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
- mCallbacks.onChildTaskEnterPip();
- }
sendStatusChanged();
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 4e1fa290270d..485b400f458d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -77,10 +77,10 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
if (mRemote.asBinder() != null) {
mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
mMainExecutor.execute(() -> {
- if (sct != null) {
- finishTransaction.merge(sct);
- }
finishCallback.onTransitionFinished(wct, null /* wctCB */);
});
}
@@ -90,7 +90,13 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
if (mRemote.asBinder() != null) {
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- mRemote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb);
+ // If the remote is actually in the same process, then make a copy of parameters since
+ // remote impls assume that they have to clean-up native references.
+ final SurfaceControl.Transaction remoteStartT = RemoteTransitionHandler.copyIfLocal(
+ startTransaction, mRemote.getRemoteTransition());
+ final TransitionInfo remoteInfo =
+ remoteStartT == startTransaction ? info : info.localRemoteCopy();
+ mRemote.getRemoteTransition().startAnimation(transition, remoteInfo, remoteStartT, cb);
// assume that remote will apply the start transaction.
startTransaction.clear();
} catch (RemoteException e) {
@@ -124,7 +130,13 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
}
};
try {
- mRemote.getRemoteTransition().mergeAnimation(transition, info, t, mergeTarget, cb);
+ // If the remote is actually in the same process, then make a copy of parameters since
+ // remote impls assume that they have to clean-up native references.
+ final SurfaceControl.Transaction remoteT =
+ RemoteTransitionHandler.copyIfLocal(t, mRemote.getRemoteTransition());
+ final TransitionInfo remoteInfo = remoteT == t ? info : info.localRemoteCopy();
+ mRemote.getRemoteTransition().mergeAnimation(
+ transition, remoteInfo, remoteT, mergeTarget, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error merging remote transition.", e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 9469529de8f1..b4e05848882c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.transition;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
@@ -120,10 +121,10 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
public void onTransitionFinished(WindowContainerTransaction wct,
SurfaceControl.Transaction sct) {
unhandleDeath(remote.asBinder(), finishCallback);
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
mMainExecutor.execute(() -> {
- if (sct != null) {
- finishTransaction.merge(sct);
- }
mRequestedRemotes.remove(transition);
finishCallback.onTransitionFinished(wct, null /* wctCB */);
});
@@ -131,8 +132,14 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
};
Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread());
try {
+ // If the remote is actually in the same process, then make a copy of parameters since
+ // remote impls assume that they have to clean-up native references.
+ final SurfaceControl.Transaction remoteStartT =
+ copyIfLocal(startTransaction, remote.getRemoteTransition());
+ final TransitionInfo remoteInfo =
+ remoteStartT == startTransaction ? info : info.localRemoteCopy();
handleDeath(remote.asBinder(), finishCallback);
- remote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb);
+ remote.getRemoteTransition().startAnimation(transition, remoteInfo, remoteStartT, cb);
// assume that remote will apply the start transaction.
startTransaction.clear();
} catch (RemoteException e) {
@@ -145,6 +152,28 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
return true;
}
+ static SurfaceControl.Transaction copyIfLocal(SurfaceControl.Transaction t,
+ IRemoteTransition remote) {
+ // We care more about parceling than local (though they should be the same); so, use
+ // queryLocalInterface since that's what Binder uses to decide if it needs to parcel.
+ if (remote.asBinder().queryLocalInterface(IRemoteTransition.DESCRIPTOR) == null) {
+ // No local interface, so binder itself will parcel and thus we don't need to.
+ return t;
+ }
+ // Binder won't be parceling; however, the remotes assume they have their own native
+ // objects (and don't know if caller is local or not), so we need to make a COPY here so
+ // that the remote can clean it up without clearing the original transaction.
+ // Since there's no direct `copy` for Transaction, we have to parcel/unparcel instead.
+ final Parcel p = Parcel.obtain();
+ try {
+ t.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ return SurfaceControl.Transaction.CREATOR.createFromParcel(p);
+ } finally {
+ p.recycle();
+ }
+ }
+
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@@ -175,7 +204,11 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
}
};
try {
- remote.mergeAnimation(transition, info, t, mergeTarget, cb);
+ // If the remote is actually in the same process, then make a copy of parameters since
+ // remote impls assume that they have to clean-up native references.
+ final SurfaceControl.Transaction remoteT = copyIfLocal(t, remote);
+ final TransitionInfo remoteInfo = remoteT == t ? info : info.localRemoteCopy();
+ remote.mergeAnimation(transition, remoteInfo, remoteT, mergeTarget, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error attempting to merge remote transition.", e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index b75c55274cff..ab792ee122c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -321,6 +321,7 @@ public class TransitionAnimationHelper {
.setPixelFormat(PixelFormat.RGBA_8888)
.setChildrenOnly(true)
.setAllowProtected(true)
+ .setCaptureSecureLayers(true)
.build();
final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
SurfaceControl.captureLayers(captureArgs);
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 857decf65567..b714d2e5e1e0 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
@@ -500,6 +500,7 @@ public class Transitions implements RemoteCallable<Transitions> {
// Treat this as an abort since we are bypassing any merge logic and effectively
// finishing immediately.
onAbort(transitionToken);
+ releaseSurfaces(info);
return;
}
@@ -604,6 +605,15 @@ public class Transitions implements RemoteCallable<Transitions> {
onFinish(transition, wct, wctCB, false /* abort */);
}
+ /**
+ * Releases an info's animation-surfaces. These don't need to persist and we need to release
+ * them asap so that SF can free memory sooner.
+ */
+ private void releaseSurfaces(@Nullable TransitionInfo info) {
+ if (info == null) return;
+ info.releaseAnimSurfaces();
+ }
+
private void onFinish(IBinder transition,
@Nullable WindowContainerTransaction wct,
@Nullable WindowContainerTransactionCallback wctCB,
@@ -642,6 +652,11 @@ public class Transitions implements RemoteCallable<Transitions> {
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"Transition animation finished (abort=%b), notifying core %s", abort, transition);
+ if (active.mStartT != null) {
+ // Applied by now, so close immediately. Do not set to null yet, though, since nullness
+ // is used later to disambiguate malformed transitions.
+ active.mStartT.close();
+ }
// Merge all relevant transactions together
SurfaceControl.Transaction fullFinish = active.mFinishT;
for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
@@ -661,12 +676,14 @@ public class Transitions implements RemoteCallable<Transitions> {
fullFinish.apply();
}
// Now perform all the finishes.
+ releaseSurfaces(active.mInfo);
mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(transition, wct, wctCB);
while (activeIdx < mActiveTransitions.size()) {
if (!mActiveTransitions.get(activeIdx).mMerged) break;
ActiveTransition merged = mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
+ releaseSurfaces(merged.mInfo);
}
// sift through aborted transitions
while (mActiveTransitions.size() > activeIdx
@@ -679,8 +696,9 @@ public class Transitions implements RemoteCallable<Transitions> {
}
mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
for (int i = 0; i < mObservers.size(); ++i) {
- mObservers.get(i).onTransitionFinished(active.mToken, true);
+ mObservers.get(i).onTransitionFinished(aborted.mToken, true);
}
+ releaseSurfaces(aborted.mInfo);
}
if (mActiveTransitions.size() <= activeIdx) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 8369569b4163..e40db4e4dcf2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -55,6 +55,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.transition.Transitions;
+import java.util.function.Supplier;
+
/**
* View model for the window decoration with a caption and shadows. Works with
* {@link CaptionWindowDecoration}.
@@ -62,6 +64,8 @@ import com.android.wm.shell.transition.Transitions;
public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private static final String TAG = "CaptionViewModel";
+ private final CaptionWindowDecoration.Factory mCaptionWindowDecorFactory;
+ private final Supplier<InputManager> mInputManagerSupplier;
private final ActivityTaskManager mActivityTaskManager;
private final ShellTaskOrganizer mTaskOrganizer;
private final Context mContext;
@@ -77,6 +81,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
+ private EventReceiverFactory mEventReceiverFactory = new EventReceiverFactory();
public CaptionWindowDecorViewModel(
Context context,
@@ -86,6 +91,29 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
DisplayController displayController,
SyncTransactionQueue syncQueue,
DesktopModeController desktopModeController) {
+ this(
+ context,
+ mainHandler,
+ mainChoreographer,
+ taskOrganizer,
+ displayController,
+ syncQueue,
+ desktopModeController,
+ new CaptionWindowDecoration.Factory(),
+ InputManager::getInstance);
+ }
+
+ public CaptionWindowDecorViewModel(
+ Context context,
+ Handler mainHandler,
+ Choreographer mainChoreographer,
+ ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
+ SyncTransactionQueue syncQueue,
+ DesktopModeController desktopModeController,
+ CaptionWindowDecoration.Factory captionWindowDecorFactory,
+ Supplier<InputManager> inputManagerSupplier) {
+
mContext = context;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
@@ -94,7 +122,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
mDisplayController = displayController;
mSyncQueue = syncQueue;
mDesktopModeController = desktopModeController;
- mTransitionDragActive = false;
+
+ mCaptionWindowDecorFactory = captionWindowDecorFactory;
+ mInputManagerSupplier = inputManagerSupplier;
+ }
+
+ void setEventReceiverFactory(EventReceiverFactory eventReceiverFactory) {
+ mEventReceiverFactory = eventReceiverFactory;
}
@Override
@@ -103,42 +137,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
@Override
- public boolean createWindowDecoration(
+ public boolean onTaskOpening(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
if (!shouldShowWindowDecor(taskInfo)) return false;
- CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
- if (oldDecoration != null) {
- // close the old decoration if it exists to avoid two window decorations being added
- oldDecoration.close();
- }
- final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
- mContext,
- mDisplayController,
- mTaskOrganizer,
- taskInfo,
- taskSurface,
- mMainHandler,
- mMainChoreographer,
- mSyncQueue);
- mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
-
- TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration,
- mDragStartListener);
- CaptionTouchEventListener touchEventListener =
- new CaptionTouchEventListener(taskInfo, taskPositioner,
- windowDecoration.getDragDetector());
- windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
- windowDecoration.setDragResizeCallback(taskPositioner);
- setupWindowDecorationForTransition(taskInfo, startT, finishT);
- if (mInputMonitor == null) {
- mInputMonitor = InputManager.getInstance().monitorGestureInput(
- "caption-touch", mContext.getDisplayId());
- mEventReceiver = new EventReceiver(
- mInputMonitor.getInputChannel(), Looper.myLooper());
- }
+ createWindowDecoration(taskInfo, taskSurface, startT, finishT);
return true;
}
@@ -151,25 +156,45 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
@Override
- public boolean setupWindowDecorationForTransition(
+ public void onTaskChanging(
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+
+ if (!shouldShowWindowDecor(taskInfo)) {
+ if (decoration != null) {
+ destroyWindowDecoration(taskInfo);
+ }
+ return;
+ }
+
+ if (decoration == null) {
+ createWindowDecoration(taskInfo, taskSurface, startT, finishT);
+ } else {
+ decoration.relayout(taskInfo, startT, finishT);
+ }
+ }
+
+ @Override
+ public void onTaskClosing(
RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
- if (decoration == null) return false;
+ if (decoration == null) return;
decoration.relayout(taskInfo, startT, finishT);
- return true;
}
@Override
- public boolean destroyWindowDecoration(RunningTaskInfo taskInfo) {
+ public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
final CaptionWindowDecoration decoration =
mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
- if (decoration == null) return false;
+ if (decoration == null) return;
decoration.close();
- return true;
}
private class CaptionTouchEventListener implements
@@ -217,6 +242,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
decoration.setButtonVisibility();
}
}
+
private void injectBackKey() {
sendBackEvent(KeyEvent.ACTION_DOWN);
sendBackEvent(KeyEvent.ACTION_UP);
@@ -266,7 +292,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
*/
private void handleEventForMove(MotionEvent e) {
RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- int windowingMode = mDesktopModeController
+ int windowingMode = mDesktopModeController
.getDisplayAreaWindowingMode(taskInfo.displayId);
if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
return;
@@ -302,7 +328,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
// InputEventReceiver to listen for touch input outside of caption bounds
- private class EventReceiver extends InputEventReceiver {
+ class EventReceiver extends InputEventReceiver {
EventReceiver(InputChannel channel, Looper looper) {
super(channel, looper);
}
@@ -318,8 +344,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
}
+ class EventReceiverFactory {
+ EventReceiver create(InputChannel channel, Looper looper) {
+ return new EventReceiver(channel, looper);
+ }
+ }
+
/**
* Handle MotionEvents relevant to focused task's caption that don't directly touch it
+ *
* @param ev the {@link MotionEvent} received by {@link EventReceiver}
*/
private void handleReceivedMotionEvent(MotionEvent ev) {
@@ -401,7 +434,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
return focusedDecor;
}
-
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopModeStatus.IS_SUPPORTED
@@ -410,7 +442,47 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
.getResources().getConfiguration().smallestScreenWidthDp >= 600;
}
- private class DragStartListenerImpl implements TaskPositioner.DragStartListener{
+ private void createWindowDecoration(
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ if (oldDecoration != null) {
+ // close the old decoration if it exists to avoid two window decorations being added
+ oldDecoration.close();
+ }
+ final CaptionWindowDecoration windowDecoration =
+ mCaptionWindowDecorFactory.create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ taskSurface,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+ mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
+
+ TaskPositioner taskPositioner =
+ new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
+ CaptionTouchEventListener touchEventListener =
+ new CaptionTouchEventListener(
+ taskInfo, taskPositioner, windowDecoration.getDragDetector());
+ windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
+ windowDecoration.setDragResizeCallback(taskPositioner);
+ windowDecoration.relayout(taskInfo, startT, finishT);
+ if (mInputMonitor == null) {
+ InputManager inputManager = mInputManagerSupplier.get();
+ mInputMonitor =
+ inputManager.monitorGestureInput("caption-touch", mContext.getDisplayId());
+ mEventReceiver =
+ mEventReceiverFactory.create(
+ mInputMonitor.getInputChannel(), Looper.myLooper());
+ }
+ }
+
+ private class DragStartListenerImpl implements TaskPositioner.DragStartListener {
@Override
public void onDragStart(int taskId) {
mWindowDecorByTaskId.get(taskId).closeHandleMenu();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 59576cd3ec15..037ca2031254 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -42,7 +42,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
- * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close button.
+ * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close
+ * button.
*
* The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
*/
@@ -181,12 +182,12 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
if (oldDecorationSurface != mDecorationContainerSurface || mDragResizeListener == null) {
closeDragResizeListener();
mDragResizeListener = new DragResizeInputListener(
- mContext,
- mHandler,
- mChoreographer,
- mDisplay.getDisplayId(),
- mDecorationContainerSurface,
- mDragResizeCallback);
+ mContext,
+ mHandler,
+ mChoreographer,
+ mDisplay.getDisplayId(),
+ mDecorationContainerSurface,
+ mDragResizeCallback);
}
int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext()).getScaledTouchSlop();
@@ -242,7 +243,6 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Sets the visibility of buttons and color of caption based on desktop mode status
- *
*/
void setButtonVisibility() {
mDesktopActive = DesktopModeStatus.isActive(mContext);
@@ -313,6 +313,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Close an open handle menu if input is outside of menu coordinates
+ *
* @param ev the tapped point to compare against
*/
void closeHandleMenuIfNeeded(MotionEvent ev) {
@@ -329,6 +330,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Offset the coordinates of a {@link MotionEvent} to be in the same coordinate space as caption
+ *
* @param ev the {@link MotionEvent} to offset
* @return the point of the input in local space
*/
@@ -343,7 +345,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Determine if a passed MotionEvent is in a view in caption
- * @param ev the {@link MotionEvent} to check
+ *
+ * @param ev the {@link MotionEvent} to check
* @param layoutId the id of the view
* @return {@code true} if event is inside the specified view, {@code false} if not
*/
@@ -363,6 +366,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
* Check a passed MotionEvent if a click has occurred on any button on this caption
* Note this should only be called when a regular onClick is not possible
* (i.e. the button was clicked through status bar layer)
+ *
* @param ev the MotionEvent to compare
*/
void checkClickEvent(MotionEvent ev) {
@@ -399,4 +403,27 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
closeHandleMenu();
super.close();
}
+
+ static class Factory {
+
+ CaptionWindowDecoration create(
+ Context context,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ Handler handler,
+ Choreographer choreographer,
+ SyncTransactionQueue syncQueue) {
+ return new CaptionWindowDecoration(
+ context,
+ displayController,
+ taskOrganizer,
+ taskInfo,
+ taskSurface,
+ handler,
+ choreographer,
+ syncQueue);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 2ce4d04377a1..907977c661f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -28,7 +28,6 @@ import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
* servers.
*/
public interface WindowDecorViewModel {
-
/**
* Sets the transition starter that starts freeform task transitions.
*
@@ -37,16 +36,16 @@ public interface WindowDecorViewModel {
void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter);
/**
- * Creates a window decoration for the given task.
- * Can be {@code null} for Fullscreen tasks but not Freeform ones.
+ * Creates a window decoration for the given task. Can be {@code null} for Fullscreen tasks but
+ * not Freeform ones.
*
- * @param taskInfo the initial task info of the task
+ * @param taskInfo the initial task info of the task
* @param taskSurface the surface of the task
- * @param startT the start transaction to be applied before the transition
- * @param finishT the finish transaction to restore states after the transition
+ * @param startT the start transaction to be applied before the transition
+ * @param finishT the finish transaction to restore states after the transition
* @return {@code true} if window decoration was created, {@code false} otherwise
*/
- boolean createWindowDecoration(
+ boolean onTaskOpening(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
@@ -54,7 +53,7 @@ public interface WindowDecorViewModel {
/**
* Notifies a task info update on the given task, with the window decoration created previously
- * for this task by {@link #createWindowDecoration}.
+ * for this task by {@link #onTaskOpening}.
*
* @param taskInfo the new task info of the task
*/
@@ -62,13 +61,29 @@ public interface WindowDecorViewModel {
/**
* Notifies a transition is about to start about the given task to give the window decoration a
- * chance to prepare for this transition.
+ * chance to prepare for this transition. Unlike {@link #onTaskInfoChanged}, this method creates
+ * a window decoration if one does not exist but is required.
+ *
+ * @param taskInfo the initial task info of the task
+ * @param taskSurface the surface of the task
+ * @param startT the start transaction to be applied before the transition
+ * @param finishT the finish transaction to restore states after the transition
+ */
+ void onTaskChanging(
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT);
+
+ /**
+ * Notifies that the given task is about to close to give the window decoration a chance to
+ * prepare for this transition.
*
- * @param startT the start transaction to be applied before the transition
- * @param finishT the finish transaction to restore states after the transition
- * @return {@code true} if window decoration exists, {@code false} otherwise
+ * @param taskInfo the initial task info of the task
+ * @param startT the start transaction to be applied before the transition
+ * @param finishT the finish transaction to restore states after the transition
*/
- boolean setupWindowDecorationForTransition(
+ void onTaskClosing(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT);
@@ -77,7 +92,6 @@ public interface WindowDecorViewModel {
* Destroys the window decoration of the give task.
*
* @param taskInfo the info of the task
- * @return {@code true} if window decoration was destroyed, {@code false} otherwise
*/
- boolean destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
-}
+ void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 7ecb3f3f6355..92154968855f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -251,7 +251,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
- final int captionWidth = loadDimensionPixelSize(resources, params.mCaptionWidthId);
+ final int captionWidth = params.mCaptionWidthId == Resources.ID_NULL
+ ? taskBounds.width()
+ : loadDimensionPixelSize(resources, params.mCaptionWidthId);
startT.setPosition(
mCaptionContainerSurface,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 7068a84c3056..48415d47304c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -103,7 +103,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(mWindowDecorViewModel).createWindowDecoration(
+ verify(mWindowDecorViewModel).onTaskOpening(
change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@@ -120,7 +120,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(mWindowDecorViewModel).setupWindowDecorationForTransition(
+ verify(mWindowDecorViewModel).onTaskClosing(
change.getTaskInfo(), startT, finishT);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
new file mode 100644
index 000000000000..8b134ed1dfe4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.InputChannel;
+import android.view.InputMonitor;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.GrantPermissionRule;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.desktopmode.DesktopModeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+/** Tests of {@link CaptionWindowDecorViewModel} */
+@SmallTest
+public class CaptionWindowDecorViewModelTests extends ShellTestCase {
+ @Mock private CaptionWindowDecoration mCaptionWindowDecoration;
+
+ @Mock private CaptionWindowDecoration.Factory mCaptionWindowDecorFactory;
+
+ @Mock private Handler mMainHandler;
+
+ @Mock private Choreographer mMainChoreographer;
+
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+
+ @Mock private DisplayController mDisplayController;
+
+ @Mock private SyncTransactionQueue mSyncQueue;
+
+ @Mock private DesktopModeController mDesktopModeController;
+
+ @Mock private InputMonitor mInputMonitor;
+
+ @Mock private InputChannel mInputChannel;
+
+ @Mock private CaptionWindowDecorViewModel.EventReceiverFactory mEventReceiverFactory;
+
+ @Mock private CaptionWindowDecorViewModel.EventReceiver mEventReceiver;
+
+ @Mock private InputManager mInputManager;
+
+ private final List<InputManager> mMockInputManagers = new ArrayList<>();
+
+ private CaptionWindowDecorViewModel mCaptionWindowDecorViewModel;
+
+ @Before
+ public void setUp() {
+ mMockInputManagers.add(mInputManager);
+
+ mCaptionWindowDecorViewModel =
+ new CaptionWindowDecorViewModel(
+ mContext,
+ mMainHandler,
+ mMainChoreographer,
+ mTaskOrganizer,
+ mDisplayController,
+ mSyncQueue,
+ mDesktopModeController,
+ mCaptionWindowDecorFactory,
+ new MockObjectSupplier<>(mMockInputManagers, () -> mock(InputManager.class)));
+ mCaptionWindowDecorViewModel.setEventReceiverFactory(mEventReceiverFactory);
+
+ doReturn(mCaptionWindowDecoration)
+ .when(mCaptionWindowDecorFactory)
+ .create(any(), any(), any(), any(), any(), any(), any(), any());
+
+ when(mInputManager.monitorGestureInput(any(), anyInt())).thenReturn(mInputMonitor);
+ when(mEventReceiverFactory.create(any(), any())).thenReturn(mEventReceiver);
+ when(mInputMonitor.getInputChannel()).thenReturn(mInputChannel);
+ }
+
+ @Test
+ public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception {
+ Looper.prepare();
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, WINDOWING_MODE_FREEFORM);
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ GrantPermissionRule.grant(android.Manifest.permission.MONITOR_INPUT);
+
+ mCaptionWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, finishT);
+ verify(mCaptionWindowDecorFactory)
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+ verify(mCaptionWindowDecoration).close();
+ }
+
+ @Test
+ public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception {
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, WINDOWING_MODE_UNDEFINED);
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+
+ mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+
+ verify(mCaptionWindowDecorFactory, never())
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+
+ mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+
+ verify(mCaptionWindowDecorFactory)
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+ }
+
+ private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
+ ActivityManager.RunningTaskInfo taskInfo =
+ new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setVisible(true)
+ .build();
+ taskInfo.taskId = taskId;
+ taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+ return taskInfo;
+ }
+
+ private static class MockObjectSupplier<T> implements Supplier<T> {
+ private final List<T> mObjects;
+ private final Supplier<T> mDefaultSupplier;
+ private int mNumOfCalls = 0;
+
+ private MockObjectSupplier(List<T> objects, Supplier<T> defaultSupplier) {
+ mObjects = objects;
+ mDefaultSupplier = defaultSupplier;
+ }
+
+ @Override
+ public T get() {
+ final T mock =
+ mNumOfCalls < mObjects.size() ? mObjects.get(mNumOfCalls)
+ : mDefaultSupplier.get();
+ ++mNumOfCalls;
+ return mock;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 15181b1549f5..dd9ab9899e13 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -113,6 +113,12 @@ public class WindowDecorationTests extends ShellTestCase {
mMockSurfaceControlFinishT = createMockSurfaceControlTransaction();
mMockSurfaceControlAddWindowT = createMockSurfaceControlTransaction();
+ mRelayoutParams.mLayoutResId = 0;
+ mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
+ // Caption should have fixed width except in testLayoutResultCalculation_fullWidthCaption()
+ mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
+ mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
+
doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
.create(any(), any(), any());
}
@@ -435,6 +441,58 @@ public class WindowDecorationTests extends ShellTestCase {
assertThat(additionalWindow.mWindowSurface).isNull();
}
+ @Test
+ public void testLayoutResultCalculation_fullWidthCaption() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+ createMockSurfaceControlBuilder(taskBackgroundSurface);
+ mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+ final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder captionContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(captionContainerSurface);
+ mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ mMockSurfaceControlTransactions.add(t);
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setBounds(TASK_BOUNDS)
+ .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+ .setVisible(true)
+ .build();
+ taskInfo.isFocused = true;
+ taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ mRelayoutParams.setOutsets(
+ R.dimen.test_window_decor_left_outset,
+ R.dimen.test_window_decor_top_outset,
+ R.dimen.test_window_decor_right_outset,
+ R.dimen.test_window_decor_bottom_outset);
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ mRelayoutParams.mCaptionWidthId = Resources.ID_NULL;
+ windowDecor.relayout(taskInfo);
+
+ verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
+ verify(captionContainerSurfaceBuilder).setContainerLayer();
+ verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, 20, 40);
+ // Width of the captionContainerSurface should match the width of TASK_BOUNDS
+ verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
+ verify(mMockSurfaceControlStartT).show(captionContainerSurface);
+ }
+
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(InstrumentationRegistry.getInstrumentation().getContext(),
@@ -490,11 +548,6 @@ public class WindowDecorationTests extends ShellTestCase {
@Override
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
- mRelayoutParams.mLayoutResId = 0;
- mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
- mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
- mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
-
relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
mMockWindowContainerTransaction, mMockView, mRelayoutResult);
}
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index c70887672f9e..4aee9eb0488e 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -41,8 +41,7 @@ import java.util.concurrent.Executor;
*/
public class AudioDeviceVolumeManager {
- // define when using Log.*
- //private static final String TAG = "AudioDeviceVolumeManager";
+ private static final String TAG = "AudioDeviceVolumeManager";
/** Indicates no special treatment in the handling of the volume adjustment */
public static final int ADJUST_MODE_NORMAL = 0;
@@ -62,11 +61,15 @@ public class AudioDeviceVolumeManager {
private static IAudioService sService;
private final @NonNull String mPackageName;
- private final @Nullable String mAttributionTag;
- public AudioDeviceVolumeManager(Context context) {
+ /**
+ * @hide
+ * Constructor
+ * @param context the Context for the device volume operations
+ */
+ public AudioDeviceVolumeManager(@NonNull Context context) {
+ Objects.requireNonNull(context);
mPackageName = context.getApplicationContext().getOpPackageName();
- mAttributionTag = context.getApplicationContext().getAttributionTag();
}
/**
@@ -308,13 +311,36 @@ public class AudioDeviceVolumeManager {
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada) {
try {
- getService().setDeviceVolume(vi, ada, mPackageName, mAttributionTag);
+ getService().setDeviceVolume(vi, ada, mPackageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns the volume on the given audio device for the given volume information.
+ * For instance if using a {@link VolumeInfo} configured for {@link AudioManager#STREAM_ALARM},
+ * it will return the alarm volume. When no volume index has ever been set for the given
+ * device, the default volume will be returned (the volume setting that would have been
+ * applied if playback for that use case had started).
+ * @param vi the volume information, only stream-based volumes are supported. Information
+ * other than the stream type is ignored.
+ * @param ada the device for which volume is to be retrieved
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @NonNull VolumeInfo getDeviceVolume(@NonNull VolumeInfo vi,
+ @NonNull AudioDeviceAttributes ada) {
+ try {
+ return getService().getDeviceVolume(vi, ada, mPackageName);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
+ return VolumeInfo.getDefaultVolumeInfo();
}
/**
+ * @hide
* Return human-readable name for volume behavior
* @param behavior one of the volume behaviors defined in AudioManager
* @return a string for the given behavior
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e7eda3ea4552..798688ea7b46 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1212,7 +1212,13 @@ public class AudioManager {
}
}
- private static boolean isPublicStreamType(int streamType) {
+ /**
+ * @hide
+ * Checks whether a stream type is part of the public SDK
+ * @param streamType
+ * @return true if the stream type is available in SDK
+ */
+ public static boolean isPublicStreamType(int streamType) {
switch (streamType) {
case STREAM_VOICE_CALL:
case STREAM_SYSTEM:
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 90eb9e644f26..ad933e02c0d5 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -99,7 +99,10 @@ interface IAudioService {
in String callingPackage, in String attributionTag);
void setDeviceVolume(in VolumeInfo vi, in AudioDeviceAttributes ada,
- in String callingPackage, in String attributionTag);
+ in String callingPackage);
+
+ VolumeInfo getDeviceVolume(in VolumeInfo vi, in AudioDeviceAttributes ada,
+ in String callingPackage);
oneway void handleVolumeKey(in KeyEvent event, boolean isOnTv,
String callingPackage, String caller);
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 8a03afb77942..d6fe68253be6 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -86,8 +86,10 @@ public abstract class Image implements AutoCloseable {
*
* <p>
* The format is one of the values from
- * {@link android.graphics.ImageFormat ImageFormat}. The mapping between the
- * formats and the planes is as follows:
+ * {@link android.graphics.ImageFormat ImageFormat},
+ * {@link android.graphics.PixelFormat PixelFormat}, or
+ * {@link android.hardware.HardwareBuffer HardwareBuffer}. The mapping between the
+ * formats and the planes is as follows (any formats not listed will have 1 plane):
* </p>
*
* <table>
@@ -171,15 +173,18 @@ public abstract class Image implements AutoCloseable {
* </tr>
* <tr>
* <td>{@link android.graphics.ImageFormat#YCBCR_P010 YCBCR_P010}</td>
- * <td>1</td>
+ * <td>3</td>
* <td>P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane
- * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit
- * little-endian value, with the lower 6 bits set to zero.
+ * followed by a Wx(H/2) Cb and Cr planes. Each sample is represented by a 16-bit
+ * little-endian value, with the lower 6 bits set to zero. Since this is guaranteed to be
+ * a semi-planar format, the Cb plane can also be treated as an interleaved Cb/Cr plane.
* </td>
* </tr>
* </table>
*
* @see android.graphics.ImageFormat
+ * @see android.graphics.PixelFormat
+ * @see android.hardware.HardwareBuffer
*/
public abstract int getFormat();
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 82c3139bb4d3..b0917c756a21 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -89,6 +89,7 @@ public class Ringtone {
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
+ private boolean mPreferBuiltinDevice;
// playback properties, use synchronized with mPlaybackSettingsLock
private boolean mIsLooping = false;
private float mVolume = 1.0f;
@@ -157,7 +158,39 @@ public class Ringtone {
}
/**
+ * Finds the output device of type {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}. This device is
+ * the one on which outgoing audio for SIM calls is played.
+ *
+ * @param audioManager the audio manage.
+ * @return the {@link AudioDeviceInfo} corresponding to the builtin device, or {@code null} if
+ * none can be found.
+ */
+ private AudioDeviceInfo getBuiltinDevice(AudioManager audioManager) {
+ AudioDeviceInfo[] deviceList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ for (AudioDeviceInfo device : deviceList) {
+ if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+ return device;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the preferred device of the ringtong playback to the built-in device.
+ *
+ * @hide
+ */
+ public boolean preferBuiltinDevice(boolean enable) {
+ mPreferBuiltinDevice = enable;
+ if (mLocalPlayer == null) {
+ return true;
+ }
+ return mLocalPlayer.setPreferredDevice(getBuiltinDevice(mAudioManager));
+ }
+
+ /**
* Creates a local media player for the ringtone using currently set attributes.
+ *
* @hide
*/
public void createLocalMediaPlayer() {
@@ -172,6 +205,8 @@ public class Ringtone {
try {
mLocalPlayer.setDataSource(mContext, mUri);
mLocalPlayer.setAudioAttributes(mAudioAttributes);
+ mLocalPlayer.setPreferredDevice(
+ mPreferBuiltinDevice ? getBuiltinDevice(mAudioManager) : null);
synchronized (mPlaybackSettingsLock) {
applyPlaybackProperties_sync();
}
diff --git a/media/java/android/media/VolumeInfo.java b/media/java/android/media/VolumeInfo.java
index c61b0e57db1a..bc91a09cf681 100644
--- a/media/java/android/media/VolumeInfo.java
+++ b/media/java/android/media/VolumeInfo.java
@@ -27,7 +27,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
-import java.util.List;
import java.util.Objects;
/**
@@ -35,8 +34,9 @@ import java.util.Objects;
* A class to represent type of volume information.
* Can be used to represent volume associated with a stream type or {@link AudioVolumeGroup}.
* Volume index is optional when used to represent a category of volume.
- * Index ranges are supported too, making the representation of volume changes agnostic to the
- * range (e.g. can be used to map BT A2DP absolute volume range to internal range).
+ * Volume ranges are supported too, making the representation of volume changes agnostic
+ * regarding the range of values that are supported (e.g. can be used to map BT A2DP absolute
+ * volume range to internal range).
*
* Note: this class is not yet part of the SystemApi but is intended to be gradually introduced
* particularly in parts of the audio framework that suffer from code ambiguity when
@@ -46,25 +46,27 @@ public final class VolumeInfo implements Parcelable {
private static final String TAG = "VolumeInfo";
private final boolean mUsesStreamType; // false implies AudioVolumeGroup is used
+ private final boolean mHasMuteCommand;
private final boolean mIsMuted;
private final int mVolIndex;
private final int mMinVolIndex;
private final int mMaxVolIndex;
- private final int mVolGroupId;
- private final int mStreamType;
+ private final @Nullable AudioVolumeGroup mVolGroup;
+ private final @AudioManager.PublicStreamTypes int mStreamType;
private static IAudioService sService;
private static VolumeInfo sDefaultVolumeInfo;
- private VolumeInfo(boolean usesStreamType, boolean isMuted, int volIndex,
- int minVolIndex, int maxVolIndex,
- int volGroupId, int streamType) {
+ private VolumeInfo(boolean usesStreamType, boolean hasMuteCommand, boolean isMuted,
+ int volIndex, int minVolIndex, int maxVolIndex,
+ AudioVolumeGroup volGroup, int streamType) {
mUsesStreamType = usesStreamType;
+ mHasMuteCommand = hasMuteCommand;
mIsMuted = isMuted;
mVolIndex = volIndex;
mMinVolIndex = minVolIndex;
mMaxVolIndex = maxVolIndex;
- mVolGroupId = volGroupId;
+ mVolGroup = volGroup;
mStreamType = streamType;
}
@@ -81,8 +83,10 @@ public final class VolumeInfo implements Parcelable {
/**
* Returns the associated stream type, or will throw if {@link #hasStreamType()} returned false.
* @return a stream type value, see AudioManager.STREAM_*
+ * @throws IllegalStateException when called on a VolumeInfo not configured for
+ * stream types.
*/
- public int getStreamType() {
+ public @AudioManager.PublicStreamTypes int getStreamType() {
if (!mUsesStreamType) {
throw new IllegalStateException("VolumeInfo doesn't use stream types");
}
@@ -101,24 +105,28 @@ public final class VolumeInfo implements Parcelable {
/**
* Returns the associated volume group, or will throw if {@link #hasVolumeGroup()} returned
* false.
- * @return the volume group corresponding to this VolumeInfo, or null if an error occurred
- * in the volume group management
+ * @return the volume group corresponding to this VolumeInfo
+ * @throws IllegalStateException when called on a VolumeInfo not configured for
+ * volume groups.
*/
- public @Nullable AudioVolumeGroup getVolumeGroup() {
+ public @NonNull AudioVolumeGroup getVolumeGroup() {
if (mUsesStreamType) {
throw new IllegalStateException("VolumeInfo doesn't use AudioVolumeGroup");
}
- List<AudioVolumeGroup> volGroups = AudioVolumeGroup.getAudioVolumeGroups();
- for (AudioVolumeGroup group : volGroups) {
- if (group.getId() == mVolGroupId) {
- return group;
- }
- }
- return null;
+ return mVolGroup;
+ }
+
+ /**
+ * Return whether this instance is conveying a mute state
+ * @return true if the muted state was explicitly set for this instance
+ */
+ public boolean hasMuteCommand() {
+ return mHasMuteCommand;
}
/**
- * Returns whether this instance is conveying a mute state.
+ * Returns whether this instance is conveying a mute state that was explicitly set
+ * by {@link Builder#setMuted(boolean)}, false otherwise
* @return true if the volume state is muted
*/
public boolean isMuted() {
@@ -185,18 +193,21 @@ public final class VolumeInfo implements Parcelable {
*/
public static final class Builder {
private boolean mUsesStreamType = true; // false implies AudioVolumeGroup is used
- private int mStreamType = AudioManager.STREAM_MUSIC;
+ private @AudioManager.PublicStreamTypes int mStreamType = AudioManager.STREAM_MUSIC;
+ private boolean mHasMuteCommand = false;
private boolean mIsMuted = false;
private int mVolIndex = INDEX_NOT_SET;
private int mMinVolIndex = INDEX_NOT_SET;
private int mMaxVolIndex = INDEX_NOT_SET;
- private int mVolGroupId = -Integer.MIN_VALUE;
+ private @Nullable AudioVolumeGroup mVolGroup;
/**
* Builder constructor for stream type-based VolumeInfo
*/
- public Builder(int streamType) {
- // TODO validate stream type
+ public Builder(@AudioManager.PublicStreamTypes int streamType) {
+ if (!AudioManager.isPublicStreamType(streamType)) {
+ throw new IllegalArgumentException("Not a valid public stream type " + streamType);
+ }
mUsesStreamType = true;
mStreamType = streamType;
}
@@ -208,7 +219,7 @@ public final class VolumeInfo implements Parcelable {
Objects.requireNonNull(volGroup);
mUsesStreamType = false;
mStreamType = -Integer.MIN_VALUE;
- mVolGroupId = volGroup.getId();
+ mVolGroup = volGroup;
}
/**
@@ -219,11 +230,12 @@ public final class VolumeInfo implements Parcelable {
Objects.requireNonNull(info);
mUsesStreamType = info.mUsesStreamType;
mStreamType = info.mStreamType;
+ mHasMuteCommand = info.mHasMuteCommand;
mIsMuted = info.mIsMuted;
mVolIndex = info.mVolIndex;
mMinVolIndex = info.mMinVolIndex;
mMaxVolIndex = info.mMaxVolIndex;
- mVolGroupId = info.mVolGroupId;
+ mVolGroup = info.mVolGroup;
}
/**
@@ -232,6 +244,7 @@ public final class VolumeInfo implements Parcelable {
* @return the same builder instance
*/
public @NonNull Builder setMuted(boolean isMuted) {
+ mHasMuteCommand = true;
mIsMuted = isMuted;
return this;
}
@@ -241,7 +254,6 @@ public final class VolumeInfo implements Parcelable {
* @param volIndex a 0 or greater value, or {@link #INDEX_NOT_SET} if unknown
* @return the same builder instance
*/
- // TODO should we allow muted true + volume index set? (useful when toggling mute on/off?)
public @NonNull Builder setVolumeIndex(int volIndex) {
if (volIndex != INDEX_NOT_SET && volIndex < 0) {
throw new IllegalArgumentException("Volume index cannot be negative");
@@ -296,9 +308,9 @@ public final class VolumeInfo implements Parcelable {
throw new IllegalArgumentException("Min volume index:" + mMinVolIndex
+ " greater than max index:" + mMaxVolIndex);
}
- return new VolumeInfo(mUsesStreamType, mIsMuted,
+ return new VolumeInfo(mUsesStreamType, mHasMuteCommand, mIsMuted,
mVolIndex, mMinVolIndex, mMaxVolIndex,
- mVolGroupId, mStreamType);
+ mVolGroup, mStreamType);
}
}
@@ -306,8 +318,8 @@ public final class VolumeInfo implements Parcelable {
// Parcelable
@Override
public int hashCode() {
- return Objects.hash(mUsesStreamType, mStreamType, mIsMuted,
- mVolIndex, mMinVolIndex, mMaxVolIndex, mVolGroupId);
+ return Objects.hash(mUsesStreamType, mHasMuteCommand, mStreamType, mIsMuted,
+ mVolIndex, mMinVolIndex, mMaxVolIndex, mVolGroup);
}
@Override
@@ -318,19 +330,20 @@ public final class VolumeInfo implements Parcelable {
VolumeInfo that = (VolumeInfo) o;
return ((mUsesStreamType == that.mUsesStreamType)
&& (mStreamType == that.mStreamType)
- && (mIsMuted == that.mIsMuted)
- && (mVolIndex == that.mVolIndex)
- && (mMinVolIndex == that.mMinVolIndex)
- && (mMaxVolIndex == that.mMaxVolIndex)
- && (mVolGroupId == that.mVolGroupId));
+ && (mHasMuteCommand == that.mHasMuteCommand)
+ && (mIsMuted == that.mIsMuted)
+ && (mVolIndex == that.mVolIndex)
+ && (mMinVolIndex == that.mMinVolIndex)
+ && (mMaxVolIndex == that.mMaxVolIndex)
+ && Objects.equals(mVolGroup, that.mVolGroup));
}
@Override
public String toString() {
return new String("VolumeInfo:"
+ (mUsesStreamType ? (" streamType:" + mStreamType)
- : (" volGroupId" + mVolGroupId))
- + " muted:" + mIsMuted
+ : (" volGroup:" + mVolGroup))
+ + (mHasMuteCommand ? (" muted:" + mIsMuted) : ("[no mute cmd]"))
+ ((mVolIndex != INDEX_NOT_SET) ? (" volIndex:" + mVolIndex) : "")
+ ((mMinVolIndex != INDEX_NOT_SET) ? (" min:" + mMinVolIndex) : "")
+ ((mMaxVolIndex != INDEX_NOT_SET) ? (" max:" + mMaxVolIndex) : ""));
@@ -345,21 +358,29 @@ public final class VolumeInfo implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeBoolean(mUsesStreamType);
dest.writeInt(mStreamType);
+ dest.writeBoolean(mHasMuteCommand);
dest.writeBoolean(mIsMuted);
dest.writeInt(mVolIndex);
dest.writeInt(mMinVolIndex);
dest.writeInt(mMaxVolIndex);
- dest.writeInt(mVolGroupId);
+ if (!mUsesStreamType) {
+ mVolGroup.writeToParcel(dest, 0 /*ignored*/);
+ }
}
private VolumeInfo(@NonNull Parcel in) {
mUsesStreamType = in.readBoolean();
mStreamType = in.readInt();
+ mHasMuteCommand = in.readBoolean();
mIsMuted = in.readBoolean();
mVolIndex = in.readInt();
mMinVolIndex = in.readInt();
mMaxVolIndex = in.readInt();
- mVolGroupId = in.readInt();
+ if (!mUsesStreamType) {
+ mVolGroup = AudioVolumeGroup.CREATOR.createFromParcel(in);
+ } else {
+ mVolGroup = null;
+ }
}
public static final @NonNull Parcelable.Creator<VolumeInfo> CREATOR =
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index af05078b4f3a..73bb5eb46f33 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -34,6 +34,8 @@ import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.EventLog;
import android.util.Log;
import java.util.Arrays;
@@ -96,6 +98,22 @@ public class InstallStart extends Activity {
mAbortInstall = true;
}
}
+
+ final String installerPackageNameFromIntent = getIntent().getStringExtra(
+ Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+ if (installerPackageNameFromIntent != null) {
+ final String callingPkgName = getLaunchedFromPackage();
+ if (!TextUtils.equals(installerPackageNameFromIntent, callingPkgName)
+ && mPackageManager.checkPermission(Manifest.permission.INSTALL_PACKAGES,
+ callingPkgName) != PackageManager.PERMISSION_GRANTED) {
+ Log.e(LOG_TAG, "The given installer package name " + installerPackageNameFromIntent
+ + " is invalid. Remove it.");
+ EventLog.writeEvent(0x534e4554, "236687884", getLaunchedFromUid(),
+ "Invalid EXTRA_INSTALLER_PACKAGE_NAME");
+ getIntent().removeExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+ }
+ }
+
if (mAbortInstall) {
setResult(RESULT_CANCELED);
finish();
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index f868c694cef9..e66e3a175336 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -278,7 +278,7 @@
<string name="mock_location_app_set" msgid="4706722469342913843">"Ứng dụng vị trí mô phỏng: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="6829757985772659599">"Mạng"</string>
<string name="wifi_display_certification" msgid="1805579519992520381">"Chứng nhận hiển thị không dây"</string>
- <string name="wifi_verbose_logging" msgid="1785910450009679371">"Bật ghi nhật ký chi tiết Wi‑Fi"</string>
+ <string name="wifi_verbose_logging" msgid="1785910450009679371">"Bật tính năng ghi nhật ký chi tiết Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Hạn chế quét tìm Wi-Fi"</string>
<string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Tạo địa chỉ MAC ngẫu nhiên, không cố định mỗi khi kết nối Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Dữ liệu di động luôn hoạt động"</string>
@@ -290,7 +290,7 @@
<string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chọn phiên bản Bluetooth AVRCP"</string>
<string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Phiên bản Bluetooth MAP"</string>
<string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Chọn phiên bản Bluetooth MAP"</string>
- <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec âm thanh Bluetooth"</string>
+ <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bộ mã hoá và giải mã âm thanh qua Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Kích hoạt chế độ chọn codec\nâm thanh Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Tốc độ lấy mẫu âm thanh Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"Kích hoạt chế độ chọn codec\nâm thanh Bluetooth: Tần số lấy mẫu"</string>
@@ -384,7 +384,7 @@
<string name="window_blurs" msgid="6831008984828425106">"Cho phép làm mờ cửa sổ"</string>
<string name="force_msaa" msgid="4081288296137775550">"Bắt buộc 4x MSAA"</string>
<string name="force_msaa_summary" msgid="9070437493586769500">"Bật 4x MSAA trong ứng dụng OpenGL ES 2.0"</string>
- <string name="show_non_rect_clip" msgid="7499758654867881817">"Gỡ lỗi hoạt động của clip không phải là hình chữ nhật"</string>
+ <string name="show_non_rect_clip" msgid="7499758654867881817">"Gỡ lỗi hoạt động của đoạn không phải hình chữ nhật"</string>
<string name="track_frame_time" msgid="522674651937771106">"Kết xuất HWUI cấu hình"</string>
<string name="enable_gpu_debug_layers" msgid="4986675516188740397">"Bật lớp gỡ lỗi GPU"</string>
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Cho phép tải lớp gỡ lỗi GPU cho ứng dụng gỡ lỗi"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 949bbfbabf0b..d1ac7d00c1cf 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1125,7 +1125,9 @@
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> left until full</string>
<!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited -->
- <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging is paused</string>
+ <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging paused</string>
+ <!-- [CHAR_LIMIT=80] Label for battery charging future pause -->
+ <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging to <xliff:g id="dock_defender_threshold">%2$s</xliff:g></string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8946516af8a2..92a938cb0b47 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -53,6 +53,7 @@ public class SecureSettings {
Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
+ Settings.Secure.CONTRAST_LEVEL,
Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET,
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED,
Settings.Secure.ACCESSIBILITY_CAPTIONING_LOCALE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index cbf79530a6b6..eabf4cc8edfd 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -86,6 +86,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.CONTRAST_LEVEL, new InclusiveFloatRangeValidator(-1f, 1f));
VALIDATORS.put(
Secure.ACCESSIBILITY_CAPTIONING_PRESET,
new DiscreteValueValidator(new String[] {"-1", "0", "1", "2", "3", "4"}));
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index aa3a983e9971..a78faaf90c37 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1756,6 +1756,9 @@ class SettingsProtoDumpUtil {
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
SecureSettingsProto.Accessibility.HIGH_TEXT_CONTRAST_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.CONTRAST_LEVEL,
+ SecureSettingsProto.Accessibility.CONTRAST_LEVEL);
+ dumpSetting(s, p,
Settings.Secure.FONT_WEIGHT_ADJUSTMENT,
SecureSettingsProto.FONT_WEIGHT_ADJUSTMENT);
dumpSetting(s, p,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 3c6f18c76f83..e624441ca777 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -167,25 +167,14 @@ java_library {
}
android_library {
- name: "SystemUI-tests",
+ name: "SystemUI-tests-base",
manifest: "tests/AndroidManifest-base.xml",
- additional_manifests: ["tests/AndroidManifest.xml"],
-
resource_dirs: [
"tests/res",
"res-product",
"res-keyguard",
"res",
],
- srcs: [
- "tests/src/**/*.kt",
- "tests/src/**/*.java",
- "src/**/*.kt",
- "src/**/*.java",
- "src/**/I*.aidl",
- ":ReleaseJavaFiles",
- ":SystemUI-tests-utils",
- ],
static_libs: [
"WifiTrackerLib",
"SystemUIAnimationLib",
@@ -224,9 +213,6 @@ android_library {
"metrics-helper-lib",
"hamcrest-library",
"androidx.test.rules",
- "androidx.test.uiautomator",
- "mockito-target-extended-minus-junit4",
- "androidx.test.ext.junit",
"testables",
"truth-prebuilt",
"monet",
@@ -236,6 +222,27 @@ android_library {
"LowLightDreamLib",
"motion_tool_lib",
],
+}
+
+android_library {
+ name: "SystemUI-tests",
+ manifest: "tests/AndroidManifest-base.xml",
+ additional_manifests: ["tests/AndroidManifest.xml"],
+ srcs: [
+ "tests/src/**/*.kt",
+ "tests/src/**/*.java",
+ "src/**/*.kt",
+ "src/**/*.java",
+ "src/**/I*.aidl",
+ ":ReleaseJavaFiles",
+ ":SystemUI-tests-utils",
+ ],
+ static_libs: [
+ "SystemUI-tests-base",
+ "androidx.test.uiautomator",
+ "mockito-target-extended-minus-junit4",
+ "androidx.test.ext.junit",
+ ],
libs: [
"android.test.runner",
"android.test.base",
@@ -249,6 +256,45 @@ android_library {
plugins: ["dagger2-compiler"],
}
+android_app {
+ name: "SystemUIRobo-stub",
+ defaults: [
+ "platform_app_defaults",
+ "SystemUI_app_defaults",
+ ],
+ manifest: "tests/AndroidManifest-base.xml",
+ static_libs: [
+ "SystemUI-tests-base",
+ ],
+ aaptflags: [
+ "--extra-packages",
+ "com.android.systemui",
+ ],
+ dont_merge_manifests: true,
+ platform_apis: true,
+ system_ext_specific: true,
+ certificate: "platform",
+ privileged: true,
+ resource_dirs: [],
+}
+
+android_robolectric_test {
+ name: "SystemUiRoboTests",
+ srcs: [
+ "tests/robolectric/src/**/*.kt",
+ "tests/robolectric/src/**/*.java",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ "truth-prebuilt",
+ ],
+ kotlincflags: ["-Xjvm-default=enable"],
+ instrumentation_for: "SystemUIRobo-stub",
+ java_resource_dirs: ["tests/robolectric/config"],
+}
+
// Opt-out config for optimizing the SystemUI target using R8.
// Disabled via `export SYSTEMUI_OPTIMIZE_JAVA=false`, or explicitly in Make via
// `SYSTEMUI_OPTIMIZE_JAVA := false`.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 68ff116be4b0..844e88a09b05 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -126,6 +126,9 @@
<uses-permission android:name="android.permission.ALLOW_SLIPPERY_TOUCHES" />
<uses-permission android:name="android.permission.INPUT_CONSUMER" />
+ <!-- DeviceStateManager -->
+ <uses-permission android:name="android.permission.CONTROL_DEVICE_STATE" />
+
<!-- DreamManager -->
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
<uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index f7bcf1faf33a..17ad55f403fb 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -36,8 +36,31 @@ android_library {
static_libs: [
"PluginCoreLib",
+ "androidx.core_core-animation-nodeps",
+ "androidx.core_core-ktx",
+ "androidx.annotation_annotation",
],
manifest: "AndroidManifest.xml",
kotlincflags: ["-Xjvm-default=all"],
}
+
+android_test {
+ name: "SystemUIAnimationLibTests",
+
+ static_libs: [
+ "SystemUIAnimationLib",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "testables",
+ ],
+ libs: [
+ "android.test.base",
+ ],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ test_suites: ["general-tests"],
+}
diff --git a/packages/SystemUI/animation/TEST_MAPPING b/packages/SystemUI/animation/TEST_MAPPING
new file mode 100644
index 000000000000..3dc8510bbf24
--- /dev/null
+++ b/packages/SystemUI/animation/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "SystemUIAnimationLibTests"
+ }
+ ]
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index fdfad2bc2fa1..54aa3516d867 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -75,7 +75,7 @@ constructor(
*/
interface Controller {
/** The [ViewRootImpl] of this controller. */
- val viewRoot: ViewRootImpl
+ val viewRoot: ViewRootImpl?
/**
* The identity object of the source animated by this controller. This animator will ensure
@@ -807,15 +807,17 @@ private class AnimatedDialog(
* inversely, removed from the overlay when the source is moved back to its original position).
*/
private fun synchronizeNextDraw(then: () -> Unit) {
- if (forceDisableSynchronization) {
- // Don't synchronize when inside an automated test.
+ val controllerRootView = controller.viewRoot?.view
+ if (forceDisableSynchronization || controllerRootView == null) {
+ // Don't synchronize when inside an automated test or if the controller root view is
+ // detached.
then()
return
}
- ViewRootSync.synchronizeNextDraw(controller.viewRoot.view, decorView, then)
+ ViewRootSync.synchronizeNextDraw(controllerRootView, decorView, then)
decorView.invalidate()
- controller.viewRoot.view.invalidate()
+ controllerRootView.invalidate()
}
private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
index 80634832acd9..9dbb9205b90e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
@@ -27,7 +27,10 @@ import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
/**
- * Utility class to receive interpolators from
+ * Utility class to receive interpolators from.
+ *
+ * Make sure that changes made to this class are also reflected in {@link InterpolatorsAndroidX}.
+ * Please consider using the androidx dependencies featuring better testability altogether.
*/
public class Interpolators {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java b/packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java
new file mode 100644
index 000000000000..8da87feb1fee
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java
@@ -0,0 +1,219 @@
+/*
+ * 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.animation;
+
+import android.graphics.Path;
+import android.util.MathUtils;
+
+import androidx.core.animation.AccelerateDecelerateInterpolator;
+import androidx.core.animation.AccelerateInterpolator;
+import androidx.core.animation.BounceInterpolator;
+import androidx.core.animation.DecelerateInterpolator;
+import androidx.core.animation.Interpolator;
+import androidx.core.animation.LinearInterpolator;
+import androidx.core.animation.PathInterpolator;
+
+/**
+ * Utility class to receive interpolators from. (androidx compatible version)
+ *
+ * This is the androidx compatible version of {@link Interpolators}. Make sure that changes made to
+ * this class are also reflected in {@link Interpolators}.
+ *
+ * Using the androidx versions of {@link androidx.core.animation.ValueAnimator} or
+ * {@link androidx.core.animation.ObjectAnimator} improves animation testability. This file provides
+ * the androidx compatible versions of the interpolators defined in {@link Interpolators}.
+ * AnimatorTestRule can be used in Tests to manipulate the animation under test (e.g. artificially
+ * advancing the time).
+ */
+public class InterpolatorsAndroidX {
+
+ /*
+ * ============================================================================================
+ * Emphasized interpolators.
+ * ============================================================================================
+ */
+
+ /**
+ * The default emphasized interpolator. Used for hero / emphasized movement of content.
+ */
+ public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
+
+ /**
+ * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
+ * is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 0.8f, 0.15f);
+
+ /**
+ * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
+ * is appearing e.g. when coming from off screen
+ */
+ public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
+ 0.05f, 0.7f, 0.1f, 1f);
+
+
+ /*
+ * ============================================================================================
+ * Standard interpolators.
+ * ============================================================================================
+ */
+
+ /**
+ * The standard interpolator that should be used on every normal animation
+ */
+ public static final Interpolator STANDARD = new PathInterpolator(
+ 0.2f, 0f, 0f, 1f);
+
+ /**
+ * The standard accelerating interpolator that should be used on every regular movement of
+ * content that is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 1f, 1f);
+
+ /**
+ * The standard decelerating interpolator that should be used on every regular movement of
+ * content that is appearing e.g. when coming from off screen.
+ */
+ public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(
+ 0f, 0f, 0f, 1f);
+
+ /*
+ * ============================================================================================
+ * Legacy
+ * ============================================================================================
+ */
+
+ /**
+ * The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN.
+ */
+ public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+ /**
+ * The default legacy accelerating interpolator as defined in Material 1.
+ * Also known as FAST_OUT_LINEAR_IN.
+ */
+ public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f);
+
+ /**
+ * The default legacy decelerating interpolator as defined in Material 1.
+ * Also known as LINEAR_OUT_SLOW_IN.
+ */
+ public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f);
+
+ /**
+ * Linear interpolator. Often used if the interpolator is for different properties who need
+ * different interpolations.
+ */
+ public static final Interpolator LINEAR = new LinearInterpolator();
+
+ /*
+ * ============================================================================================
+ * Custom interpolators
+ * ============================================================================================
+ */
+
+ public static final Interpolator FAST_OUT_SLOW_IN = LEGACY;
+ public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE;
+ public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE;
+
+ /**
+ * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
+ * goes from 1 to 0 instead of 0 to 1).
+ */
+ public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
+ new PathInterpolator(0.8f, 0f, 0.6f, 1f);
+ public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
+ public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+ public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
+ public static final Interpolator ACCELERATE = new AccelerateInterpolator();
+ public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
+ public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
+ public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
+ public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
+ public static final Interpolator ICON_OVERSHOT_LESS = new PathInterpolator(0.4f, 0f, 0.2f,
+ 1.1f);
+ public static final Interpolator PANEL_CLOSE_ACCELERATED = new PathInterpolator(0.3f, 0, 0.5f,
+ 1);
+ public static final Interpolator BOUNCE = new BounceInterpolator();
+ /**
+ * For state transitions on the control panel that lives in GlobalActions.
+ */
+ public static final Interpolator CONTROL_STATE = new PathInterpolator(0.4f, 0f, 0.2f,
+ 1.0f);
+
+ /**
+ * Interpolator to be used when animating a move based on a click. Pair with enough duration.
+ */
+ public static final Interpolator TOUCH_RESPONSE =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
+ /**
+ * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t
+ * goes from 1 to 0 instead of 0 to 1).
+ */
+ public static final Interpolator TOUCH_RESPONSE_REVERSE =
+ new PathInterpolator(0.9f, 0f, 0.7f, 1f);
+
+ /*
+ * ============================================================================================
+ * Functions / Utilities
+ * ============================================================================================
+ */
+
+ /**
+ * Calculate the amount of overshoot using an exponential falloff function with desired
+ * properties, where the overshoot smoothly transitions at the 1.0f boundary into the
+ * overshoot, retaining its acceleration.
+ *
+ * @param progress a progress value going from 0 to 1
+ * @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max
+ * value of the overall progress will be at 1.1.
+ * @param overshootStart the point in (0,1] where the result should reach 1
+ * @return the interpolated overshoot
+ */
+ public static float getOvershootInterpolation(float progress, float overshootAmount,
+ float overshootStart) {
+ if (overshootAmount == 0.0f || overshootStart == 0.0f) {
+ throw new IllegalArgumentException("Invalid values for overshoot");
+ }
+ float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart;
+ return MathUtils.max(0.0f,
+ (float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f));
+ }
+
+ /**
+ * Similar to {@link #getOvershootInterpolation(float, float, float)} but the overshoot
+ * starts immediately here, instead of first having a section of non-overshooting
+ *
+ * @param progress a progress value going from 0 to 1
+ */
+ public static float getOvershootInterpolation(float progress) {
+ return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress)));
+ }
+
+ // Create the default emphasized interpolator
+ private static PathInterpolator createEmphasizedInterpolator() {
+ Path path = new Path();
+ // Doing the same as fast_out_extra_slow_in
+ path.moveTo(0f, 0f);
+ path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
+ path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
+ return new PathInterpolator(path);
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
index f9c6841f96b5..0e2d23b04a4f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
@@ -195,8 +195,16 @@ class RemoteTransitionAdapter {
val out = ArrayList<RemoteAnimationTarget>()
for (i in info.changes.indices) {
val change = info.changes[i]
- val changeIsWallpaper = change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0
- if (wallpapers != changeIsWallpaper) continue
+ if (change.hasFlags(TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+ // For embedded container, when the parent Task is also in the transition, we
+ // should only animate the parent Task.
+ if (change.parent != null) continue
+ // For embedded container without parent, we should only animate if it fills
+ // the Task. Otherwise we may animate only partial of the Task.
+ if (!change.hasFlags(TransitionInfo.FLAG_FILLS_TASK)) continue
+ }
+ // Check if it is wallpaper
+ if (wallpapers != change.hasFlags(TransitionInfo.FLAG_IS_WALLPAPER)) continue
out.add(createTarget(change, info.changes.size - i, info, t))
if (leashMap != null) {
leashMap[change.leash] = out[out.size - 1].leash
@@ -320,9 +328,7 @@ class RemoteTransitionAdapter {
counterWallpaper.cleanUp(finishTransaction)
// Release surface references now. This is apparently to free GPU
// memory while doing quick operations (eg. during CTS).
- for (i in info.changes.indices.reversed()) {
- info.changes[i].leash.release()
- }
+ info.releaseAllSurfaces()
for (i in leashMap.size - 1 downTo 0) {
leashMap.valueAt(i).release()
}
@@ -331,6 +337,7 @@ class RemoteTransitionAdapter {
null /* wct */,
finishTransaction
)
+ finishTransaction.close()
} catch (e: RemoteException) {
Log.e(
"ActivityOptionsCompat",
@@ -364,6 +371,9 @@ class RemoteTransitionAdapter {
) {
// TODO: hook up merge to recents onTaskAppeared if applicable. Until then,
// ignore any incoming merges.
+ // Clean up stuff though cuz GC takes too long for benchmark tests.
+ t.close()
+ info.releaseAllSurfaces()
}
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
index ecee598afe4e..964ef8c88098 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
@@ -28,7 +28,7 @@ internal constructor(
private val source: View,
override val cuj: DialogCuj?,
) : DialogLaunchAnimator.Controller {
- override val viewRoot: ViewRootImpl
+ override val viewRoot: ViewRootImpl?
get() = source.viewRootImpl
override val sourceIdentity: Any = source
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
index 93e78acc63fd..93e78acc63fd 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
index f558fee776e6..b8dc223af56c 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
@@ -22,6 +22,7 @@ import android.graphics.Paint
import android.util.AttributeSet
import android.util.Log
import android.view.View
+import androidx.annotation.VisibleForTesting
/**
* A view that allows multiple ripples to play.
@@ -30,7 +31,8 @@ import android.view.View
*/
class MultiRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
- internal val ripples = ArrayList<RippleAnimation>()
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ val ripples = ArrayList<RippleAnimation>()
private val listeners = ArrayList<RipplesFinishedListener>()
private val ripplePaint = Paint()
private var isWarningLogged = false
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
index b2f8994ebf51..0e3d41c40c97 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
@@ -19,11 +19,13 @@ package com.android.systemui.surfaceeffects.ripple
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
+import androidx.annotation.VisibleForTesting
import androidx.core.graphics.ColorUtils
/** A single ripple animation. */
class RippleAnimation(private val config: RippleAnimationConfig) {
- internal val rippleShader: RippleShader = RippleShader(config.rippleShape)
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ val rippleShader: RippleShader = RippleShader(config.rippleShape)
private val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f)
init {
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
index 773ac55130d4..773ac55130d4 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
index a950d34513ee..f55fb97a9a97 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
@@ -32,7 +32,7 @@ import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
*
* Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
*/
-class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.CIRCLE) :
+class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) :
RuntimeShader(buildShader(rippleShape)) {
/** Shapes that the [RippleShader] supports. */
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
index ae28a8b6dc2e..ae28a8b6dc2e 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
index 8b2f46648fef..8b2f46648fef 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
index d78e0c153db8..d78e0c153db8 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
index 5ac3aad749fc..5ac3aad749fc 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
index 4c7e5f4c7093..4c7e5f4c7093 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt
index 19c114d2693c..19c114d2693c 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
index 8649d5924587..68712c6cd1de 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
@@ -68,7 +68,7 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex
canvas.drawPaint(paint)
}
- internal fun play(config: TurbulenceNoiseAnimationConfig) {
+ fun play(config: TurbulenceNoiseAnimationConfig) {
if (isPlaying) {
return // Ignore if the animation is playing.
}
diff --git a/packages/SystemUI/animation/tests/com/android/systemui/animation/InterpolatorsAndroidXTest.kt b/packages/SystemUI/animation/tests/com/android/systemui/animation/InterpolatorsAndroidXTest.kt
new file mode 100644
index 000000000000..389eed015820
--- /dev/null
+++ b/packages/SystemUI/animation/tests/com/android/systemui/animation/InterpolatorsAndroidXTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.animation
+
+import androidx.test.filters.SmallTest
+import java.lang.reflect.Modifier
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class InterpolatorsAndroidXTest {
+
+ @Test
+ fun testInterpolatorsAndInterpolatorsAndroidXPublicMethodsAreEqual() {
+ assertEquals(
+ Interpolators::class.java.getPublicMethods(),
+ InterpolatorsAndroidX::class.java.getPublicMethods()
+ )
+ }
+
+ @Test
+ fun testInterpolatorsAndInterpolatorsAndroidXPublicFieldsAreEqual() {
+ assertEquals(
+ Interpolators::class.java.getPublicFields(),
+ InterpolatorsAndroidX::class.java.getPublicFields()
+ )
+ }
+
+ private fun <T> Class<T>.getPublicMethods() =
+ declaredMethods
+ .filter { Modifier.isPublic(it.modifiers) }
+ .map { it.toString().replace(name, "") }
+ .toSet()
+
+ private fun <T> Class<T>.getPublicFields() =
+ fields.filter { Modifier.isPublic(it.modifiers) }.map { it.name }.toSet()
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
index 50c3d7e1e76b..d6db574a34ae 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
@@ -262,7 +262,7 @@ internal class ExpandableControllerImpl(
private fun dialogController(cuj: DialogCuj?): DialogLaunchAnimator.Controller {
return object : DialogLaunchAnimator.Controller {
- override val viewRoot: ViewRootImpl = composeViewRoot.viewRootImpl
+ override val viewRoot: ViewRootImpl? = composeViewRoot.viewRootImpl
override val sourceIdentity: Any = this@ExpandableControllerImpl
override val cuj: DialogCuj? = cuj
diff --git a/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt b/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
index e611e8bf0068..979e1a08b7d4 100644
--- a/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
+++ b/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
@@ -38,12 +38,18 @@ import platform.test.screenshot.ScreenshotTestRule
import platform.test.screenshot.getEmulatedDevicePathConfig
/** A rule for Compose screenshot diff tests. */
-class ComposeScreenshotTestRule(emulationSpec: DeviceEmulationSpec) : TestRule {
+class ComposeScreenshotTestRule(
+ emulationSpec: DeviceEmulationSpec,
+ assetPathRelativeToBuildRoot: String
+) : TestRule {
private val colorsRule = MaterialYouColorsRule()
private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
private val screenshotRule =
ScreenshotTestRule(
- SystemUIGoldenImagePathManager(getEmulatedDevicePathConfig(emulationSpec))
+ SystemUIGoldenImagePathManager(
+ getEmulatedDevicePathConfig(emulationSpec),
+ assetPathRelativeToBuildRoot
+ )
)
private val composeRule = createAndroidComposeRule<ScreenshotActivity>()
private val delegateRule =
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 869884474ffe..e1f21742bf93 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -20,7 +20,6 @@ import android.graphics.Rect
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
-import android.view.View
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import com.android.systemui.customization.R
@@ -152,15 +151,9 @@ class DefaultClockController(
view: AnimatableClockView,
) : DefaultClockFaceController(view) {
override fun recomputePadding(targetRegion: Rect?) {
- // We center the view within the targetRegion instead of within the parent
- // view by computing the difference and adding that to the padding.
- val parent = view.parent
- val yDiff =
- if (targetRegion != null && parent is View && parent.isLaidOut())
- targetRegion.centerY() - parent.height / 2f
- else 0f
+ // Ignore Target Region until top padding fixed in aod
val lp = view.getLayoutParams() as FrameLayout.LayoutParams
- lp.topMargin = (-0.5f * view.bottom + yDiff).toInt()
+ lp.topMargin = (-0.5f * view.bottom).toInt()
view.setLayoutParams(lp)
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt
new file mode 100644
index 000000000000..cb1a5f958ed6
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.shared.quickaffordance.data.content
+
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+
+class FakeKeyguardQuickAffordanceProviderClient(
+ slots: List<KeyguardQuickAffordanceProviderClient.Slot> =
+ listOf(
+ KeyguardQuickAffordanceProviderClient.Slot(
+ id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ capacity = 1,
+ ),
+ KeyguardQuickAffordanceProviderClient.Slot(
+ id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
+ capacity = 1,
+ ),
+ ),
+ affordances: List<KeyguardQuickAffordanceProviderClient.Affordance> =
+ listOf(
+ KeyguardQuickAffordanceProviderClient.Affordance(
+ id = AFFORDANCE_1,
+ name = AFFORDANCE_1,
+ iconResourceId = 1,
+ ),
+ KeyguardQuickAffordanceProviderClient.Affordance(
+ id = AFFORDANCE_2,
+ name = AFFORDANCE_2,
+ iconResourceId = 2,
+ ),
+ KeyguardQuickAffordanceProviderClient.Affordance(
+ id = AFFORDANCE_3,
+ name = AFFORDANCE_3,
+ iconResourceId = 3,
+ ),
+ ),
+ flags: List<KeyguardQuickAffordanceProviderClient.Flag> =
+ listOf(
+ KeyguardQuickAffordanceProviderClient.Flag(
+ name = KeyguardQuickAffordanceProviderContract.FlagsTable.FLAG_NAME_FEATURE_ENABLED,
+ value = true,
+ )
+ ),
+) : KeyguardQuickAffordanceProviderClient {
+
+ private val slots = MutableStateFlow(slots)
+ private val affordances = MutableStateFlow(affordances)
+ private val flags = MutableStateFlow(flags)
+
+ private val selections = MutableStateFlow<Map<String, List<String>>>(emptyMap())
+
+ override suspend fun insertSelection(slotId: String, affordanceId: String) {
+ val slotCapacity =
+ querySlots().find { it.id == slotId }?.capacity
+ ?: error("Slot with ID \"$slotId\" not found!")
+ val affordances = selections.value.getOrDefault(slotId, mutableListOf()).toMutableList()
+ while (affordances.size + 1 > slotCapacity) {
+ affordances.removeAt(0)
+ }
+ affordances.remove(affordanceId)
+ affordances.add(affordanceId)
+ selections.value = selections.value.toMutableMap().apply { this[slotId] = affordances }
+ }
+
+ override suspend fun querySlots(): List<KeyguardQuickAffordanceProviderClient.Slot> {
+ return slots.value
+ }
+
+ override suspend fun queryFlags(): List<KeyguardQuickAffordanceProviderClient.Flag> {
+ return flags.value
+ }
+
+ override fun observeSlots(): Flow<List<KeyguardQuickAffordanceProviderClient.Slot>> {
+ return slots.asStateFlow()
+ }
+
+ override fun observeFlags(): Flow<List<KeyguardQuickAffordanceProviderClient.Flag>> {
+ return flags.asStateFlow()
+ }
+
+ override suspend fun queryAffordances():
+ List<KeyguardQuickAffordanceProviderClient.Affordance> {
+ return affordances.value
+ }
+
+ override fun observeAffordances():
+ Flow<List<KeyguardQuickAffordanceProviderClient.Affordance>> {
+ return affordances.asStateFlow()
+ }
+
+ override suspend fun querySelections(): List<KeyguardQuickAffordanceProviderClient.Selection> {
+ return toSelectionList(selections.value, affordances.value)
+ }
+
+ override fun observeSelections(): Flow<List<KeyguardQuickAffordanceProviderClient.Selection>> {
+ return combine(selections, affordances) { selections, affordances ->
+ toSelectionList(selections, affordances)
+ }
+ }
+
+ override suspend fun deleteSelection(slotId: String, affordanceId: String) {
+ val affordances = selections.value.getOrDefault(slotId, mutableListOf()).toMutableList()
+ affordances.remove(affordanceId)
+
+ selections.value = selections.value.toMutableMap().apply { this[slotId] = affordances }
+ }
+
+ override suspend fun deleteAllSelections(slotId: String) {
+ selections.value = selections.value.toMutableMap().apply { this[slotId] = emptyList() }
+ }
+
+ override suspend fun getAffordanceIcon(iconResourceId: Int, tintColor: Int): Drawable {
+ return when (iconResourceId) {
+ 1 -> ICON_1
+ 2 -> ICON_2
+ 3 -> ICON_3
+ else -> BitmapDrawable()
+ }
+ }
+
+ fun setFlag(
+ name: String,
+ value: Boolean,
+ ) {
+ flags.value =
+ flags.value.toMutableList().apply {
+ removeIf { it.name == name }
+ add(KeyguardQuickAffordanceProviderClient.Flag(name = name, value = value))
+ }
+ }
+
+ fun setSlotCapacity(slotId: String, capacity: Int) {
+ slots.value =
+ slots.value.toMutableList().apply {
+ val index = indexOfFirst { it.id == slotId }
+ check(index != -1) { "Slot with ID \"$slotId\" doesn't exist!" }
+ set(
+ index,
+ KeyguardQuickAffordanceProviderClient.Slot(id = slotId, capacity = capacity)
+ )
+ }
+ }
+
+ fun addAffordance(affordance: KeyguardQuickAffordanceProviderClient.Affordance): Int {
+ affordances.value = affordances.value + listOf(affordance)
+ return affordances.value.size - 1
+ }
+
+ private fun toSelectionList(
+ selections: Map<String, List<String>>,
+ affordances: List<KeyguardQuickAffordanceProviderClient.Affordance>,
+ ): List<KeyguardQuickAffordanceProviderClient.Selection> {
+ return selections
+ .map { (slotId, affordanceIds) ->
+ affordanceIds.map { affordanceId ->
+ val affordanceName =
+ affordances.find { it.id == affordanceId }?.name
+ ?: error("No affordance with ID of \"$affordanceId\"!")
+ KeyguardQuickAffordanceProviderClient.Selection(
+ slotId = slotId,
+ affordanceId = affordanceId,
+ affordanceName = affordanceName,
+ )
+ }
+ }
+ .flatten()
+ }
+
+ companion object {
+ const val AFFORDANCE_1 = "affordance_1"
+ const val AFFORDANCE_2 = "affordance_2"
+ const val AFFORDANCE_3 = "affordance_3"
+ val ICON_1 = BitmapDrawable()
+ val ICON_2 = BitmapDrawable()
+ val ICON_3 = BitmapDrawable()
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderClient.kt
new file mode 100644
index 000000000000..3213b2e97ac9
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderClient.kt
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.shared.quickaffordance.data.content
+
+import android.annotation.SuppressLint
+import android.content.ContentValues
+import android.content.Context
+import android.database.ContentObserver
+import android.graphics.Color
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import androidx.annotation.DrawableRes
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
+
+/** Client for using a content provider implementing the [Contract]. */
+interface KeyguardQuickAffordanceProviderClient {
+
+ /**
+ * Selects an affordance with the given ID for a slot on the lock screen with the given ID.
+ *
+ * Note that the maximum number of selected affordances on this slot is automatically enforced.
+ * Selecting a slot that is already full (e.g. already has a number of selected affordances at
+ * its maximum capacity) will automatically remove the oldest selected affordance before adding
+ * the one passed in this call. Additionally, selecting an affordance that's already one of the
+ * selected affordances on the slot will move the selected affordance to the newest location in
+ * the slot.
+ */
+ suspend fun insertSelection(
+ slotId: String,
+ affordanceId: String,
+ )
+
+ /** Returns all available slots supported by the device. */
+ suspend fun querySlots(): List<Slot>
+
+ /** Returns the list of flags. */
+ suspend fun queryFlags(): List<Flag>
+
+ /**
+ * Returns [Flow] for observing the collection of slots.
+ *
+ * @see [querySlots]
+ */
+ fun observeSlots(): Flow<List<Slot>>
+
+ /**
+ * Returns [Flow] for observing the collection of flags.
+ *
+ * @see [queryFlags]
+ */
+ fun observeFlags(): Flow<List<Flag>>
+
+ /**
+ * Returns all available affordances supported by the device, regardless of current slot
+ * placement.
+ */
+ suspend fun queryAffordances(): List<Affordance>
+
+ /**
+ * Returns [Flow] for observing the collection of affordances.
+ *
+ * @see [queryAffordances]
+ */
+ fun observeAffordances(): Flow<List<Affordance>>
+
+ /** Returns the current slot-affordance selections. */
+ suspend fun querySelections(): List<Selection>
+
+ /**
+ * Returns [Flow] for observing the collection of selections.
+ *
+ * @see [querySelections]
+ */
+ fun observeSelections(): Flow<List<Selection>>
+
+ /** Unselects an affordance with the given ID from the slot with the given ID. */
+ suspend fun deleteSelection(
+ slotId: String,
+ affordanceId: String,
+ )
+
+ /** Unselects all affordances from the slot with the given ID. */
+ suspend fun deleteAllSelections(
+ slotId: String,
+ )
+
+ /** Returns a [Drawable] with the given ID, loaded from the system UI package. */
+ suspend fun getAffordanceIcon(
+ @DrawableRes iconResourceId: Int,
+ tintColor: Int = Color.WHITE,
+ ): Drawable
+
+ /** Models a slot. A position that quick affordances can be positioned in. */
+ data class Slot(
+ /** Unique ID of the slot. */
+ val id: String,
+ /**
+ * The maximum number of quick affordances that are allowed to be positioned in this slot.
+ */
+ val capacity: Int,
+ )
+
+ /**
+ * Models a quick affordance. An action that can be selected by the user to appear in one or
+ * more slots on the lock screen.
+ */
+ data class Affordance(
+ /** Unique ID of the quick affordance. */
+ val id: String,
+ /** User-facing label for this affordance. */
+ val name: String,
+ /**
+ * Resource ID for the user-facing icon for this affordance. This resource is hosted by the
+ * System UI process so it must be used with
+ * `PackageManager.getResourcesForApplication(String)`.
+ */
+ val iconResourceId: Int,
+ /**
+ * Whether the affordance is enabled. Disabled affordances should be shown on the picker but
+ * should be rendered as "disabled". When tapped, the enablement properties should be used
+ * to populate UI that would explain to the user what to do in order to re-enable this
+ * affordance.
+ */
+ val isEnabled: Boolean = true,
+ /**
+ * If the affordance is disabled, this is a set of instruction messages to be shown to the
+ * user when the disabled affordance is selected. The instructions should help the user
+ * figure out what to do in order to re-neable this affordance.
+ */
+ val enablementInstructions: List<String>? = null,
+ /**
+ * If the affordance is disabled, this is a label for a button shown together with the set
+ * of instruction messages when the disabled affordance is selected. The button should help
+ * send the user to a flow that would help them achieve the instructions and re-enable this
+ * affordance.
+ *
+ * If `null`, the button should not be shown.
+ */
+ val enablementActionText: String? = null,
+ /**
+ * If the affordance is disabled, this is a "component name" of the format
+ * `packageName/action` to be used as an `Intent` for `startActivity` when the action button
+ * (shown together with the set of instruction messages when the disabled affordance is
+ * selected) is clicked by the user. The button should help send the user to a flow that
+ * would help them achieve the instructions and re-enable this affordance.
+ *
+ * If `null`, the button should not be shown.
+ */
+ val enablementActionComponentName: String? = null,
+ )
+
+ /** Models a selection of a quick affordance on a slot. */
+ data class Selection(
+ /** The unique ID of the slot. */
+ val slotId: String,
+ /** The unique ID of the quick affordance. */
+ val affordanceId: String,
+ /** The user-visible label for the quick affordance. */
+ val affordanceName: String,
+ )
+
+ /** Models a System UI flag. */
+ data class Flag(
+ /** The name of the flag. */
+ val name: String,
+ /** The value of the flag. */
+ val value: Boolean,
+ )
+}
+
+class KeyguardQuickAffordanceProviderClientImpl(
+ private val context: Context,
+ private val backgroundDispatcher: CoroutineDispatcher,
+) : KeyguardQuickAffordanceProviderClient {
+
+ override suspend fun insertSelection(
+ slotId: String,
+ affordanceId: String,
+ ) {
+ withContext(backgroundDispatcher) {
+ context.contentResolver.insert(
+ Contract.SelectionTable.URI,
+ ContentValues().apply {
+ put(Contract.SelectionTable.Columns.SLOT_ID, slotId)
+ put(Contract.SelectionTable.Columns.AFFORDANCE_ID, affordanceId)
+ }
+ )
+ }
+ }
+
+ override suspend fun querySlots(): List<KeyguardQuickAffordanceProviderClient.Slot> {
+ return withContext(backgroundDispatcher) {
+ context.contentResolver
+ .query(
+ Contract.SlotTable.URI,
+ null,
+ null,
+ null,
+ null,
+ )
+ ?.use { cursor ->
+ buildList {
+ val idColumnIndex = cursor.getColumnIndex(Contract.SlotTable.Columns.ID)
+ val capacityColumnIndex =
+ cursor.getColumnIndex(Contract.SlotTable.Columns.CAPACITY)
+ if (idColumnIndex == -1 || capacityColumnIndex == -1) {
+ return@buildList
+ }
+
+ while (cursor.moveToNext()) {
+ add(
+ KeyguardQuickAffordanceProviderClient.Slot(
+ id = cursor.getString(idColumnIndex),
+ capacity = cursor.getInt(capacityColumnIndex),
+ )
+ )
+ }
+ }
+ }
+ }
+ ?: emptyList()
+ }
+
+ override suspend fun queryFlags(): List<KeyguardQuickAffordanceProviderClient.Flag> {
+ return withContext(backgroundDispatcher) {
+ context.contentResolver
+ .query(
+ Contract.FlagsTable.URI,
+ null,
+ null,
+ null,
+ null,
+ )
+ ?.use { cursor ->
+ buildList {
+ val nameColumnIndex =
+ cursor.getColumnIndex(Contract.FlagsTable.Columns.NAME)
+ val valueColumnIndex =
+ cursor.getColumnIndex(Contract.FlagsTable.Columns.VALUE)
+ if (nameColumnIndex == -1 || valueColumnIndex == -1) {
+ return@buildList
+ }
+
+ while (cursor.moveToNext()) {
+ add(
+ KeyguardQuickAffordanceProviderClient.Flag(
+ name = cursor.getString(nameColumnIndex),
+ value = cursor.getInt(valueColumnIndex) == 1,
+ )
+ )
+ }
+ }
+ }
+ }
+ ?: emptyList()
+ }
+
+ override fun observeSlots(): Flow<List<KeyguardQuickAffordanceProviderClient.Slot>> {
+ return observeUri(Contract.SlotTable.URI).map { querySlots() }
+ }
+
+ override fun observeFlags(): Flow<List<KeyguardQuickAffordanceProviderClient.Flag>> {
+ return observeUri(Contract.FlagsTable.URI).map { queryFlags() }
+ }
+
+ override suspend fun queryAffordances():
+ List<KeyguardQuickAffordanceProviderClient.Affordance> {
+ return withContext(backgroundDispatcher) {
+ context.contentResolver
+ .query(
+ Contract.AffordanceTable.URI,
+ null,
+ null,
+ null,
+ null,
+ )
+ ?.use { cursor ->
+ buildList {
+ val idColumnIndex =
+ cursor.getColumnIndex(Contract.AffordanceTable.Columns.ID)
+ val nameColumnIndex =
+ cursor.getColumnIndex(Contract.AffordanceTable.Columns.NAME)
+ val iconColumnIndex =
+ cursor.getColumnIndex(Contract.AffordanceTable.Columns.ICON)
+ val isEnabledColumnIndex =
+ cursor.getColumnIndex(Contract.AffordanceTable.Columns.IS_ENABLED)
+ val enablementInstructionsColumnIndex =
+ cursor.getColumnIndex(
+ Contract.AffordanceTable.Columns.ENABLEMENT_INSTRUCTIONS
+ )
+ val enablementActionTextColumnIndex =
+ cursor.getColumnIndex(
+ Contract.AffordanceTable.Columns.ENABLEMENT_ACTION_TEXT
+ )
+ val enablementComponentNameColumnIndex =
+ cursor.getColumnIndex(
+ Contract.AffordanceTable.Columns.ENABLEMENT_COMPONENT_NAME
+ )
+ if (
+ idColumnIndex == -1 ||
+ nameColumnIndex == -1 ||
+ iconColumnIndex == -1 ||
+ isEnabledColumnIndex == -1 ||
+ enablementInstructionsColumnIndex == -1 ||
+ enablementActionTextColumnIndex == -1 ||
+ enablementComponentNameColumnIndex == -1
+ ) {
+ return@buildList
+ }
+
+ while (cursor.moveToNext()) {
+ add(
+ KeyguardQuickAffordanceProviderClient.Affordance(
+ id = cursor.getString(idColumnIndex),
+ name = cursor.getString(nameColumnIndex),
+ iconResourceId = cursor.getInt(iconColumnIndex),
+ isEnabled = cursor.getInt(isEnabledColumnIndex) == 1,
+ enablementInstructions =
+ cursor
+ .getString(enablementInstructionsColumnIndex)
+ ?.split(
+ Contract.AffordanceTable
+ .ENABLEMENT_INSTRUCTIONS_DELIMITER
+ ),
+ enablementActionText =
+ cursor.getString(enablementActionTextColumnIndex),
+ enablementActionComponentName =
+ cursor.getString(enablementComponentNameColumnIndex),
+ )
+ )
+ }
+ }
+ }
+ }
+ ?: emptyList()
+ }
+
+ override fun observeAffordances():
+ Flow<List<KeyguardQuickAffordanceProviderClient.Affordance>> {
+ return observeUri(Contract.AffordanceTable.URI).map { queryAffordances() }
+ }
+
+ override suspend fun querySelections(): List<KeyguardQuickAffordanceProviderClient.Selection> {
+ return withContext(backgroundDispatcher) {
+ context.contentResolver
+ .query(
+ Contract.SelectionTable.URI,
+ null,
+ null,
+ null,
+ null,
+ )
+ ?.use { cursor ->
+ buildList {
+ val slotIdColumnIndex =
+ cursor.getColumnIndex(Contract.SelectionTable.Columns.SLOT_ID)
+ val affordanceIdColumnIndex =
+ cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_ID)
+ val affordanceNameColumnIndex =
+ cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_NAME)
+ if (
+ slotIdColumnIndex == -1 ||
+ affordanceIdColumnIndex == -1 ||
+ affordanceNameColumnIndex == -1
+ ) {
+ return@buildList
+ }
+
+ while (cursor.moveToNext()) {
+ add(
+ KeyguardQuickAffordanceProviderClient.Selection(
+ slotId = cursor.getString(slotIdColumnIndex),
+ affordanceId = cursor.getString(affordanceIdColumnIndex),
+ affordanceName = cursor.getString(affordanceNameColumnIndex),
+ )
+ )
+ }
+ }
+ }
+ }
+ ?: emptyList()
+ }
+
+ override fun observeSelections(): Flow<List<KeyguardQuickAffordanceProviderClient.Selection>> {
+ return observeUri(Contract.SelectionTable.URI).map { querySelections() }
+ }
+
+ override suspend fun deleteSelection(
+ slotId: String,
+ affordanceId: String,
+ ) {
+ withContext(backgroundDispatcher) {
+ context.contentResolver.delete(
+ Contract.SelectionTable.URI,
+ "${Contract.SelectionTable.Columns.SLOT_ID} = ? AND" +
+ " ${Contract.SelectionTable.Columns.AFFORDANCE_ID} = ?",
+ arrayOf(
+ slotId,
+ affordanceId,
+ ),
+ )
+ }
+ }
+
+ override suspend fun deleteAllSelections(
+ slotId: String,
+ ) {
+ withContext(backgroundDispatcher) {
+ context.contentResolver.delete(
+ Contract.SelectionTable.URI,
+ Contract.SelectionTable.Columns.SLOT_ID,
+ arrayOf(
+ slotId,
+ ),
+ )
+ }
+ }
+
+ @SuppressLint("UseCompatLoadingForDrawables")
+ override suspend fun getAffordanceIcon(
+ @DrawableRes iconResourceId: Int,
+ tintColor: Int,
+ ): Drawable {
+ return withContext(backgroundDispatcher) {
+ context.packageManager
+ .getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME)
+ .getDrawable(iconResourceId, context.theme)
+ .apply { setTint(tintColor) }
+ }
+ }
+
+ private fun observeUri(
+ uri: Uri,
+ ): Flow<Unit> {
+ return callbackFlow {
+ val observer =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(Unit)
+ }
+ }
+
+ context.contentResolver.registerContentObserver(
+ uri,
+ /* notifyForDescendants= */ true,
+ observer,
+ )
+
+ awaitClose { context.contentResolver.unregisterContentObserver(observer) }
+ }
+ .onStart { emit(Unit) }
+ }
+
+ companion object {
+ private const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt
index 98d8d3eb9a4a..17be74b08690 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.shared.keyguard.data.content
+package com.android.systemui.shared.quickaffordance.data.content
import android.content.ContentResolver
import android.net.Uri
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/shared/model/KeyguardQuickAffordanceSlots.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordanceSlots.kt
index 2dc7a280e423..2dc7a280e423 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/shared/model/KeyguardQuickAffordanceSlots.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardQuickAffordanceSlots.kt
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 0abbb1e5bd84..b75c5c722403 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -753,7 +753,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 1282d771579e..f96644fbd667 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -23,14 +23,11 @@
-keep class ** extends androidx.preference.PreferenceFragment
-keep class com.android.systemui.tuner.*
-# The plugins and animation subpackages both act as shared libraries that might be referenced in
+# The plugins subpackage acts as a shared library that might be referenced in
# dynamically-loaded plugin APKs.
-keep class com.android.systemui.plugins.** {
*;
}
--keep class !com.android.systemui.animation.R$**,com.android.systemui.animation.** {
- *;
-}
-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
*;
}
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_flashlight_off.xml b/packages/SystemUI/res-keyguard/drawable/ic_flashlight_off.xml
new file mode 100644
index 000000000000..e850d6823e97
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_flashlight_off.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#1f1f1f"
+ android:pathData="M8,22V11L6,8V2H18V8L16,11V22ZM12,15.5Q11.375,15.5 10.938,15.062Q10.5,14.625 10.5,14Q10.5,13.375 10.938,12.938Q11.375,12.5 12,12.5Q12.625,12.5 13.062,12.938Q13.5,13.375 13.5,14Q13.5,14.625 13.062,15.062Q12.625,15.5 12,15.5ZM8,5H16V4H8ZM16,7H8V7.4L10,10.4V20H14V10.4L16,7.4ZM12,12Z"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_flashlight_on.xml b/packages/SystemUI/res-keyguard/drawable/ic_flashlight_on.xml
new file mode 100644
index 000000000000..91b9ae56b145
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_flashlight_on.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#1f1f1f"
+ android:pathData="M6,5V2H18V5ZM12,15.5Q12.625,15.5 13.062,15.062Q13.5,14.625 13.5,14Q13.5,13.375 13.062,12.938Q12.625,12.5 12,12.5Q11.375,12.5 10.938,12.938Q10.5,13.375 10.5,14Q10.5,14.625 10.938,15.062Q11.375,15.5 12,15.5ZM8,22V11L6,8V7H18V8L16,11V22Z"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index b49afeef09f3..218c5cc9b7fe 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -35,6 +35,7 @@
android:visibility="invisible" />
<FrameLayout
android:id="@+id/lockscreen_clock_view_large"
+ android:layout_marginTop="@dimen/keyguard_large_clock_top_margin"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
diff --git a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
index 669f8fb642de..e5e17b7d1554 100644
--- a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
@@ -17,4 +17,7 @@
<resources>
<dimen name="widget_big_font_size">54dp</dimen>
+
+ <!-- Margin above the ambient indication container -->
+ <dimen name="ambient_indication_container_margin_top">10dp</dimen>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index 579824a2a734..57e5f8a49535 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -74,7 +74,7 @@
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Bewerking met pincode voor simkaart is mislukt."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Bewerking met pukcode voor simkaart is mislukt."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Invoermethode wijzigen"</string>
- <string name="airplane_mode" msgid="2528005343938497866">"Vliegtuigmodus"</string>
+ <string name="airplane_mode" msgid="2528005343938497866">"Vliegtuig­modus"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Patroon vereist nadat het apparaat opnieuw is opgestart"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Pincode vereist nadat het apparaat opnieuw is opgestart"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Wachtwoord vereist nadat het apparaat opnieuw is opgestart"</string>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 3861d983b309..c5ffdc0051da 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -41,6 +41,9 @@
<!-- Minimum bottom margin under the security view -->
<dimen name="keyguard_security_view_bottom_margin">60dp</dimen>
+ <!-- Margin above the ambient indication container -->
+ <dimen name="ambient_indication_container_margin_top">0dp</dimen>
+
<dimen name="keyguard_eca_top_margin">18dp</dimen>
<dimen name="keyguard_eca_bottom_margin">12dp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index a129fb650ba6..da485a99c29b 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -53,7 +53,7 @@
<string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string>
<!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that charging is temporarily limited. -->
- <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging is paused to protect battery</string>
+ <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging paused to protect battery</string>
<!-- On the keyguard screen, when pattern lock is disabled, only tell them to press menu to unlock. This is shown in small font at the bottom. -->
<string name="keyguard_instructions_when_pattern_disabled">Press Menu to unlock.</string>
diff --git a/packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml b/packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml
new file mode 100644
index 000000000000..9a022960d1e3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/screenrecord_options_spinner_popup_background.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/screenrecord_spinner_background_radius"/>
+ <solid android:color="?androidprv:attr/colorAccentSecondary" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screenshare_options_spinner_background.xml b/packages/SystemUI/res/drawable/screenshare_options_spinner_background.xml
new file mode 100644
index 000000000000..34e7d0afcd97
--- /dev/null
+++ b/packages/SystemUI/res/drawable/screenshare_options_spinner_background.xml
@@ -0,0 +1,35 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:paddingMode="stack">
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/screenrecord_spinner_background_radius" />
+ <stroke
+ android:width="1dp"
+ android:color="?androidprv:attr/textColorTertiary" />
+ <solid android:color="@android:color/transparent"/>
+ </shape>
+ </item>
+ <item
+ android:drawable="@drawable/ic_ksh_key_down"
+ android:gravity="end|center_vertical"
+ android:textColor="?androidprv:attr/textColorPrimary"
+ android:width="@dimen/screenrecord_spinner_arrow_size"
+ android:height="@dimen/screenrecord_spinner_arrow_size"
+ android:end="20dp" />
+</layer-list> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/udfps_enroll_checkmark.xml b/packages/SystemUI/res/drawable/udfps_enroll_checkmark.xml
index a3ed3d1f6b79..f8169d377f12 100644
--- a/packages/SystemUI/res/drawable/udfps_enroll_checkmark.xml
+++ b/packages/SystemUI/res/drawable/udfps_enroll_checkmark.xml
@@ -26,10 +26,10 @@
android:fillType="evenOdd"/>
<path
android:pathData="M27,0C12.088,0 0,12.088 0,27C0,41.912 12.088,54 27,54C41.912,54 54,41.912 54,27C54,12.088 41.912,0 27,0ZM27,3.962C39.703,3.962 50.037,14.297 50.037,27C50.037,39.703 39.703,50.038 27,50.038C14.297,50.038 3.963,39.703 3.963,27C3.963,14.297 14.297,3.962 27,3.962Z"
- android:fillColor="?attr/biometricsEnrollProgress"
+ android:fillColor="@color/udfps_enroll_progress"
android:fillType="evenOdd"/>
<path
android:pathData="M23.0899,38.8534L10.4199,26.1824L13.2479,23.3544L23.0899,33.1974L41.2389,15.0474L44.0679,17.8754L23.0899,38.8534Z"
- android:fillColor="?attr/biometricsEnrollProgress"
+ android:fillColor="@color/udfps_enroll_progress"
android:fillType="evenOdd"/>
</vector>
diff --git a/packages/SystemUI/res/layout/activity_rear_display_education.xml b/packages/SystemUI/res/layout/activity_rear_display_education.xml
new file mode 100644
index 000000000000..73d3f0236fff
--- /dev/null
+++ b/packages/SystemUI/res/layout/activity_rear_display_education.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center" >
+
+ <androidx.cardview.widget.CardView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:cardElevation="0dp"
+ app:cardCornerRadius="28dp"
+ app:cardBackgroundColor="@color/rear_display_overlay_animation_background_color">
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/rear_display_folded_animation"
+ android:layout_width="@dimen/rear_display_animation_width"
+ android:layout_height="@dimen/rear_display_animation_height"
+ android:layout_gravity="center"
+ android:contentDescription="@null"
+ android:scaleType="fitXY"
+ app:lottie_rawRes="@raw/rear_display_folded"
+ app:lottie_autoPlay="true"
+ app:lottie_repeatMode="reverse"/>
+ </androidx.cardview.widget.CardView>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/rear_display_fold_bottom_sheet_title"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ android:lineSpacingExtra="2sp"
+ android:paddingTop="@dimen/rear_display_title_top_padding"
+ android:paddingBottom="@dimen/rear_display_title_bottom_padding"
+ android:gravity="center_horizontal|center_vertical"
+ />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/rear_display_bottom_sheet_description"
+ android:textAppearance="@style/TextAppearance.Dialog.Body"
+ android:lineSpacingExtra="2sp"
+ android:translationY="-1.24sp"
+ android:gravity="center_horizontal|top"
+ />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml b/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml
new file mode 100644
index 000000000000..20b93d9bc40d
--- /dev/null
+++ b/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center" >
+
+ <androidx.cardview.widget.CardView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:cardElevation="0dp"
+ app:cardCornerRadius="28dp"
+ app:cardBackgroundColor="@color/rear_display_overlay_animation_background_color">
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/rear_display_folded_animation"
+ android:layout_width="@dimen/rear_display_animation_width"
+ android:layout_height="@dimen/rear_display_animation_height"
+ android:layout_gravity="center"
+ android:contentDescription="@null"
+ android:scaleType="fitXY"
+ app:lottie_rawRes="@raw/rear_display_turnaround"
+ app:lottie_autoPlay="true"
+ app:lottie_repeatMode="reverse"/>
+ </androidx.cardview.widget.CardView>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/rear_display_unfold_bottom_sheet_title"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ android:lineSpacingExtra="2sp"
+ android:paddingTop="@dimen/rear_display_title_top_padding"
+ android:paddingBottom="@dimen/rear_display_title_bottom_padding"
+ android:gravity="center_horizontal|center_vertical"
+ />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/rear_display_bottom_sheet_description"
+ android:textAppearance="@style/TextAppearance.Dialog.Body"
+ android:lineSpacingExtra="2sp"
+ android:translationY="-1.24sp"
+ android:gravity="center_horizontal|top"
+ />
+
+ <TextView
+ android:id="@+id/rear_display_warning_text_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/rear_display_bottom_sheet_warning"
+ android:textAppearance="@style/TextAppearance.Dialog.Body"
+ android:lineSpacingExtra="2sp"
+ android:gravity="center_horizontal|top"
+ />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
index 2d67d95ab17e..efcb6f3435b9 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -14,25 +14,32 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.shared.shadow.DoubleShadowTextClock
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/time_view"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:fontFamily="@*android:string/config_clockFontFamily"
- android:textColor="@android:color/white"
- android:format12Hour="@string/dream_time_complication_12_hr_time_format"
- android:format24Hour="@string/dream_time_complication_24_hr_time_format"
- android:fontFeatureSettings="pnum, lnum"
- android:letterSpacing="0.02"
- android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
- app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
- app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
- app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
- app:keyShadowAlpha="0.3"
- app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
- app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
- app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
- app:ambientShadowAlpha="0.3"
-/>
+ android:layout_height="wrap_content">
+
+ <com.android.systemui.shared.shadow.DoubleShadowTextClock
+ android:id="@+id/time_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="@*android:string/config_clockFontFamily"
+ android:textColor="@android:color/white"
+ android:format12Hour="@string/dream_time_complication_12_hr_time_format"
+ android:format24Hour="@string/dream_time_complication_24_hr_time_format"
+ android:fontFeatureSettings="pnum, lnum"
+ android:letterSpacing="0.02"
+ android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
+ android:translationY="@dimen/dream_overlay_complication_clock_time_translation_y"
+ app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
+ app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
+ app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
+ app:keyShadowAlpha="0.3"
+ app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
+ app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
+ app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
+ app:ambientShadowAlpha="0.3"
+ />
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
index 4f0a78e9c35d..de96e9765668 100644
--- a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
@@ -14,16 +14,21 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<ImageView
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/home_controls_chip"
- android:layout_height="@dimen/keyguard_affordance_fixed_height"
- android:layout_width="@dimen/keyguard_affordance_fixed_width"
- android:layout_gravity="bottom|start"
- android:scaleType="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/controls_icon"
- android:background="@drawable/keyguard_bottom_affordance_bg"
- android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset"
- android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
- android:contentDescription="@string/quick_controls_title" />
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingVertical="@dimen/dream_overlay_complication_home_controls_padding">
+
+ <ImageView
+ android:id="@+id/home_controls_chip"
+ android:layout_height="@dimen/keyguard_affordance_fixed_height"
+ android:layout_width="@dimen/keyguard_affordance_fixed_width"
+ android:layout_gravity="bottom|start"
+ android:scaleType="center"
+ android:tint="?android:attr/textColorPrimary"
+ android:src="@drawable/controls_icon"
+ android:background="@drawable/keyguard_bottom_affordance_bg"
+ android:contentDescription="@string/quick_controls_title" />
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index 79ba7ead1ec3..aa655e6b3564 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -41,7 +41,7 @@
android:layout_width="@dimen/qs_media_app_icon_size"
android:layout_height="@dimen/qs_media_app_icon_size"
android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginTop="@dimen/qs_media_rec_icon_top_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
diff --git a/packages/SystemUI/res/layout/screen_record_options.xml b/packages/SystemUI/res/layout/screen_record_options.xml
index d6c9e98d8b4d..3f0eea9004a6 100644
--- a/packages/SystemUI/res/layout/screen_record_options.xml
+++ b/packages/SystemUI/res/layout/screen_record_options.xml
@@ -16,7 +16,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/screenrecord_options_padding_bottom">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -72,7 +73,7 @@
android:gravity="center_vertical"
android:text="@string/screenrecord_taps_label"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
android:textColor="?android:attr/textColorPrimary"
android:contentDescription="@string/screenrecord_taps_label"/>
<Switch
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index ac46cdb1be24..bd719894e25f 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -16,7 +16,7 @@
<!-- Scrollview is necessary to fit everything in landscape layout -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/screen_share_permission_dialog"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -32,10 +32,11 @@
android:gravity="center_horizontal">
<ImageView
+ android:id="@+id/screen_share_dialog_icon"
android:layout_width="@dimen/screenrecord_logo_size"
android:layout_height="@dimen/screenrecord_logo_size"
- android:src="@drawable/ic_screenrecord"
- android:tint="@color/screenrecord_icon_color"
+ android:src="@drawable/ic_media_projection_permission"
+ android:tint="?androidprv:attr/colorAccentPrimary"
android:importantForAccessibility="no"/>
<TextView
android:id="@+id/screen_share_dialog_title"
@@ -43,34 +44,37 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:fontFamily="@*android:string/config_headlineFontFamily"
- android:layout_marginTop="22dp"
- android:layout_marginBottom="15dp"/>
+ android:layout_marginTop="@dimen/screenrecord_title_margin_top"
+ android:gravity="center"/>
<Spinner
android:id="@+id/screen_share_mode_spinner"
- android:layout_width="320dp"
- android:layout_height="72dp"
- android:layout_marginTop="24dp"
- android:layout_marginBottom="24dp" />
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/screenrecord_spinner_height"
+ android:layout_marginTop="@dimen/screenrecord_spinner_margin"
+ android:layout_marginBottom="@dimen/screenrecord_spinner_margin"
+ android:gravity="center_vertical"
+ android:background="@drawable/screenshare_options_spinner_background"
+ android:popupBackground="@drawable/screenrecord_options_spinner_popup_background"/>
<ViewStub
android:id="@+id/options_stub"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/text_warning"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/screenrecord_description"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorSecondary"
android:gravity="start"
- android:layout_marginBottom="20dp"/>
+ android:lineHeight="@dimen/screenrecord_warning_line_height"/>
<!-- Buttons -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:layout_marginTop="36dp">
+ android:layout_marginTop="@dimen/screenrecord_buttons_margin_top">
<TextView
android:id="@+id/button_cancel"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml b/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
new file mode 100644
index 000000000000..66c215599949
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@android:id/text1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?androidprv:attr/textColorOnAccent"
+ android:singleLine="true"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/screenrecord_spinner_height"
+ android:gravity="center_vertical"
+ android:ellipsize="marquee"
+ android:paddingStart="@dimen/screenrecord_spinner_text_padding_start"
+ android:paddingEnd="@dimen/screenrecord_spinner_text_padding_end"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_share_dialog_spinner_text.xml b/packages/SystemUI/res/layout/screen_share_dialog_spinner_text.xml
new file mode 100644
index 000000000000..4cc4cbae3569
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_share_dialog_spinner_text.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textColor="?android:textColorPrimary"
+ android:singleLine="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:ellipsize="marquee"
+ android:paddingStart="@dimen/screenrecord_spinner_text_padding_start"
+ android:paddingEnd="@dimen/screenrecord_spinner_text_padding_end"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index b63ee5095554..3b71dc32b256 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -55,6 +55,7 @@
android:id="@+id/status_bar_start_side_container"
android:layout_height="match_parent"
android:layout_width="0dp"
+ android:clipChildren="false"
android:layout_weight="1">
<!-- Container that is wrapped around the views on the start half of the status bar.
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index a2abdb211602..856ba92b6d41 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -21,12 +21,29 @@
android:layout_height="wrap_content"
android:visibility="gone"
>
- <TextView
- android:id="@+id/no_notifications"
+ <LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="64dp"
- android:textAppearance="?android:attr/textAppearanceButton"
+ android:layout_gravity="center"
android:gravity="center"
- android:text="@string/empty_shade_text"/>
+ >
+ <TextView
+ android:id="@+id/no_notifications"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="64dp"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceButton"
+ android:text="@string/empty_shade_text"/>
+ <TextView
+ android:id="@+id/no_notifications_footer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center"
+ android:drawablePadding="8dp"
+ android:visibility="gone"
+ android:textAppearance="?android:attr/textAppearanceButton"
+ android:text="@string/unlock_to_see_notif_text"/>
+ </LinearLayout>
</com.android.systemui.statusbar.EmptyShadeView>
diff --git a/packages/SystemUI/res/raw/rear_display_folded.json b/packages/SystemUI/res/raw/rear_display_folded.json
new file mode 100644
index 000000000000..5140f4197ef3
--- /dev/null
+++ b/packages/SystemUI/res/raw/rear_display_folded.json
@@ -0,0 +1 @@
+{"v":"5.8.1","fr":60,"ip":0,"op":181,"w":412,"h":300,"nm":"Close to Open - Generic Version V01","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"SHUTTER_ANIMATION 2","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":270.564,"ix":3},"y":{"a":0,"k":239.916,"ix":4}},"a":{"a":0,"k":[460.228,450.736,0],"ix":1,"l":2},"s":{"a":0,"k":[138.889,138.889,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.528,0],[0,-2.528],[2.528,0],[0,2.528]],"o":[[2.528,0],[0,2.528],[-2.528,0],[0,-2.528]],"v":[[0,-4.5],[4.5,0],[0,4.5],[-4.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 599","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[460.228,450.736],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[140,140],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"SHUTTER_01","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.337,0],[0,-5.337],[5.337,0],[0,5.337]],"o":[[5.337,0],[0,5.337],[-5.337,0],[0,-5.337]],"v":[[0,-9.5],[9.5,0],[0,9.5],[-9.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 598","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[460.228,450.736],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"SHUTTER_02","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":196,"st":-90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"CAMERA_CASING","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.125,149.875,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[20,20,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":77.334,"s":[{"i":[[0.121,-8.986],[-0.129,-6.81],[0,0],[0.079,-13.74],[-0.055,-2.977],[0,0],[-0.084,4.25],[0,5.834],[0,20.89],[-0.063,-6.336]],"o":[[-0.029,2.163],[0.129,6.811],[0,0],[-0.079,13.74],[0.123,6.697],[0,0],[0.045,-2.268],[0,-6.193],[0,-0.486],[0.247,24.895]],"v":[[-248.474,-424.663],[-248.252,-408.338],[-247.986,-395.258],[-248.158,-368.523],[-248.248,-337.322],[-243.792,-329.875],[-243.834,-338.44],[-243.875,-350.098],[-244.25,-495.639],[-248.374,-488.414]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":78,"s":[{"i":[[0.121,-8.986],[-0.035,-6.791],[0,0],[0.257,-54.598],[-0.08,-2.976],[0,0],[-0.084,4.25],[0,5.834],[0,20.89],[0.187,-5.461]],"o":[[-0.029,2.163],[0.035,6.791],[0,0],[-0.257,54.598],[0.121,4.51],[0,0],[0.045,-2.268],[0,-6.193],[0,-0.486],[-0.853,24.882]],"v":[[-246.724,-424.581],[-246.689,-408.295],[-246.611,-395.254],[-247.145,-286.802],[-247.56,-173.885],[-239.293,-170.875],[-239.335,-179.44],[-239.251,-191.098],[-239,-495.889],[-246.437,-493.601]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":79,"s":[{"i":[[0.714,-8.993],[0.023,-5.892],[0,0],[0.218,-62.519],[-0.04,-3.021],[0,0],[-2.041,1.97],[0,5.92],[0,21.197],[1.374,-7.356]],"o":[[-0.174,2.184],[-0.023,5.892],[0,0],[-0.218,62.519],[0.061,5.942],[0,0],[0.335,-2.259],[0,-6.284],[0,-0.493],[-3.801,24.629]],"v":[[-249.702,-423.916],[-249.966,-409.403],[-249.968,-398.166],[-250.414,-273.882],[-250.801,-145.067],[-238.043,-139.562],[-231.043,-142.595],[-229.959,-182.924],[-230,-497.57],[-247.031,-494.301]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80,"s":[{"i":[[1.306,-9],[0.081,-4.992],[0,0],[0.2,-61.534],[0,-3.065],[0,0],[-6.497,19.5],[0,6.005],[0,21.503],[2.562,-9.251]],"o":[[-0.32,2.204],[-0.081,4.992],[0,0],[-0.2,61.535],[0,7.375],[0,0],[0.738,-2.215],[0,-6.375],[0,-0.5],[-6.75,24.375]],"v":[[-252.681,-423.25],[-253.243,-410.511],[-253.325,-401.078],[-253.726,-278.775],[-254.126,-151.875],[-236.876,-143.875],[-224.128,-171.375],[-221.128,-194.75],[-221,-499.25],[-241.375,-496.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81,"s":[{"i":[[0,0],[2.977,-4.617],[0.592,-15.582],[0.085,-28.87],[-7.478,-2.938],[0,0],[-7.716,4.062],[0.774,19.04],[0.351,22.315],[4.742,-7.015]],"o":[[0,0],[-5.02,7.785],[-0.054,1.42],[-0.036,12.426],[7.58,-2.313],[0,0],[6.882,-3.623],[-0.821,-20.201],[-4.272,-4.668],[-5.774,8.542]],"v":[[-281.915,-495.91],[-289.56,-482.16],[-299.005,-452.549],[-298.089,-174.301],[-285.022,-158.312],[-272.428,-163.108],[-254.243,-172.187],[-246.548,-198.415],[-247.911,-507.832],[-271.41,-509.792]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":83,"s":[{"i":[[0,0],[5.84,-4.09],[0.943,-15.582],[-0.504,-28.868],[-9.184,-4.813],[0,0],[-12.283,4.062],[0.017,19.08],[0.558,22.315],[10.444,-5.833]],"o":[[0,0],[-9.041,6.332],[-0.086,1.42],[0.293,16.801],[12.066,-2.313],[0,0],[10.956,-3.623],[-0.018,-20.335],[-6.483,-6.543],[-12.057,6.734]],"v":[[-335.521,-505.91],[-351.465,-495.91],[-370.479,-463.799],[-369.043,-170.551],[-352.691,-149.562],[-328.662,-154.983],[-300.058,-162.812],[-286.232,-189.04],[-289.767,-509.707],[-316.694,-517.917]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86,"s":[{"i":[[0,0],[10.236,-2.84],[1.247,-15.582],[0,-28.874],[-6.428,0.187],[0,0],[-16.249,4.062],[0,17.165],[0.739,22.315],[11.672,-3.958]],"o":[[0,0],[-13.709,3.803],[-0.114,1.42],[0,17.426],[15.447,-2.313],[0,0],[14.494,-3.623],[0,-20.252],[-3.545,-8.418],[-17.281,5.861]],"v":[[-411.704,-507.785],[-438.297,-499.035],[-463.451,-466.924],[-464.325,-183.676],[-446.9,-161.437],[-413.235,-166.858],[-377.21,-173.437],[-354.78,-199.04],[-356.455,-507.207],[-381.047,-517.292]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88,"s":[{"i":[[0,0],[11.399,-2.84],[1.389,-15.582],[-0.154,-28.874],[-10.466,-4.813],[0,0],[-18.094,4.062],[0.311,21.54],[0.823,22.315],[12.693,-2.083]],"o":[[0,0],[-15.266,3.803],[-0.127,1.42],[0.136,25.551],[16.344,-2.733],[0,0],[16.14,-3.623],[-0.293,-20.255],[-6.423,-10.918],[-20.046,3.29]],"v":[[-453.081,-516.535],[-484.085,-509.035],[-512.096,-476.924],[-511.208,-201.176],[-491.095,-170.812],[-450.071,-176.858],[-415.594,-182.187],[-391.248,-214.04],[-394.202,-507.207],[-419.568,-523.542]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":91,"s":[{"i":[[0,0],[11.98,-2.84],[1.46,-15.582],[-0.514,-28.87],[-22.839,0.574],[0,0],[-19.017,4.062],[-1.512,28.415],[0.865,22.315],[18.516,-1.458]],"o":[[0,0],[-16.044,3.803],[-0.133,1.42],[0.514,28.87],[17.426,-0.438],[0,0],[16.962,-3.623],[1.076,-20.231],[-2.031,-12.793],[-21.281,1.676]],"v":[[-517.52,-526.535],[-550.105,-520.285],[-579.544,-488.174],[-580.475,-228.676],[-552.254,-182.687],[-515.05,-186.858],[-479.472,-193.437],[-453.852,-232.165],[-454.219,-505.957],[-481.641,-531.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96,"s":[{"i":[[0,0],[9.594,-0.965],[1.674,-15.582],[-0.589,-28.87],[-26.176,0.574],[0,0],[-26.125,4.687],[-0.648,20.252],[0.991,22.315],[23.63,-1.458]],"o":[[0,0],[-18.792,1.889],[-0.153,1.42],[0.589,28.87],[19.971,-0.438],[0,0],[19.531,-3.504],[0.648,-20.252],[-5.417,-15.918],[-24.412,1.507]],"v":[[-566.624,-545.91],[-605.219,-542.785],[-638.958,-510.674],[-640.256,-243.676],[-605.048,-197.687],[-564.208,-201.233],[-524.5,-205.937],[-494.422,-252.79],[-495.208,-520.332],[-526.755,-548.542]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101,"s":[{"i":[[0,0],[10.756,0.606],[1.787,-15.582],[-0.629,-28.87],[-29.482,0.187],[0,0],[-26.75,1.562],[-0.693,20.252],[1.058,22.315],[29.006,2.879]],"o":[[0,0],[-20.135,-1.134],[-0.163,1.42],[0.629,28.87],[22.579,-0.143],[0,0],[21.139,-1.234],[0.693,-20.252],[-0.991,-20.899],[-22.881,-2.271]],"v":[[-590.882,-561.535],[-630.768,-559.035],[-666.802,-526.924],[-667.496,-244.926],[-626.768,-198.312],[-587.72,-199.358],[-544.491,-201.562],[-512.976,-245.915],[-513.29,-522.832],[-546.76,-561.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107,"s":[{"i":[[0,0],[10.983,0.606],[1.825,-15.582],[-0.642,-28.87],[-28.524,-1.302],[0,0],[-22.316,2.187],[-0.707,20.252],[1.081,22.315],[29.62,2.879]],"o":[[0,0],[-20.561,-1.134],[-0.166,1.42],[0.642,28.87],[23.032,1.051],[0,0],[21.519,-2.108],[0.707,-20.252],[-1.012,-20.899],[-23.365,-2.271]],"v":[[-603.82,-572.785],[-647.049,-572.785],[-683.846,-540.674],[-684.555,-258.676],[-646.156,-212.687],[-598.091,-212.483],[-558.309,-214.687],[-525.505,-260.29],[-527.086,-532.832],[-561.264,-571.667]],"c":true}]},{"t":115,"s":[{"i":[[0,0],[10.983,0.606],[1.825,-15.582],[-0.642,-28.87],[-28.524,-1.302],[0,0],[-21.06,0.551],[-0.707,20.252],[1.081,22.315],[29.62,2.879]],"o":[[0,0],[-20.561,-1.134],[-0.166,1.42],[0.642,28.87],[23.032,1.051],[0,0],[21.614,-0.566],[0.707,-20.252],[-1.012,-20.899],[-23.365,-2.271]],"v":[[-605.07,-581.535],[-648.299,-581.535],[-685.096,-549.424],[-685.805,-263.676],[-647.406,-217.687],[-601.841,-216.858],[-560.092,-217.187],[-527.348,-259.04],[-528.336,-541.582],[-562.514,-580.417]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294118524,0.250980407,0.262745112181,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":78,"op":241,"st":60,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"FRONT_LENS","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[269.043,44.23,0],"ix":2,"l":2},"a":{"a":0,"k":[387.188,-561.875,0],"ix":1,"l":2},"s":{"a":0,"k":[18.519,18.519,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-10.77,0],[0,-10.77],[10.77,0],[0,10.77]],"o":[[10.77,0],[0,10.77],[-10.77,0],[0,-10.77]],"v":[[1.484,-19.734],[20.984,-0.234],[1.484,19.266],[-18.016,-0.234]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[385.5,-561.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":241,"st":60,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"FRONT_SCREEN 2","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[268.577,150.446,0],"ix":2,"l":2},"a":{"a":0,"k":[-2.142,1.067,0],"ix":1,"l":2},"s":{"a":0,"k":[18.894,18.894,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[635,1210],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":50,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.641,0.871],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":241,"st":60,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Figure","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[207.459,144.79,0],"to":[0.312,0,0],"ti":[-1.146,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.334,"s":[209.334,144.79,0],"to":[1.146,0,0],"ti":[-2.125,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66.666,"s":[214.334,144.79,0],"to":[2.125,0,0],"ti":[-2.812,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70,"s":[222.084,144.79,0],"to":[2.812,0,0],"ti":[-4.396,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":73.334,"s":[231.209,144.79,0],"to":[4.396,0,0],"ti":[-5.104,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80,"s":[248.459,144.79,0],"to":[5.104,0,0],"ti":[-3.812,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[261.834,144.79,0],"to":[3.812,0,0],"ti":[-2.667,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[271.334,144.79,0],"to":[2.667,0,0],"ti":[-1.812,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100,"s":[277.834,144.79,0],"to":[1.812,0,0],"ti":[-1.083,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":106.666,"s":[282.209,144.79,0],"to":[1.083,0,0],"ti":[-0.458,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":113.334,"s":[284.334,144.79,0],"to":[0.458,0,0],"ti":[-0.104,0,0]},{"t":120,"s":[284.959,144.79,0]}],"ix":2,"l":2},"a":{"a":0,"k":[270.209,145.54,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.55,0.55,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.001]},"t":60,"s":[108,108,100]},{"t":120,"s":[106.5,106.5,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.939,-1.387],[6.061,-1.3],[0.907,1.308]],"o":[[1.066,0.711],[2.423,1.734],[-4.85,1.04],[0,0]],"v":[[-0.582,-6.237],[5.035,-2.37],[2.01,5.979],[-6.77,3.354]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.596078455448,0.321568638086,0.239215686917,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.12,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[272.026,152.086],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.926,7.277],[-8.848,0],[-3.783,2.669],[18.309,-0.059],[0.81,-15.17],[-4.916,0]],"o":[[3.926,7.277],[4.982,0],[-0.845,-15.213],[-18.267,0.059],[3.755,2.604],[8.848,0]],"v":[[-0.106,3.616],[20.359,15.839],[33.729,11.589],[-0.12,-15.891],[-33.793,11.7],[-20.571,15.839]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,9.805],[9.805,0],[0,-9.805],[-9.805,0]],"o":[[0,-9.805],[-9.805,0],[0,9.805],[9.805,0]],"v":[[-6.59,-22.526],[-24.345,-40.281],[-42.099,-22.526],[-24.345,-4.772]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.337254911661,0.23137255013,0.129411771894,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[270.47,108.276],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Hair","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-7.446,13.962],[0,0],[0,0],[-0.313,2.62],[0.015,4.351],[0,0],[-18.83,0.06],[-0.051,-15.806],[0,0],[-0.49,-2.669],[-1.873,-4.352],[-0.89,-3.988],[0,-4.307],[8.014,-5.508],[12.335,-0.04],[8.035,5.309],[0,7.994],[0,0]],"o":[[0,0],[0,0],[1.222,-4.479],[0.309,-2.598],[0,0],[-0.051,-15.806],[18.83,-0.06],[0,0],[0.013,4.276],[0.495,2.702],[3.604,8.387],[0.884,3.962],[0,7.459],[-7.997,5.495],[-12.35,0.04],[-8.04,-5.313],[0,0],[0,-6.072]],"v":[[-36.861,-4.598],[-36.804,-4.703],[-36.772,-4.819],[-34.61,-14.535],[-34.324,-24.06],[-34.324,-24.128],[-0.614,-53.129],[33.278,-24.345],[33.278,-24.252],[33.789,-14.641],[37.134,-4.922],[43.41,12.489],[44.307,23.718],[31.53,43.997],[0.092,53.129],[-31.449,44.589],[-44.307,24.009],[-44.307,23.977]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[4.598,1.656],[-2.64,7.412],[-4.598,-1.656],[2.64,-7.412]],"o":[[-4.6,-1.657],[2.64,-7.412],[4.6,1.657],[-2.64,7.412]],"v":[[30.635,-0.09],[27.088,-16.51],[40.195,-26.931],[43.742,-10.511]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[4.598,-1.656],[2.64,7.412],[-4.598,1.656],[-2.64,-7.412]],"o":[[-4.6,1.657],[-2.64,-7.412],[4.6,-1.657],[2.64,7.412]],"v":[[-32.206,-0.025],[-45.315,-10.446],[-41.768,-26.866],[-28.659,-16.445]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431391716,0.403921574354,0.305882364511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[270.996,145.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Head","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[32.75,0],[5.125,-6.5],[0,0],[0,0],[0.125,7.75]],"o":[[-36.75,0],[-0.25,11],[0,0],[0,0],[-14.25,-12.5]],"v":[[269.5,203.875],[202.375,231.25],[202.125,265.125],[335.375,265.125],[335.375,229.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.105865478516,0.450958251953,0.901947021484,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Body","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,-5.243]],"o":[[0,0],[0,0],[0,0],[5.243,0],[0,0]],"v":[[335.369,264.28],[202.496,264.441],[202.496,35.601],[325.98,35.551],[335.473,45.044]],"c":true},"ix":2},"nm":"Mask","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.815673828125,0.88232421875,0.980377197266,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Background","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":660,"st":60,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"FOLDABLE_BODY","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.125,149.875,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[20,20,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,-16.75],[0,0],[0.131,-20.875],[0,0],[0,0],[-1.054,43.75],[0,0],[55.442,-0.625],[0,0],[0,0]],"o":[[0,0],[0.07,32.5],[0,0],[0,0],[50.138,1],[0,0],[0.75,-40.75],[0,0],[0,0],[-0.317,13.125]],"v":[[-380.75,-475.25],[-381.945,472.625],[-381.756,573.5],[-381.766,647.625],[278.237,648.25],[356.555,572.75],[356.375,-566.125],[277.933,-644.25],[-380.936,-644.5],[-380.183,-550.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":63.334,"s":[{"i":[[0,-16.75],[0,0],[0.131,-20.875],[0,0],[0,0],[-1.054,43.75],[0,0],[55.442,-0.625],[0,0],[0,0]],"o":[[0,0],[0.07,32.5],[0,0],[0,0],[50.138,1],[0,0],[0.75,-40.75],[0,0],[0,0],[-0.317,13.125]],"v":[[-373.625,-475],[-373.633,472.562],[-373.444,573.438],[-373.454,647.562],[287.112,648.375],[364.93,573.625],[364.75,-566.938],[285.995,-645],[-373.811,-644.25],[-373.058,-550.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66.666,"s":[{"i":[[0,-16.75],[0,0],[0.131,-20.875],[0,0],[0,0],[-1.054,43.75],[0,0],[55.442,-0.625],[0,0],[0,0]],"o":[[0,0],[0.07,32.5],[0,0],[0,0],[50.138,1],[0,0],[0.75,-40.75],[0,0],[0,0],[-0.317,13.125]],"v":[[-350,-475.25],[-349.82,472.5],[-349.631,573.375],[-349.641,647.5],[311.487,647],[388.805,573],[389.125,-566.75],[310.058,-644.75],[-350.186,-644.5],[-349.433,-550.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70,"s":[{"i":[[0,-16.75],[0,0],[0.131,-20.875],[0,0],[0,0],[-1.054,43.75],[0,0],[54.942,-0.625],[0,0],[0,0]],"o":[[0,0],[0.07,32.5],[0,0],[0,0],[50.138,1],[0,0],[0.187,-41.375],[0,0],[0,0],[-0.317,13.125]],"v":[[-315.875,-476.875],[-315.445,471],[-315.256,571.875],[-315.266,646],[348.612,647.562],[427.555,572.75],[427.438,-565.75],[349.058,-643.688],[-316.077,-643.625],[-315.308,-552.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":73.334,"s":[{"i":[[0,-16.75],[0,0],[0.131,-20.875],[0,0],[0,0],[-1.054,43.75],[0,0],[54.442,-0.625],[0,0],[0,0]],"o":[[0,0],[0.07,32.5],[0,0],[0,0],[50.138,1],[0,0],[0.75,-40.75],[0,0],[0,0],[-0.317,13.125]],"v":[[-278.75,-478],[-278.57,469.75],[-278.381,570.625],[-278.391,644.75],[395.237,645.125],[471.554,571],[471.25,-564],[393.808,-642.125],[-278.968,-642.25],[-278.183,-553.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[0,-16.75],[0,0],[-0.119,-21.25],[0,0],[0,0],[-1.054,42.938],[0,0],[54.442,-0.625],[0,0],[0,0]],"o":[[0,0],[0.32,32.625],[0,0],[0,0],[50.075,0.562],[0,0],[0.75,-40.75],[0,0],[0,0],[0.183,12.75]],"v":[[-248.25,-479],[-248.82,468.5],[-248.381,550.25],[-248.516,643.75],[440.3,644.25],[515.804,570.375],[516,-564.375],[438.058,-641],[-248.468,-640.125],[-248.308,-541.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":79.334,"s":[{"i":[[0,-16.75],[0,0],[0.081,-18.8],[0,0],[0,0],[-1.054,42.287],[0,0],[54.442,-0.625],[0,0],[0,0]],"o":[[0,0],[0.52,32.725],[0,0],[0,0],[50.025,0.212],[0,0],[0.75,-40.75],[0,0],[0,0],[-1.317,12.05]],"v":[[-223.65,-479.7],[-224.12,467.9],[-224.081,534.35],[-224.116,642.5],[471.15,643.15],[548.004,570.275],[548.2,-564.675],[468.658,-640.1],[-224.668,-638.65],[-224.208,-523.1]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80,"s":[{"i":[[0,-16.75],[0,0],[-2.744,-18],[0,0],[0,0],[-1.054,42.125],[0,0],[54.442,-0.625],[0,0],[0,0]],"o":[[0,0],[0.57,32.75],[0,0],[0,0],[50.013,0.125],[0,0],[0.75,-40.75],[0,0],[0,0],[-1.692,11.875]],"v":[[-233.75,-479.75],[-233.57,467.5],[-229.881,530.125],[-213.641,642.5],[478.862,642.875],[556.054,570.25],[556.25,-564.75],[476.308,-639.875],[-212.468,-638.5],[-230.683,-518.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80.666,"s":[{"i":[[0.133,-15.433],[0,0],[-4.881,-15.762],[0,0],[0,0],[-1.067,41.975],[0,0],[54.08,-0.675],[0,0],[0,0]],"o":[[0,0],[0.458,32.95],[0,0],[0,0],[49.838,0.162],[0,0],[0.75,-40.625],[0,0],[0,0],[-6.455,9.608]],"v":[[-267.883,-471.067],[-267.329,462.242],[-262.735,514.346],[-201.341,642],[486.187,642.638],[563.142,570.212],[563.35,-564.412],[483.87,-639.625],[-201.81,-638.275],[-260.045,-517.358]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":82,"s":[{"i":[[0.4,-12.8],[0,0],[-9.156,-11.287],[0,0],[0,0],[-1.092,41.675],[0,0],[53.355,-0.775],[0,0],[0,0]],"o":[[0,0],[0.233,33.35],[0,0],[0,0],[49.488,0.238],[0,0],[0.75,-40.375],[0,0],[0,0],[-10.48,6.825]],"v":[[-331.65,-460.45],[-331.47,451.725],[-319.444,518.037],[-181.241,641.75],[500.837,642.162],[577.317,570.138],[577.55,-563.737],[498.995,-639.125],[-180.493,-637.825],[-318.77,-515.325]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":83.334,"s":[{"i":[[0,-16.75],[0,0],[-13.431,-6.812],[0,0],[0,0],[-1.117,41.375],[0,0],[52.63,-0.875],[0,0],[0,0]],"o":[[0,0],[0.008,33.75],[0,0],[0,0],[49.138,0.312],[0,0],[0.75,-40.125],[0,0],[0,0],[-18.005,8.375]],"v":[[-387.75,-461.75],[-388.82,454.125],[-369.569,523.312],[-159.641,641.25],[515.487,641.688],[591.492,570.062],[591.75,-563.062],[514.12,-638.625],[-160.843,-637.375],[-367.495,-521.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.666,"s":[{"i":[[0,-16.75],[0,0],[-20.006,-7.188],[0,0],[0,0],[-1.142,41.075],[0,0],[49.955,-0.925],[0,0],[0,0]],"o":[[0,0],[-0.217,34.15],[0,0],[0,0],[48.788,0.387],[0,0],[0.75,-39.875],[0,0],[0,0],[-24.98,7.975]],"v":[[-439.2,-468.15],[-438.72,458.425],[-413.244,530.237],[-141.041,640.35],[527.537,641.212],[603.067,569.987],[603.15,-564.787],[526.445,-638.125],[-143.993,-637.125],[-411.07,-528.125]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[0,-16.75],[0,0],[-29.869,-7.75],[0,0],[0,0],[-1.179,40.625],[0,0],[45.942,-1],[0,0],[0,0]],"o":[[0,0],[-0.555,34.75],[0,0],[0,0],[48.263,0.5],[0,0],[0.75,-39.5],[0,0],[0,0],[-35.442,7.375]],"v":[[-503.25,-477.75],[-503.57,464.25],[-468.756,540],[-113.141,639],[545.612,640.5],[620.429,569.875],[620.25,-567.375],[544.933,-637.375],[-118.718,-636.75],[-463.308,-537.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0.062,-18.625],[0,0],[-31.869,-6.438],[0,0],[0,0],[-1.179,40.438],[0,0],[44.63,-1],[0,0],[0,0]],"o":[[0,0],[-0.43,34.75],[0,0],[0,0],[47.638,0.625],[0,0],[0.5,-39.062],[0,0],[0,0],[-38.817,5.938]],"v":[[-584.938,-494.688],[-585.445,486.188],[-542.506,557.375],[-88.141,639],[570.612,639.75],[646.054,568.625],[646.188,-566.875],[570.495,-636.625],[-92.093,-636],[-537.183,-555.188]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[0.125,-20.5],[0,0],[-33.869,-5.125],[0,0],[0,0],[-1.179,40.25],[0,0],[43.317,-1],[0,0],[0,0]],"o":[[0,0],[-0.305,34.75],[0,0],[0,0],[47.013,0.75],[0,0],[0.25,-38.625],[0,0],[0,0],[-42.192,4.5]],"v":[[-640.125,-510.625],[-640.32,507.125],[-589.256,573.75],[-63.141,639],[591.112,639.25],[667.179,567.625],[666.625,-567.5],[593.058,-635.5],[-65.468,-635.25],[-584.558,-571.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96.666,"s":[{"i":[[0.062,-30.685],[0,0],[-36.306,-4.562],[0,0],[0,0],[-0.59,40.56],[0,0],[43.317,-1],[0,0],[0,0]],"o":[[0,0],[-0.43,36.25],[0,0],[0,0],[45.45,0.25],[0,0],[0.25,-38.625],[0,0],[0,0],[-41.442,3.938]],"v":[[-676.125,-519.625],[-676.008,522.062],[-617.819,589.812],[-46.391,638.5],[609.862,638.75],[684.617,567.25],[684.375,-566.125],[610.808,-634.75],[-47.593,-634.5],[-616.558,-587.188]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100,"s":[{"i":[[0,-40.869],[0,0],[-38.744,-4],[0,0],[0,0],[0,40.869],[0,0],[43.317,-1],[0,0],[0,0]],"o":[[0,0],[-0.555,37.75],[0,0],[0,0],[43.888,-0.25],[0,0],[0.25,-38.625],[0,0],[0,0],[-40.692,3.375]],"v":[[-697.625,-528.125],[-696.945,535.5],[-634.381,602.875],[-29.641,638],[625.612,638.25],[699.054,566.875],[699.125,-564.75],[625.558,-634],[-29.718,-633.75],[-633.558,-599.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":106.666,"s":[{"i":[[0,-40.869],[0,0],[-42.994,-3.25],[0,0],[0,0],[0,40.869],[0,0],[43.317,-1],[0,0],[0,0]],"o":[[0,0],[-0.93,42.625],[0,0],[0,0],[43.888,-0.25],[0,0],[0.25,-38.625],[0,0],[0,0],[-41.067,2.125]],"v":[[-715.875,-547.625],[-715.945,550.5],[-647.881,622.875],[-8.891,637.25],[646.487,637.75],[719.679,565.375],[718.875,-563.25],[646.183,-633.75],[-9.218,-633.875],[-649.058,-619.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":113.334,"s":[{"i":[[0,-40.869],[0,0],[-44.369,-2],[0,0],[0,0],[0,40.869],[0,0],[43.317,-1],[0,0],[0,0]],"o":[[0,0],[-0.305,42.5],[0,0],[0,0],[43.888,-0.25],[0,0],[0.25,-38.625],[0,0],[0,0],[-40.423,0]],"v":[[-720.125,-562.375],[-720.82,561.25],[-649.131,634],[3.109,637.25],[658.987,636.625],[729.679,566.125],[729.625,-564.75],[657.308,-633.375],[2.907,-633.625],[-649.808,-630.5]],"c":true}]},{"t":120,"s":[{"i":[[0,-40.869],[0,0],[-46.994,-1.25],[0,0],[0,0],[0,40.869],[0,0],[43.317,-1],[0,0],[0,0]],"o":[[0,0],[-0.305,42.5],[0,0],[0,0],[43.888,-0.25],[0,0],[0.75,-40],[0,0],[0,0],[-40.423,0]],"v":[[-721.75,-564.375],[-721.57,561.5],[-649.631,637.75],[-3.641,637.25],[659.862,637],[733.179,565.625],[732.75,-564.875],[659.058,-634.25],[2.032,-633.75],[-648.683,-634.375]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":18,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":241,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"BUTTON","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[71.498,114.825,0],"ix":2,"l":2},"a":{"a":0,"k":[-698.509,46.873,0],"ix":1,"l":2},"s":{"a":0,"k":[40,18,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-0.549,1.538],[0,0]],"o":[[0,0],[-0.554,-3.125],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[27.005,73.269],[26.312,72.137],[26,64.269],[25.741,-76.022],[26.183,-82.526],[27.125,-83.046]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":98,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-0.987,1.288],[0,0]],"o":[[0,0],[-0.741,-2.125],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[10.88,76.769],[7.625,75.627],[6.812,68.26],[6.553,-78.594],[7.808,-85.536],[11.375,-86.11]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-1.424,1.038],[0,0]],"o":[[0,0],[-0.929,-1.125],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-1.995,80.394],[-4.313,79.127],[-5.625,72.26],[-5.884,-81.156],[-3.817,-88.536],[-1.625,-89.046]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-1.581,0.831],[0,0]],"o":[[0,0],[-1.154,-1.025],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-4.92,81.663],[-10.263,80.552],[-11.8,73.81],[-11.934,-81.831],[-9.692,-89.236],[-4.55,-89.626]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":103.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-1.817,0.519],[0,0]],"o":[[0,0],[-1.491,-0.875],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-10.917,83.519],[-16.922,82.69],[-18.797,76.135],[-18.744,-82.844],[-16.239,-90.286],[-10.547,-90.546]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.052,0.208],[0,0]],"o":[[0,0],[-1.829,-0.725],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-16.289,85.394],[-21.456,84.827],[-23.669,78.46],[-23.428,-83.856],[-20.661,-91.336],[-15.919,-91.446]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":106.666,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.054,-0.625],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-20.027,86.635],[-23.438,86.252],[-25.875,80.01],[-25.509,-84.531],[-22.567,-92.036],[-19.656,-92.055]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.069,-0.563],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-20.345,86.897],[-24.538,86.552],[-26.994,80.535],[-26.609,-85.031],[-23.661,-92.186],[-19.969,-92.205]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":109.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.116,-0.375],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-21.718,87.685],[-26.535,87.452],[-29.048,82.11],[-28.605,-86.531],[-25.64,-92.636],[-21.323,-92.655]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.163,-0.188],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-22.982,88.472],[-27.674,88.352],[-30.243,83.685],[-29.742,-88.031],[-26.76,-93.086],[-22.568,-93.105]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":112.666,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.194,-0.063],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-24.293,88.997],[-28.485,88.952],[-31.091,84.735],[-30.553,-89.031],[-27.558,-93.386],[-23.866,-93.405]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":113.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-24.558,89.26],[-28.188,89.252],[-30.812,85.26],[-30.255,-89.531],[-27.255,-93.536],[-24.125,-93.555]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":114.666,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-24.765,89.36],[-28.394,89.352],[-31.219,85.359],[-30.748,-89.532],[-27.548,-93.536],[-24.419,-93.555]],"c":true}]},{"t":120,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-25.373,90.005],[-27.502,89.998],[-31.127,86],[-31.005,-89.291],[-27.005,-93.291],[-25.375,-93.31]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-698.509,46.873],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":96,"op":241,"st":60,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"BUTTON 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[71.498,179.325,0],"ix":2,"l":2},"a":{"a":0,"k":[-698.509,46.873,0],"ix":1,"l":2},"s":{"a":0,"k":[40,25,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-0.549,1.538],[0,0]],"o":[[0,0],[-0.554,-3.125],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[27.005,73.269],[26.312,72.137],[26,64.269],[25.741,-76.022],[26.183,-82.526],[27.125,-83.046]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":98,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-0.987,1.288],[0,0]],"o":[[0,0],[-0.741,-2.125],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[10.88,76.769],[7.625,75.627],[6.812,68.26],[6.553,-78.594],[7.808,-85.536],[11.375,-86.11]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-1.424,1.038],[0,0]],"o":[[0,0],[-0.929,-1.125],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-1.995,80.394],[-4.313,79.127],[-5.625,72.26],[-5.884,-81.156],[-3.817,-88.536],[-1.625,-89.046]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-1.581,0.831],[0,0]],"o":[[0,0],[-1.154,-1.025],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-4.92,81.663],[-10.263,80.552],[-11.8,73.81],[-11.934,-81.831],[-9.692,-89.236],[-4.55,-89.626]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":103.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-1.817,0.519],[0,0]],"o":[[0,0],[-1.491,-0.875],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-10.917,83.519],[-16.922,82.69],[-18.797,76.135],[-18.744,-82.844],[-16.239,-90.286],[-10.547,-90.546]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.052,0.208],[0,0]],"o":[[0,0],[-1.829,-0.725],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-16.289,85.394],[-21.456,84.827],[-23.669,78.46],[-23.428,-83.856],[-20.661,-91.336],[-15.919,-91.446]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":106.666,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.054,-0.625],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-20.027,86.635],[-23.438,86.252],[-25.875,80.01],[-25.509,-84.531],[-22.567,-92.036],[-19.656,-92.055]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.069,-0.563],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-20.345,86.897],[-24.538,86.552],[-26.994,80.535],[-26.609,-85.031],[-23.661,-92.186],[-19.969,-92.205]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":109.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.116,-0.375],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-21.718,87.685],[-26.535,87.452],[-29.048,82.11],[-28.605,-86.531],[-25.64,-92.636],[-21.323,-92.655]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.163,-0.188],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-22.982,88.472],[-27.674,88.352],[-30.243,83.685],[-29.742,-88.031],[-26.76,-93.086],[-22.568,-93.105]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":112.666,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.194,-0.063],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-24.293,88.997],[-28.485,88.952],[-31.091,84.735],[-30.553,-89.031],[-27.558,-93.386],[-23.866,-93.405]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":113.334,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-24.558,89.26],[-28.188,89.252],[-30.812,85.26],[-30.255,-89.531],[-27.255,-93.536],[-24.125,-93.555]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":114.666,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-24.765,89.36],[-28.394,89.352],[-31.219,85.359],[-30.748,-89.532],[-27.548,-93.536],[-24.419,-93.555]],"c":true}]},{"t":120,"s":[{"i":[[0,0],[0,0],[0,2.209],[0,0],[-2.209,0],[0,0]],"o":[[0,0],[-2.209,0],[0,0],[0,-2.209],[0,0],[0,0]],"v":[[-25.373,89.63],[-27.502,89.623],[-31.127,85.625],[-31.005,-89.666],[-27.005,-93.666],[-25.375,-93.685]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-698.509,46.873],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":96,"op":241,"st":60,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/rear_display_turnaround.json b/packages/SystemUI/res/raw/rear_display_turnaround.json
new file mode 100644
index 000000000000..82204c726e8d
--- /dev/null
+++ b/packages/SystemUI/res/raw/rear_display_turnaround.json
@@ -0,0 +1 @@
+{"v":"5.8.1","fr":60,"ip":0,"op":181,"w":412,"h":300,"nm":"Turnaround - Generic Version V01","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"SHUTTER_ANIMATION 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":55,"s":[100]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":320.842,"ix":3},"y":{"a":0,"k":149.716,"ix":4}},"a":{"a":0,"k":[460.228,450.736,0],"ix":1,"l":2},"s":{"a":0,"k":[150,150,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.528,0],[0,-2.528],[2.528,0],[0,2.528]],"o":[[2.528,0],[0,2.528],[-2.528,0],[0,-2.528]],"v":[[0,-4.5],[4.5,0],[0,4.5],[-4.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 599","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[460.228,450.736],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[140,140],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"SHUTTER_01","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.337,0],[0,-5.337],[5.337,0],[0,5.337]],"o":[[5.337,0],[0,5.337],[-5.337,0],[0,-5.337]],"v":[[0,-9.5],[9.5,0],[0,9.5],[-9.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 598","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[460.228,450.736],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"SHUTTER_02","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":150,"st":-90,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"SHUTTER_ANIMATION","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":51,"s":[0]},{"t":56,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":286.342,"ix":3},"y":{"a":0,"k":245.716,"ix":4}},"a":{"a":0,"k":[460.228,450.736,0],"ix":1,"l":2},"s":{"a":0,"k":[150,150,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.528,0],[0,-2.528],[2.528,0],[0,2.528]],"o":[[2.528,0],[0,2.528],[-2.528,0],[0,-2.528]],"v":[[0,-4.5],[4.5,0],[0,4.5],[-4.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 599","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[460.228,450.736],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[140,140],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":129,"s":[0]},{"t":139,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"SHUTTER_01","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.337,0],[0,-5.337],[5.337,0],[0,5.337]],"o":[[5.337,0],[0,5.337],[-5.337,0],[0,-5.337]],"v":[[0,-9.5],[9.5,0],[0,9.5],[-9.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 598","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[460.228,450.736],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":129,"s":[0]},{"t":139,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"SHUTTER_02","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":129,"op":279,"st":39,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"PATCH 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[207.062,149.969,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":99.166,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54.347,-125.337],[54.347,125.337],[-15.847,125.337],[-15.847,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[34.347,-125.337],[34.347,125.337],[-35.847,125.337],[-35.847,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":109.166,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-3.683,-125.337],[-3.683,125.337],[-73.877,125.337],[-73.877,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":110,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-3.867,-125.337],[-3.867,125.337],[-74.061,125.337],[-74.061,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":113.334,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-1.606,-125.337],[-1.606,125.337],[-71.799,125.337],[-71.799,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":115,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0.775,-125.337],[0.775,125.337],[-69.418,125.337],[-69.418,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":117.5,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[4.347,-125.337],[4.347,125.337],[-65.847,125.337],[-65.847,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":121.666,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[9.686,-125.337],[9.686,125.337],[-60.508,125.337],[-60.508,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":122.5,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[11.954,-125.337],[11.954,125.337],[-58.24,125.337],[-58.24,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":125.834,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[14.759,-125.337],[14.759,125.337],[-55.435,125.337],[-55.435,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126.666,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[15.668,-125.337],[15.668,125.337],[-54.525,125.337],[-54.525,-125.337]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":128.334,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[15.987,-125.337],[15.987,125.337],[-54.206,125.337],[-54.206,-125.337]],"c":true}]},{"t":135,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16.847,-125.337],[16.847,125.337],[-53.347,125.337],[-53.347,-125.337]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-674.097,51.337],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":99,"op":300,"st":60,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"PATCH","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[207.908,149.969,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[722.046,-80],[722.046,165.813],[708.25,165.813],[708.25,-80]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[736.168,-80],[736.168,165.813],[722.372,165.813],[722.372,-80]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.834,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[737.98,-80],[737.98,165.813],[724.184,165.813],[724.184,-80]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66.666,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[739.435,-80],[739.435,165.813],[725.639,165.813],[725.639,-80]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":67.5,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[740.796,-80],[740.796,165.813],[727,165.813],[727,-80]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":69.166,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[743.651,-79.231],[743.651,166.583],[729.855,166.583],[729.855,-79.231]],"c":true}]},{"t":72.5,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[738.112,-77.692],[738.112,168.121],[724.316,168.121],[724.316,-77.692]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":74,"st":60,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"BUTTON_OUT_BOTTOM 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":60,"s":[206.312,95.219,0],"to":[0,-2.417,0],"ti":[0,0,0]},{"i":{"x":0.333,"y":1},"o":{"x":0.167,"y":0.167},"t":88,"s":[206.312,80.719,0],"to":[0,0,0],"ti":[0,-2.417,0]},{"t":128,"s":[206.312,95.219,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,18,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,-9.113],[0,0],[3.544,-0.062],[0,9.113],[0,0],[-1.905,0.228]],"o":[[0,0],[0,9.113],[-1.908,0.034],[0,0],[0,-9.113],[4.17,-0.5]],"v":[[687.673,-97.826],[687.673,65.299],[679.205,76.875],[675.75,65.375],[675.75,-97.75],[679.205,-108.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65,"s":[{"i":[[0,-9.113],[0,0],[4.532,-0.062],[0,9.113],[0,0],[-2.436,0.228]],"o":[[0,0],[0,9.113],[-2.44,0.034],[0,0],[0,-9.113],[5.332,-0.5]],"v":[[699.977,-97.944],[699.977,68.306],[689.293,80],[684.875,68.5],[684.875,-97.75],[689.293,-108.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70,"s":[{"i":[[0,-9.113],[0,0],[4.532,-0.062],[0,9.113],[0,0],[-2.436,0.228]],"o":[[0,0],[0,9.113],[-2.44,0.034],[0,0],[0,-9.113],[5.332,-0.5]],"v":[[711.227,-100.491],[711.227,76.697],[700.543,87.875],[696.125,76.375],[696.125,-100.812],[700.543,-111.688]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":73,"s":[{"i":[[0,-9.113],[0,0],[5.113,-0.062],[0,9.113],[0,0],[-2.748,0.228]],"o":[[0,0],[0,9.113],[-2.753,0.034],[0,0],[0,-9.113],[6.016,-0.5]],"v":[[700.685,-102.905],[700.685,81.345],[692.568,93.125],[687.583,81.625],[687.583,-102.625],[692.568,-113.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":74.166,"s":[{"i":[[0,-9.113],[0,0],[5.694,-0.062],[0,9.113],[0,0],[-3.061,0.228]],"o":[[0,0],[0,9.113],[-3.065,0.034],[0,0],[0,-9.113],[6.699,-0.5]],"v":[[691.581,-103.125],[691.581,83.5],[686.03,95],[680.479,83.5],[680.479,-103.125],[686.03,-114]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":75,"s":[{"i":[[0,-9.113],[0,0],[6.275,-0.062],[0,9.113],[0,0],[-3.373,0.228]],"o":[[0,0],[0,9.113],[-3.378,0.034],[0,0],[0,-9.113],[7.383,-0.5]],"v":[[683.359,-103.625],[683.359,85.375],[677.242,96.875],[671.125,85.375],[671.125,-103.625],[677.242,-114.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":75.834,"s":[{"i":[[0,-9.113],[0,0],[6.856,-0.062],[0,9.113],[0,0],[-3.685,0.228]],"o":[[0,0],[0,9.113],[-3.691,0.034],[0,0],[0,-9.113],[8.066,-0.5]],"v":[[672.305,-104.125],[672.305,87.25],[665.621,98.75],[658.938,87.25],[658.938,-104.125],[665.621,-115]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[0,-9.113],[0,0],[7.438,-0.062],[0,9.113],[0,0],[-3.997,0.228]],"o":[[0,0],[0,9.113],[-4.004,0.034],[0,0],[0,-9.113],[8.75,-0.5]],"v":[[657.5,-104.625],[657.5,89.125],[650.25,100.625],[643,89.125],[643,-104.625],[650.25,-115.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":77.5,"s":[{"i":[[0,-9.113],[0,0],[7.149,-0.047],[0,9.113],[0,0],[-4.569,0.171]],"o":[[0,0],[0,9.113],[-4.573,0.025],[0,0],[0,-9.113],[8.133,-0.375]],"v":[[637.562,-104.531],[637.562,90.531],[629.281,103.281],[621,90.531],[621,-104.531],[629.281,-116.812]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":78.334,"s":[{"i":[[0,-9.113],[0,0],[6.86,-0.031],[0,9.113],[0,0],[-5.14,0.114]],"o":[[0,0],[0,9.113],[-5.143,0.017],[0,0],[0,-9.113],[7.516,-0.25]],"v":[[612.625,-104.438],[612.625,91.938],[603.312,105.938],[594,91.938],[594,-104.438],[603.312,-118.125]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":79.166,"s":[{"i":[[0,-9.113],[0,0],[6.571,-0.016],[0,9.113],[0,0],[-5.711,0.057]],"o":[[0,0],[0,9.113],[-5.713,0.008],[0,0],[0,-9.113],[6.899,-0.125]],"v":[[580.188,-104.094],[580.188,93.594],[569.844,108.844],[559.5,93.594],[559.5,-104.094],[569.844,-119.188]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80,"s":[{"i":[[0,-9.113],[0,0],[6.282,0],[0,9.113],[0,0],[-6.282,0]],"o":[[0,0],[0,9.113],[-6.282,0],[0,0],[0,-9.113],[6.282,0]],"v":[[540.25,-103.75],[540.25,95.25],[528.875,111.75],[517.5,95.25],[517.5,-103.75],[528.875,-120.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80.834,"s":[{"i":[[0,-9.113],[0,0],[6.731,0],[0,9.113],[0,0],[-6.731,0]],"o":[[0,0],[0,9.113],[-6.731,0],[0,0],[0,-9.113],[6.731,0]],"v":[[491.25,-104.562],[491.25,97.688],[479.062,114.188],[466.875,97.688],[466.875,-104.562],[479.062,-121.062]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[0,-9.113],[0,0],[7.18,0],[0,9.113],[0,0],[-7.18,0]],"o":[[0,0],[0,9.113],[-7.18,0],[0,0],[0,-9.113],[7.18,0]],"v":[[432.958,-105.375],[432.958,100.125],[419.958,116.625],[406.958,100.125],[406.958,-105.375],[419.958,-121.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":82.5,"s":[{"i":[[0,-9.113],[0,0],[7.628,0],[0,9.113],[0,0],[-7.628,0]],"o":[[0,0],[0,9.113],[-7.628,0],[0,0],[0,-9.113],[7.628,0]],"v":[[364.854,-106.188],[364.854,102.562],[351.042,119.062],[337.229,102.562],[337.229,-106.188],[351.042,-122.688]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":83.334,"s":[{"i":[[0,-9.113],[0,0],[8.077,0],[0,9.113],[0,0],[-8.077,0]],"o":[[0,0],[0,9.113],[-8.077,0],[0,0],[0,-9.113],[8.077,0]],"v":[[286.75,-107],[286.75,105],[272.125,121.5],[257.5,105],[257.5,-107],[272.125,-123.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[0,-9.113],[0,0],[8.33,0],[0,9.113],[0,0],[-8.33,0]],"o":[[0,0],[0,9.113],[-8.33,0],[0,0],[0,-9.113],[8.33,0]],"v":[[201.667,-107.5],[201.667,106.167],[186.583,122.667],[171.5,106.167],[171.5,-107.5],[186.583,-124]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":85,"s":[{"i":[[0,-9.113],[0,0],[8.583,0],[0,9.113],[0,0],[-8.583,0]],"o":[[0,0],[0,9.113],[-8.583,0],[0,0],[0,-9.113],[8.583,0]],"v":[[110.333,-108],[110.333,107.333],[94.792,123.833],[79.25,107.333],[79.25,-108],[94.792,-124.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":85.834,"s":[{"i":[[0,-9.113],[0,0],[8.837,0],[0,9.113],[0,0],[-8.837,0]],"o":[[0,0],[0,9.113],[-8.837,0],[0,0],[0,-9.113],[8.837,0]],"v":[[16.5,-108.5],[16.5,108.5],[0.5,125],[-15.5,108.5],[-15.5,-108.5],[0.5,-125]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[0,-9.113],[0,0],[8.698,0],[0,9.113],[0,0],[-8.699,0]],"o":[[0,0],[0,9.113],[-8.699,0],[0,0],[0,-9.113],[8.698,0]],"v":[[-75.766,-108.229],[-75.766,107.667],[-91.516,124.167],[-107.266,107.667],[-107.266,-108.229],[-91.516,-124.729]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[0,-9.113],[0,0],[8.56,0],[0,9.113],[0,0],[-8.56,0]],"o":[[0,0],[0,9.113],[-8.56,0],[0,0],[0,-9.113],[8.56,0]],"v":[[-166.781,-107.958],[-166.781,106.833],[-182.281,123.333],[-197.781,106.833],[-197.781,-107.958],[-182.281,-124.458]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88.334,"s":[{"i":[[0,-9.113],[0,0],[8.422,0],[0,9.113],[0,0],[-8.422,0]],"o":[[0,0],[0,9.113],[-8.422,0],[0,0],[0,-9.113],[8.422,0]],"v":[[-252.172,-107.688],[-252.172,106],[-267.422,122.5],[-282.672,106],[-282.672,-107.688],[-267.422,-124.188]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":89.166,"s":[{"i":[[0,-9.113],[0,0],[8.284,0],[0,9.113],[0,0],[-8.284,0]],"o":[[0,0],[0,9.113],[-8.284,0],[0,0],[0,-9.113],[8.284,0]],"v":[[-330.062,-107.417],[-330.062,105.167],[-345.062,121.667],[-360.062,105.167],[-360.062,-107.417],[-345.062,-123.917]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,-9.113],[0,0],[8.146,0],[0,9.113],[0,0],[-8.146,0]],"o":[[0,0],[0,9.113],[-8.146,0],[0,0],[0,-9.113],[8.146,0]],"v":[[-400.781,-107.146],[-400.781,103.396],[-415.531,119.896],[-430.281,103.396],[-430.281,-107.146],[-415.531,-123.646]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90.834,"s":[{"i":[[0,-9.113],[0,0],[8.008,0],[0,9.113],[0,0],[-8.008,0]],"o":[[0,0],[0,9.113],[-8.008,0],[0,0],[0,-9.113],[8.008,0]],"v":[[-462.75,-106.875],[-462.75,101.625],[-477.25,118.125],[-491.75,101.625],[-491.75,-106.875],[-477.25,-123.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":91.666,"s":[{"i":[[0,-9.113],[0,0],[7.525,0],[0,9.113],[0,0],[-7.525,0]],"o":[[0,0],[0,9.113],[-7.525,0],[0,0],[0,-9.113],[7.525,0]],"v":[[-518.308,-106.5],[-518.308,100.125],[-531.933,116.125],[-545.558,100.125],[-545.558,-106.5],[-531.933,-122.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":92.5,"s":[{"i":[[0,-9.113],[0,0],[7.042,0],[0,9.113],[0,0],[-7.042,0]],"o":[[0,0],[0,9.113],[-7.042,0],[0,0],[0,-9.113],[7.042,0]],"v":[[-566.367,-106.125],[-566.367,98.625],[-579.117,114.125],[-591.867,98.625],[-591.867,-106.125],[-579.117,-121.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[0,-9.113],[0,0],[6.558,0],[0,9.113],[0,0],[-6.558,0]],"o":[[0,0],[0,9.113],[-6.558,0],[0,0],[0,-9.113],[6.558,0]],"v":[[-606.925,-105.75],[-606.925,97.125],[-618.8,112.125],[-630.675,97.125],[-630.675,-105.75],[-618.8,-120.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":94.166,"s":[{"i":[[0,-9.113],[0,0],[6.075,0],[0,9.113],[0,0],[-6.075,0]],"o":[[0,0],[0,9.113],[-6.075,0],[0,0],[0,-9.113],[6.075,0]],"v":[[-641.963,-105.375],[-641.963,95.625],[-652.963,110.125],[-663.963,95.625],[-663.963,-105.375],[-652.963,-119.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95,"s":[{"i":[[0,-9.113],[0,0],[5.592,0],[0,9.113],[0,0],[-5.592,0]],"o":[[0,0],[0,9.113],[-5.592,0],[0,0],[0,-9.113],[5.592,0]],"v":[[-671.062,-105],[-671.062,94.125],[-681.188,108.125],[-691.312,94.125],[-691.312,-105],[-681.188,-119]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95.834,"s":[{"i":[[0,-9.113],[0,0],[5.295,0],[0,9.113],[0,0],[-5.295,0]],"o":[[0,0],[0,9.113],[-5.295,0],[0,0],[0,-9.113],[5.295,0]],"v":[[-696.971,-104.5],[-696.971,92.5],[-706.558,106.25],[-716.146,92.5],[-716.146,-104.5],[-706.558,-118.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96.666,"s":[{"i":[[0,-9.113],[0,0],[4.998,0],[0,9.113],[0,0],[-4.998,0]],"o":[[0,0],[0,9.113],[-4.998,0],[0,0],[0,-9.113],[4.998,0]],"v":[[-718.192,-104],[-718.192,90.875],[-727.242,104.375],[-736.292,90.875],[-736.292,-104],[-727.242,-117.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[0,-9.113],[0,0],[4.701,0],[0,9.113],[0,0],[-4.701,0]],"o":[[0,0],[0,9.113],[-4.701,0],[0,0],[0,-9.113],[4.701,0]],"v":[[-735.35,-103.5],[-735.35,89.25],[-743.862,102.5],[-752.375,89.25],[-752.375,-103.5],[-743.862,-116.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":98.334,"s":[{"i":[[0,-9.113],[0,0],[4.404,0],[0,9.113],[0,0],[-4.404,0]],"o":[[0,0],[0,9.113],[-4.404,0],[0,0],[0,-9.113],[4.404,0]],"v":[[-750.425,-103],[-750.425,87.625],[-758.4,100.625],[-766.375,87.625],[-766.375,-103],[-758.4,-116]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":99.166,"s":[{"i":[[0,-9.113],[0,0],[4.108,0],[0,9.113],[0,0],[-4.108,0]],"o":[[0,0],[0,9.113],[-4.108,0],[0,0],[0,-9.113],[4.108,0]],"v":[[-761.75,-102.5],[-761.75,86],[-769.188,98.75],[-776.625,86],[-776.625,-102.5],[-769.188,-115.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100,"s":[{"i":[[0,-9.113],[0,0],[3.844,-0.015],[0.141,9.991],[0,0],[-4.51,0]],"o":[[0,0],[0,9.113],[-4.635,0.031],[0,0],[0.016,-9.897],[3.844,0]],"v":[[-771.312,-102.188],[-771.312,84.594],[-778.273,97.344],[-785.234,84.594],[-785.234,-102.188],[-778.273,-114.938]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100.834,"s":[{"i":[[0,-9.113],[0,0],[3.581,-0.031],[0.281,10.869],[0,0],[-4.913,0]],"o":[[0,0],[0,9.113],[-5.163,0.062],[0,0],[0.031,-10.681],[3.581,0]],"v":[[-779.25,-101.875],[-779.25,83.188],[-785.734,95.938],[-792.219,83.188],[-792.219,-101.875],[-785.734,-114.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101.666,"s":[{"i":[[0,-9.113],[0,0],[3.317,-0.046],[0.422,11.747],[0,0],[-5.316,0]],"o":[[0,0],[0,9.113],[-5.691,0.094],[0,0],[0.047,-11.466],[3.318,0]],"v":[[-786.125,-101.562],[-786.125,81.781],[-792.133,94.531],[-798.141,81.781],[-798.141,-101.562],[-792.133,-114.312]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":102.5,"s":[{"i":[[0,-9.113],[0,0],[3.054,-0.061],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-6.219,0.125],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-791.75,-101.25],[-791.75,80.375],[-797.281,93.125],[-802.812,80.375],[-802.812,-101.25],[-797.281,-114]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[0,-9.113],[0,0],[3.054,-0.07],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-6.51,0.153],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-796.934,-100.556],[-796.934,78.292],[-802.465,91.042],[-807.997,78.292],[-807.997,-100.556],[-802.465,-113.306]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105.834,"s":[{"i":[[0,-9.113],[0,0],[3.054,-0.079],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-6.802,0.181],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-799.618,-99.861],[-799.618,76.208],[-805.149,88.958],[-810.681,76.208],[-810.681,-99.861],[-805.149,-112.611]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":108.334,"s":[{"i":[[0,-9.113],[0,0],[3.053,-0.092],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-7.24,0.222],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-800.61,-98.819],[-800.61,73.083],[-806.141,85.833],[-811.672,73.083],[-811.672,-98.819],[-806.141,-111.569]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":110,"s":[{"i":[[0,-9.113],[0,0],[3.053,-0.101],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-7.531,0.25],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-799.188,-98.125],[-799.188,71],[-804.719,83.75],[-814.636,70.875],[-814.636,-98.25],[-804.719,-110.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":119,"s":[{"i":[[0,-9.113],[0,0],[3.053,-0.101],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-7.531,0.25],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-787.944,-96.125],[-787.944,66.75],[-793.475,79.5],[-804.019,66.875],[-804.019,-96],[-793.475,-108.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":124,"s":[{"i":[[0,-9.113],[0,0],[3.053,-0.101],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-7.531,0.25],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-780.109,-96.579],[-780.109,65.77],[-785.64,78.355],[-796.184,65.566],[-796.184,-96.783],[-785.64,-108.836]],"c":true}]},{"t":128,"s":[{"i":[[0,-9.113],[0,0],[3.053,-0.101],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-7.531,0.25],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-779.207,-96.704],[-779.207,65.118],[-784.738,77.54],[-794.029,65.112],[-794.029,-96.71],[-784.738,-108.467]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[53.5,61],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300,"st":60,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"BUTTON_OUT_BOTTOM","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.312,157.219,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,25,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,-9.113],[0,0],[3.544,-0.062],[0,9.113],[0,0],[-1.905,0.228]],"o":[[0,0],[0,9.113],[-1.908,0.034],[0,0],[0,-9.113],[4.17,-0.5]],"v":[[688.299,-98],[688.299,65.125],[679.205,76.875],[675.75,65.375],[675.75,-97.75],[679.205,-108.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65,"s":[{"i":[[0,-9.113],[0,0],[4.532,-0.062],[0,9.113],[0,0],[-2.436,0.228]],"o":[[0,0],[0,9.113],[-2.44,0.034],[0,0],[0,-9.113],[5.332,-0.5]],"v":[[701.856,-97.875],[701.856,68.375],[689.293,80],[684.875,68.5],[684.875,-97.75],[689.293,-108.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70,"s":[{"i":[[0,-9.113],[0,0],[4.532,-0.062],[0,9.113],[0,0],[-2.436,0.228]],"o":[[0,0],[0,9.113],[-2.44,0.034],[0,0],[0,-9.113],[5.332,-0.5]],"v":[[709.974,-101.062],[709.974,76.125],[700.543,87.875],[696.125,76.375],[696.125,-100.812],[700.543,-111.688]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":73,"s":[{"i":[[0,-9.113],[0,0],[5.113,-0.062],[0,9.113],[0,0],[-2.748,0.228]],"o":[[0,0],[0,9.113],[-2.753,0.034],[0,0],[0,-9.113],[6.016,-0.5]],"v":[[703.818,-102.625],[703.818,81.625],[692.568,93.125],[687.583,81.625],[687.583,-102.625],[692.568,-113.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":74.166,"s":[{"i":[[0,-9.113],[0,0],[5.694,-0.062],[0,9.113],[0,0],[-3.061,0.228]],"o":[[0,0],[0,9.113],[-3.065,0.034],[0,0],[0,-9.113],[6.699,-0.5]],"v":[[691.581,-103.125],[691.581,83.5],[686.03,95],[680.479,83.5],[680.479,-103.125],[686.03,-114]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":75,"s":[{"i":[[0,-9.113],[0,0],[6.275,-0.062],[0,9.113],[0,0],[-3.373,0.228]],"o":[[0,0],[0,9.113],[-3.378,0.034],[0,0],[0,-9.113],[7.383,-0.5]],"v":[[683.359,-103.625],[683.359,85.375],[677.242,96.875],[671.125,85.375],[671.125,-103.625],[677.242,-114.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":75.834,"s":[{"i":[[0,-9.113],[0,0],[6.856,-0.062],[0,9.113],[0,0],[-3.685,0.228]],"o":[[0,0],[0,9.113],[-3.691,0.034],[0,0],[0,-9.113],[8.066,-0.5]],"v":[[672.305,-104.125],[672.305,87.25],[665.621,98.75],[658.938,87.25],[658.938,-104.125],[665.621,-115]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[0,-9.113],[0,0],[7.438,-0.062],[0,9.113],[0,0],[-3.997,0.228]],"o":[[0,0],[0,9.113],[-4.004,0.034],[0,0],[0,-9.113],[8.75,-0.5]],"v":[[657.5,-104.625],[657.5,89.125],[650.25,100.625],[643,89.125],[643,-104.625],[650.25,-115.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":77.5,"s":[{"i":[[0,-9.113],[0,0],[7.149,-0.047],[0,9.113],[0,0],[-4.569,0.171]],"o":[[0,0],[0,9.113],[-4.573,0.025],[0,0],[0,-9.113],[8.133,-0.375]],"v":[[637.562,-104.531],[637.562,90.531],[629.281,103.281],[621,90.531],[621,-104.531],[629.281,-116.812]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":78.334,"s":[{"i":[[0,-9.113],[0,0],[6.86,-0.031],[0,9.113],[0,0],[-5.14,0.114]],"o":[[0,0],[0,9.113],[-5.143,0.017],[0,0],[0,-9.113],[7.516,-0.25]],"v":[[612.625,-104.438],[612.625,91.938],[603.312,105.938],[594,91.938],[594,-104.438],[603.312,-118.125]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":79.166,"s":[{"i":[[0,-9.113],[0,0],[6.571,-0.016],[0,9.113],[0,0],[-5.711,0.057]],"o":[[0,0],[0,9.113],[-5.713,0.008],[0,0],[0,-9.113],[6.899,-0.125]],"v":[[580.188,-104.094],[580.188,93.594],[569.844,108.844],[559.5,93.594],[559.5,-104.094],[569.844,-119.188]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80,"s":[{"i":[[0,-9.113],[0,0],[6.282,0],[0,9.113],[0,0],[-6.282,0]],"o":[[0,0],[0,9.113],[-6.282,0],[0,0],[0,-9.113],[6.282,0]],"v":[[540.25,-103.75],[540.25,95.25],[528.875,111.75],[517.5,95.25],[517.5,-103.75],[528.875,-120.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80.834,"s":[{"i":[[0,-9.113],[0,0],[6.731,0],[0,9.113],[0,0],[-6.731,0]],"o":[[0,0],[0,9.113],[-6.731,0],[0,0],[0,-9.113],[6.731,0]],"v":[[491.25,-104.562],[491.25,97.688],[479.062,114.188],[466.875,97.688],[466.875,-104.562],[479.062,-121.062]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[0,-9.113],[0,0],[7.18,0],[0,9.113],[0,0],[-7.18,0]],"o":[[0,0],[0,9.113],[-7.18,0],[0,0],[0,-9.113],[7.18,0]],"v":[[432.958,-105.375],[432.958,100.125],[419.958,116.625],[406.958,100.125],[406.958,-105.375],[419.958,-121.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":82.5,"s":[{"i":[[0,-9.113],[0,0],[7.628,0],[0,9.113],[0,0],[-7.628,0]],"o":[[0,0],[0,9.113],[-7.628,0],[0,0],[0,-9.113],[7.628,0]],"v":[[364.854,-106.188],[364.854,102.562],[351.042,119.062],[337.229,102.562],[337.229,-106.188],[351.042,-122.688]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":83.334,"s":[{"i":[[0,-9.113],[0,0],[8.077,0],[0,9.113],[0,0],[-8.077,0]],"o":[[0,0],[0,9.113],[-8.077,0],[0,0],[0,-9.113],[8.077,0]],"v":[[286.75,-107],[286.75,105],[272.125,121.5],[257.5,105],[257.5,-107],[272.125,-123.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[0,-9.113],[0,0],[8.33,0],[0,9.113],[0,0],[-8.33,0]],"o":[[0,0],[0,9.113],[-8.33,0],[0,0],[0,-9.113],[8.33,0]],"v":[[201.667,-107.5],[201.667,106.167],[186.583,122.667],[171.5,106.167],[171.5,-107.5],[186.583,-124]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":85,"s":[{"i":[[0,-9.113],[0,0],[8.583,0],[0,9.113],[0,0],[-8.583,0]],"o":[[0,0],[0,9.113],[-8.583,0],[0,0],[0,-9.113],[8.583,0]],"v":[[110.333,-108],[110.333,107.333],[94.792,123.833],[79.25,107.333],[79.25,-108],[94.792,-124.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":85.834,"s":[{"i":[[0,-9.113],[0,0],[8.837,0],[0,9.113],[0,0],[-8.837,0]],"o":[[0,0],[0,9.113],[-8.837,0],[0,0],[0,-9.113],[8.837,0]],"v":[[16.5,-108.5],[16.5,108.5],[0.5,125],[-15.5,108.5],[-15.5,-108.5],[0.5,-125]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[0,-9.113],[0,0],[8.698,0],[0,9.113],[0,0],[-8.699,0]],"o":[[0,0],[0,9.113],[-8.699,0],[0,0],[0,-9.113],[8.698,0]],"v":[[-75.766,-108.229],[-75.766,107.667],[-91.516,124.167],[-107.266,107.667],[-107.266,-108.229],[-91.516,-124.729]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[0,-9.113],[0,0],[8.56,0],[0,9.113],[0,0],[-8.56,0]],"o":[[0,0],[0,9.113],[-8.56,0],[0,0],[0,-9.113],[8.56,0]],"v":[[-166.781,-107.958],[-166.781,106.833],[-182.281,123.333],[-197.781,106.833],[-197.781,-107.958],[-182.281,-124.458]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88.334,"s":[{"i":[[0,-9.113],[0,0],[8.422,0],[0,9.113],[0,0],[-8.422,0]],"o":[[0,0],[0,9.113],[-8.422,0],[0,0],[0,-9.113],[8.422,0]],"v":[[-252.172,-107.688],[-252.172,106],[-267.422,122.5],[-282.672,106],[-282.672,-107.688],[-267.422,-124.188]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":89.166,"s":[{"i":[[0,-9.113],[0,0],[8.284,0],[0,9.113],[0,0],[-8.284,0]],"o":[[0,0],[0,9.113],[-8.284,0],[0,0],[0,-9.113],[8.284,0]],"v":[[-330.062,-107.417],[-330.062,105.167],[-345.062,121.667],[-360.062,105.167],[-360.062,-107.417],[-345.062,-123.917]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,-9.113],[0,0],[8.146,0],[0,9.113],[0,0],[-8.146,0]],"o":[[0,0],[0,9.113],[-8.146,0],[0,0],[0,-9.113],[8.146,0]],"v":[[-400.781,-107.146],[-400.781,103.396],[-415.531,119.896],[-430.281,103.396],[-430.281,-107.146],[-415.531,-123.646]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90.834,"s":[{"i":[[0,-9.113],[0,0],[8.008,0],[0,9.113],[0,0],[-8.008,0]],"o":[[0,0],[0,9.113],[-8.008,0],[0,0],[0,-9.113],[8.008,0]],"v":[[-462.75,-106.875],[-462.75,101.625],[-477.25,118.125],[-491.75,101.625],[-491.75,-106.875],[-477.25,-123.375]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":91.666,"s":[{"i":[[0,-9.113],[0,0],[7.525,0],[0,9.113],[0,0],[-7.525,0]],"o":[[0,0],[0,9.113],[-7.525,0],[0,0],[0,-9.113],[7.525,0]],"v":[[-518.308,-106.5],[-518.308,100.125],[-531.933,116.125],[-545.558,100.125],[-545.558,-106.5],[-531.933,-122.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":92.5,"s":[{"i":[[0,-9.113],[0,0],[7.042,0],[0,9.113],[0,0],[-7.042,0]],"o":[[0,0],[0,9.113],[-7.042,0],[0,0],[0,-9.113],[7.042,0]],"v":[[-566.367,-106.125],[-566.367,98.625],[-579.117,114.125],[-591.867,98.625],[-591.867,-106.125],[-579.117,-121.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[0,-9.113],[0,0],[6.558,0],[0,9.113],[0,0],[-6.558,0]],"o":[[0,0],[0,9.113],[-6.558,0],[0,0],[0,-9.113],[6.558,0]],"v":[[-606.925,-105.75],[-606.925,97.125],[-618.8,112.125],[-630.675,97.125],[-630.675,-105.75],[-618.8,-120.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":94.166,"s":[{"i":[[0,-9.113],[0,0],[6.075,0],[0,9.113],[0,0],[-6.075,0]],"o":[[0,0],[0,9.113],[-6.075,0],[0,0],[0,-9.113],[6.075,0]],"v":[[-641.963,-105.375],[-641.963,95.625],[-652.963,110.125],[-663.963,95.625],[-663.963,-105.375],[-652.963,-119.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95,"s":[{"i":[[0,-9.113],[0,0],[5.592,0],[0,9.113],[0,0],[-5.592,0]],"o":[[0,0],[0,9.113],[-5.592,0],[0,0],[0,-9.113],[5.592,0]],"v":[[-671.062,-105],[-671.062,94.125],[-681.188,108.125],[-691.312,94.125],[-691.312,-105],[-681.188,-119]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95.834,"s":[{"i":[[0,-9.113],[0,0],[5.295,0],[0,9.113],[0,0],[-5.295,0]],"o":[[0,0],[0,9.113],[-5.295,0],[0,0],[0,-9.113],[5.295,0]],"v":[[-696.971,-104.5],[-696.971,92.5],[-706.558,106.25],[-716.146,92.5],[-716.146,-104.5],[-706.558,-118.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96.666,"s":[{"i":[[0,-9.113],[0,0],[4.998,0],[0,9.113],[0,0],[-4.998,0]],"o":[[0,0],[0,9.113],[-4.998,0],[0,0],[0,-9.113],[4.998,0]],"v":[[-718.192,-104],[-718.192,90.875],[-727.242,104.375],[-736.292,90.875],[-736.292,-104],[-727.242,-117.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[0,-9.113],[0,0],[4.701,0],[0,9.113],[0,0],[-4.701,0]],"o":[[0,0],[0,9.113],[-4.701,0],[0,0],[0,-9.113],[4.701,0]],"v":[[-735.35,-103.5],[-735.35,89.25],[-743.862,102.5],[-752.375,89.25],[-752.375,-103.5],[-743.862,-116.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":98.334,"s":[{"i":[[0,-9.113],[0,0],[4.404,0],[0,9.113],[0,0],[-4.404,0]],"o":[[0,0],[0,9.113],[-4.404,0],[0,0],[0,-9.113],[4.404,0]],"v":[[-750.425,-103],[-750.425,87.625],[-758.4,100.625],[-766.375,87.625],[-766.375,-103],[-758.4,-116]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":99.166,"s":[{"i":[[0,-9.113],[0,0],[4.108,0],[0,9.113],[0,0],[-4.108,0]],"o":[[0,0],[0,9.113],[-4.108,0],[0,0],[0,-9.113],[4.108,0]],"v":[[-761.75,-102.5],[-761.75,86],[-769.188,98.75],[-776.625,86],[-776.625,-102.5],[-769.188,-115.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100,"s":[{"i":[[0,-9.113],[0,0],[3.844,-0.015],[0.141,9.991],[0,0],[-4.51,0]],"o":[[0,0],[0,9.113],[-4.635,0.031],[0,0],[0.016,-9.897],[3.844,0]],"v":[[-771.312,-102.188],[-771.312,84.594],[-778.273,97.344],[-785.234,84.594],[-785.234,-102.188],[-778.273,-114.938]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100.834,"s":[{"i":[[0,-9.113],[0,0],[3.581,-0.031],[0.281,10.869],[0,0],[-4.913,0]],"o":[[0,0],[0,9.113],[-5.163,0.062],[0,0],[0.031,-10.681],[3.581,0]],"v":[[-779.25,-101.875],[-779.25,83.188],[-785.734,95.938],[-792.219,83.188],[-792.219,-101.875],[-785.734,-114.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101.666,"s":[{"i":[[0,-9.113],[0,0],[3.317,-0.046],[0.422,11.747],[0,0],[-5.316,0]],"o":[[0,0],[0,9.113],[-5.691,0.094],[0,0],[0.047,-11.466],[3.318,0]],"v":[[-786.125,-101.562],[-786.125,81.781],[-792.133,94.531],[-798.141,81.781],[-798.141,-101.562],[-792.133,-114.312]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":102.5,"s":[{"i":[[0,-9.113],[0,0],[3.054,-0.061],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-6.219,0.125],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-791.75,-101.25],[-791.75,80.375],[-797.281,93.125],[-802.812,80.375],[-802.812,-101.25],[-797.281,-114]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[0,-9.113],[0,0],[3.054,-0.07],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-6.51,0.153],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-796.934,-100.556],[-796.934,78.292],[-802.465,91.042],[-807.997,78.292],[-807.997,-100.556],[-802.465,-113.306]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105.834,"s":[{"i":[[0,-9.113],[0,0],[3.054,-0.079],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-6.802,0.181],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-799.618,-99.861],[-799.618,76.208],[-805.149,88.958],[-810.681,76.208],[-810.681,-99.861],[-805.149,-112.611]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":108.334,"s":[{"i":[[0,-9.113],[0,0],[3.053,-0.092],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-7.24,0.222],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-800.61,-98.819],[-800.61,73.083],[-806.141,85.833],[-811.672,73.083],[-811.672,-98.819],[-806.141,-111.569]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":110,"s":[{"i":[[0,-9.113],[0,0],[3.053,-0.101],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-7.531,0.25],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-799.188,-98.125],[-799.188,71],[-804.719,83.75],[-814.636,70.875],[-814.636,-98.25],[-804.719,-110.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":119,"s":[{"i":[[0,-9.113],[0,0],[3.053,-0.101],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-7.531,0.25],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-787.944,-96.125],[-787.944,66.75],[-793.475,79.5],[-804.019,66.875],[-804.019,-96],[-793.475,-108.875]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":124,"s":[{"i":[[0,-9.113],[0,0],[3.053,-0.101],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-7.531,0.25],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-780.109,-96.579],[-780.109,65.77],[-785.64,78.355],[-796.184,65.566],[-796.184,-96.783],[-785.64,-108.836]],"c":true}]},{"t":128,"s":[{"i":[[0,-9.113],[0,0],[3.053,-0.101],[0.562,12.625],[0,0],[-5.719,0]],"o":[[0,0],[0,9.113],[-7.531,0.25],[0,0],[0.062,-12.25],[3.055,0]],"v":[[-779.207,-96.704],[-779.207,65.118],[-784.738,77.54],[-794.029,65.112],[-794.029,-96.71],[-784.738,-108.467]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[53.5,61],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300,"st":60,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"CENTER_LENS","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.013,149.919,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,-5.969],[5.969,0],[0,0],[0,5.969],[-5.969,0],[0,0]],"o":[[0,5.969],[0,0],[-5.969,0],[0,-5.969],[0,0],[5.969,0]],"v":[[21.408,0],[10.601,10.808],[-10.601,10.808],[-21.408,0],[-10.601,-10.808],[10.601,-10.808]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62.5,"s":[{"i":[[-0.162,-5.954],[5.954,-0.162],[0,0],[0.162,5.954],[-5.954,0.162],[0,0]],"o":[[0.162,5.954],[0,0],[-5.954,0.162],[-0.162,-5.954],[0,0],[5.954,-0.162]],"v":[[23.471,-4.918],[12.899,6.54],[-8.812,6.806],[-19.793,-3.944],[-9.024,-15.178],[12.5,-15.603]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64.166,"s":[{"i":[[-0.27,-5.944],[5.944,-0.27],[0,0],[0.27,5.944],[-5.944,0.27],[0,0]],"o":[[0.27,5.944],[0,0],[-5.944,0.27],[-0.27,-5.944],[0,0],[5.944,-0.27]],"v":[[24.429,-9.031],[14.014,2.862],[-8.037,3.305],[-19.132,-7.407],[-8.389,-18.924],[13.349,-19.633]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":66.666,"s":[{"i":[[-0.432,-5.929],[5.929,-0.432],[0,0],[0.432,5.929],[-5.929,0.432],[0,0]],"o":[[0.432,5.929],[0,0],[-5.929,0.432],[-0.432,-5.929],[0,0],[5.929,-0.432]],"v":[[24.667,-16.787],[14.488,-4.243],[-8.074,-3.534],[-19.342,-14.188],[-8.638,-26.132],[13.424,-27.266]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[-0.54,-5.92],[5.92,-0.54],[0,0],[0.54,5.92],[-5.92,0.54],[0,0]],"o":[[0.54,5.92],[0,0],[-5.92,0.54],[-0.54,-5.92],[0,0],[5.92,-0.54]],"v":[[23.575,-22.686],[13.553,-9.709],[-9.348,-8.823],[-20.732,-19.439],[-10.053,-31.666],[12.223,-33.083]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70,"s":[{"i":[[-0.648,-5.91],[5.91,-0.648],[0,0],[0.648,5.91],[-5.91,0.648],[0,0]],"o":[[0.648,5.91],[0,0],[-5.91,0.648],[-0.648,-5.91],[0,0],[5.91,-0.648]],"v":[[20.259,-30.049],[10.394,-16.638],[-13.048,-15.324],[-24.296,-25.801],[-13.644,-38.312],[9.173,-40.114]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":71.666,"s":[{"i":[[-0.756,-5.9],[5.9,-0.756],[0,0],[0.756,5.9],[-5.9,0.756],[0,0]],"o":[[0.756,5.9],[0,0],[-5.9,0.756],[-0.756,-5.9],[0,0],[5.9,-0.756]],"v":[[14.171,-38.307],[4.463,-24.462],[-19.518,-22.721],[-30.632,-33.06],[-20.005,-45.855],[3.351,-48.04]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":72.5,"s":[{"i":[[-0.81,-5.895],[5.895,-0.81],[0,0],[0.81,5.895],[-5.895,0.81],[0,0]],"o":[[0.81,5.895],[0,0],[-5.895,0.81],[-0.81,-5.895],[0,0],[5.895,-0.81]],"v":[[9.346,-43.279],[-0.283,-29.218],[-24.535,-27.263],[-35.581,-37.533],[-24.967,-50.47],[-1.34,-52.846]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":73.334,"s":[{"i":[[-0.864,-5.89],[5.89,-0.864],[0,0],[0.864,5.89],[-5.89,0.864],[0,0]],"o":[[0.864,5.89],[0,0],[-5.89,0.864],[-0.864,-5.89],[0,0],[5.89,-0.864]],"v":[[3.001,-48.298],[-6.55,-34.019],[-30.497,-31.851],[-41.476,-42.052],[-30.875,-55.131],[-7.553,-57.699]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":74.166,"s":[{"i":[[-0.918,-5.885],[5.885,-0.918],[0,0],[0.918,5.885],[-5.885,0.918],[0,0]],"o":[[0.918,5.885],[0,0],[-5.885,0.918],[-0.918,-5.885],[0,0],[5.885,-0.918]],"v":[[-4.97,-53.817],[-14.443,-39.321],[-38.084,-36.939],[-48.996,-47.071],[-38.408,-60.292],[-15.391,-63.052]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":75,"s":[{"i":[[-0.972,-5.88],[5.88,-0.972],[0,0],[0.972,5.88],[-5.88,0.972],[0,0]],"o":[[0.972,5.88],[0,0],[-5.88,0.972],[-0.972,-5.88],[0,0],[5.88,-0.972]],"v":[[-14.941,-59.46],[-24.335,-44.748],[-47.671,-42.152],[-58.515,-52.215],[-47.94,-65.577],[-25.229,-68.529]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[-1.08,-5.87],[5.87,-1.08],[0,0],[1.08,5.87],[-5.87,1.08],[0,0]],"o":[[1.08,5.87],[0,0],[-5.87,1.08],[-1.08,-5.87],[0,0],[5.87,-1.08]],"v":[[-42.382,-72.123],[-51.619,-56.976],[-74.346,-53.953],[-85.055,-63.877],[-74.506,-77.524],[-52.404,-80.859]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":77.5,"s":[{"i":[[-0.856,-5.865],[5.839,-1.205],[0,0],[0.985,5.907],[-5.839,1.205],[0,0]],"o":[[0.855,5.886],[0,0],[-5.839,1.205],[-0.983,-5.883],[0,0],[5.839,-1.205]],"v":[[-61.785,-78.763],[-70.703,-63.449],[-91.979,-60.128],[-102.22,-70.048],[-92,-83.986],[-71.252,-87.688]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":78.334,"s":[{"i":[[-0.632,-5.86],[5.807,-1.33],[0,0],[0.89,5.944],[-5.807,1.33],[0,0]],"o":[[0.63,5.901],[0,0],[-5.807,1.33],[-0.886,-5.896],[0,0],[5.807,-1.33]],"v":[[-84.656,-85.779],[-93.256,-70.297],[-113.08,-66.679],[-122.854,-76.593],[-112.964,-90.823],[-93.568,-94.893]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":79.166,"s":[{"i":[[-0.408,-5.856],[5.776,-1.455],[0,0],[0.796,5.981],[-5.776,1.455],[0,0]],"o":[[0.406,5.917],[0,0],[-5.776,1.455],[-0.79,-5.909],[0,0],[5.776,-1.455]],"v":[[-112.027,-92.795],[-120.308,-77.146],[-138.681,-73.229],[-147.987,-83.138],[-138.427,-97.659],[-120.384,-102.097]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80,"s":[{"i":[[-0.184,-5.851],[5.744,-1.58],[0,0],[0.701,6.017],[-5.744,1.58],[0,0]],"o":[[0.181,5.932],[0,0],[-5.744,1.58],[-0.693,-5.922],[0,0],[5.744,-1.58]],"v":[[-144.7,-99.873],[-152.663,-84.056],[-169.585,-79.842],[-178.423,-89.745],[-169.193,-104.558],[-152.502,-109.364]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80.834,"s":[{"i":[[0.039,-5.846],[5.713,-1.705],[0,0],[0.606,6.054],[-5.713,1.705],[0,0]],"o":[[-0.044,5.947],[0,0],[-5.713,1.705],[-0.596,-5.935],[0,0],[5.713,-1.705]],"v":[[-181.936,-106.857],[-189.58,-90.873],[-205.051,-86.361],[-213.421,-96.259],[-204.521,-111.364],[-189.183,-116.536]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[0.263,-5.841],[5.681,-1.83],[0,0],[0.511,6.091],[-5.681,1.83],[0,0]],"o":[[-0.269,5.963],[0,0],[-5.681,1.83],[-0.499,-5.948],[0,0],[5.682,-1.83]],"v":[[-225.421,-113.467],[-232.747,-97.315],[-246.767,-92.505],[-254.67,-102.398],[-246.098,-117.794],[-232.114,-123.334]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":82.5,"s":[{"i":[[0.201,-5.842],[4.849,-1.973],[0,0],[0.428,5.773],[-4.511,1.767],[0,0]],"o":[[-0.205,5.947],[0,0],[-4.849,1.973],[-0.429,-5.935],[0,0],[4.849,-1.973]],"v":[[-275.671,-118.903],[-281.694,-102.971],[-292.741,-98.418],[-299.149,-108.831],[-292.426,-123.38],[-281.329,-128.511]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":83.334,"s":[{"i":[[0.138,-5.843],[4.017,-2.117],[0,0],[0.345,5.456],[-3.34,1.705],[0,0]],"o":[[-0.141,5.931],[0,0],[-4.017,2.117],[-0.359,-5.922],[0,0],[4.017,-2.117]],"v":[[-331.129,-123.089],[-335.849,-107.376],[-343.923,-103.082],[-348.837,-114.013],[-343.962,-127.716],[-335.753,-132.438]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[0.075,-5.844],[3.184,-2.261],[0,0],[0.262,5.138],[-2.169,1.643],[0,0]],"o":[[-0.077,5.915],[0,0],[-3.184,2.261],[-0.289,-5.909],[0,0],[4.042,-3.442]],"v":[[-389.712,-125.963],[-393.129,-110.469],[-398.605,-106.433],[-402.024,-117.883],[-398.998,-130.74],[-393.301,-136.302]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":85,"s":[{"i":[[0.012,-5.845],[2.352,-2.405],[0,0],[0.179,4.821],[-0.999,1.58],[0,0]],"o":[[-0.012,5.899],[0,0],[-2.352,2.405],[-0.219,-5.895],[0,0],[4.066,-4.767]],"v":[[-451.421,-127.712],[-453.534,-112.437],[-455.663,-108.66],[-457.587,-120.628],[-456.41,-132.638],[-453.975,-136.541]],"c":true}]},{"t":85.833984375,"s":[{"i":[[0.012,-5.845],[0.626,-2.62],[0,0],[0.179,4.821],[-0.999,1.58],[0,0]],"o":[[-0.012,5.899],[0,0],[-1.245,-2.898],[-0.219,-5.895],[0,0],[0.566,-0.017]],"v":[[-512.671,-125.712],[-513.534,-115.437],[-514.163,-115.66],[-514.337,-122.878],[-513.66,-133.888],[-512.975,-134.041]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[509.408,-610.192],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":87,"st":60,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"FRONT_LENS 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.312,149.969,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[-1.125,-0.688],[0,-10.77],[3.375,0.312],[0,10.77]],"o":[[2.027,1.239],[0,10.77],[-1.122,-0.104],[0,-10.77]],"v":[[-333.875,52.938],[-332.25,68.688],[-333.875,88.188],[-335.5,68.688]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88.334,"s":[{"i":[[-1.661,-0.597],[0,-10.77],[3.518,0.277],[0,10.77]],"o":[[2.541,1.071],[0,10.77],[-1.622,-0.098],[0,-10.77]],"v":[[-304.741,51.121],[-301.683,67.085],[-304.277,86.335],[-307.339,67.121]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":89.166,"s":[{"i":[[-2.197,-0.507],[0,-10.77],[3.661,0.241],[0,10.77]],"o":[[3.055,0.903],[0,10.77],[-2.123,-0.093],[0,-10.77]],"v":[[-277.357,49.304],[-272.866,65.482],[-276.429,84.482],[-280.929,65.554]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90.834,"s":[{"i":[[-3.27,-0.326],[0,-10.77],[3.946,0.17],[0,10.77]],"o":[[4.083,0.567],[0,10.77],[-3.124,-0.082],[0,-10.77]],"v":[[-227.089,45.67],[-219.732,62.277],[-225.232,80.777],[-232.607,62.42]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":91.666,"s":[{"i":[[-3.806,-0.235],[0,-10.77],[4.089,0.134],[0,10.77]],"o":[[4.597,0.399],[0,10.77],[-3.624,-0.077],[0,-10.77]],"v":[[-205.132,43.467],[-196.561,60.289],[-202.811,78.539],[-211.478,60.467]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":92.5,"s":[{"i":[[-4.342,-0.145],[0,-10.77],[4.232,0.098],[0,10.77]],"o":[[5.111,0.231],[0,10.77],[-4.125,-0.071],[0,-10.77]],"v":[[-185.301,41.265],[-175.515,58.301],[-182.515,76.301],[-192.473,58.515]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[-4.878,-0.054],[0,-10.77],[4.375,0.062],[0,10.77]],"o":[[5.625,0.062],[0,10.77],[-4.625,-0.066],[0,-10.77]],"v":[[-167.125,38.688],[-156.125,55.938],[-163.875,73.688],[-175.125,56.188]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":94.166,"s":[{"i":[[-5.277,0],[-0.118,-10.643],[4.875,0.012],[-0.062,10.649]],"o":[[5.85,0.012],[0.088,10.399],[-5.238,-0.009],[0.07,-10.767]],"v":[[-150.531,36.3],[-139.006,53.575],[-147.306,71.35],[-159.156,53.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95,"s":[{"i":[[-5.675,0.055],[-0.236,-10.516],[5.375,-0.037],[-0.125,10.528]],"o":[[6.075,-0.038],[0.175,10.028],[-5.852,0.048],[0.14,-10.765]],"v":[[-135.188,33.788],[-123.137,51.088],[-131.988,68.887],[-144.438,51.188]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95.834,"s":[{"i":[[-6.073,0.11],[-0.353,-10.39],[5.875,-0.087],[-0.188,10.407]],"o":[[6.3,-0.087],[0.262,9.657],[-6.465,0.104],[0.211,-10.763]],"v":[[-121.771,31.306],[-109.196,48.631],[-118.596,66.456],[-131.646,48.656]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96.666,"s":[{"i":[[-6.472,0.164],[-0.471,-10.263],[6.375,-0.138],[-0.25,10.287]],"o":[[6.525,-0.137],[0.35,9.287],[-7.078,0.161],[0.281,-10.76]],"v":[[-109.104,28.825],[-96.004,46.175],[-105.954,64.025],[-119.604,46.125]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[-6.87,0.219],[-0.589,-10.137],[6.875,-0.188],[-0.312,10.166]],"o":[[6.75,-0.188],[0.438,8.916],[-7.691,0.218],[0.351,-10.758]],"v":[[-97.75,26.438],[-84.125,43.812],[-94.625,61.688],[-108.875,43.688]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":98.334,"s":[{"i":[[-7.268,0.274],[-0.706,-10.01],[7.375,-0.238],[-0.375,10.045]],"o":[[6.975,-0.238],[0.525,8.545],[-8.305,0.275],[0.421,-10.756]],"v":[[-87.767,24.188],[-73.617,41.588],[-84.667,59.488],[-99.517,41.387]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100,"s":[{"i":[[-8.065,0.383],[-0.942,-9.757],[8.375,-0.338],[-0.5,9.804]],"o":[[7.425,-0.337],[0.7,7.804],[-9.531,0.388],[0.562,-10.751]],"v":[[-70.425,19.688],[-55.225,37.137],[-67.375,55.088],[-83.425,36.788]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101.666,"s":[{"i":[[-8.862,0.492],[-1.177,-9.504],[9.375,-0.438],[-0.625,9.562]],"o":[[7.875,-0.438],[0.875,7.062],[-10.758,0.502],[0.702,-10.747]],"v":[[-56.375,15.188],[-40.125,32.688],[-53.375,50.688],[-70.625,32.188]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":103.334,"s":[{"i":[[-9.071,0.394],[-0.942,-9.584],[9.481,-0.35],[-0.5,9.631]],"o":[[8.281,-0.35],[0.7,7.631],[-10.588,0.402],[0.562,-10.579]],"v":[[-45.213,11.238],[-28.625,28.887],[-42.775,47.012],[-60.2,28.488]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105,"s":[{"i":[[-9.28,0.295],[-0.706,-9.665],[9.588,-0.262],[-0.375,9.7]],"o":[[8.688,-0.262],[0.525,8.2],[-10.417,0.301],[0.421,-10.411]],"v":[[-36.05,7.496],[-19.125,25.296],[-34.175,43.546],[-51.775,24.996]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105.834,"s":[{"i":[[-9.384,0.246],[-0.589,-9.705],[9.641,-0.219],[-0.312,9.735]],"o":[[8.891,-0.219],[0.438,8.485],[-10.332,0.251],[0.351,-10.327]],"v":[[-31.875,5.625],[-14.781,23.5],[-30.281,41.812],[-47.969,23.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107.5,"s":[{"i":[[-9.593,0.148],[-0.353,-9.786],[9.747,-0.131],[-0.188,9.803]],"o":[[9.297,-0.131],[0.262,9.053],[-10.162,0.151],[0.211,-10.159]],"v":[[-25.337,2.55],[-7.906,20.575],[-24.306,39.012],[-42.169,20.425]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":110,"s":[{"i":[[-9.907,0],[0,-9.907],[9.907,0],[0,9.907]],"o":[[9.907,0],[0,9.907],[-9.907,0],[0,-9.907]],"v":[[-17.125,-2.062],[0.812,16.188],[-16.938,34.812],[-35.062,16.188]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":114.166,"s":[{"i":[[-10.77,0],[0,-10.77],[10.77,0],[0,10.77]],"o":[[10.77,0],[0,10.77],[-10.77,0],[0,-10.77]],"v":[[-8.781,-9.156],[10.719,10.344],[-8.781,29.844],[-28.281,10.344]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118.334,"s":[{"i":[[-10.77,0],[0,-10.77],[10.77,0],[0,10.77]],"o":[[10.77,0],[0,10.77],[-10.77,0],[0,-10.77]],"v":[[-4.188,-13.375],[15.312,6.125],[-4.188,25.625],[-23.688,6.125]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":122.5,"s":[{"i":[[-10.77,0],[0,-10.77],[10.77,0],[0,10.77]],"o":[[10.77,0],[0,10.77],[-10.77,0],[0,-10.77]],"v":[[-1.562,-16.438],[17.938,3.062],[-1.562,22.562],[-21.062,3.062]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126.666,"s":[{"i":[[-10.77,0],[0,-10.77],[10.77,0],[0,10.77]],"o":[[10.77,0],[0,10.77],[-10.77,0],[0,-10.77]],"v":[[-0.188,-18.25],[19.312,1.25],[-0.188,20.75],[-19.688,1.25]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":130.834,"s":[{"i":[[-10.77,0],[0,-10.77],[10.77,0],[0,10.77]],"o":[[10.77,0],[0,10.77],[-10.77,0],[0,-10.77]],"v":[[0.5,-19.469],[20,0.031],[0.5,19.531],[-19,0.031]],"c":true}]},{"t":135,"s":[{"i":[[-10.77,0],[0,-10.77],[10.77,0],[0,10.77]],"o":[[10.77,0],[0,10.77],[-10.77,0],[0,-10.77]],"v":[[0.688,-20.062],[20.188,-0.562],[0.688,18.938],[-18.812,-0.562]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[385.5,-561.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":87,"op":342,"st":60,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"CENTER_SCREEN 3","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.312,149.969,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[699,-539],[699,539],[653,585],[-0.999,585],[-653,585],[-699,539],[-699,-539],[-653,-585],[1,-585],[653,-585]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64.166,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[707,-547],[707,547],[661,593],[-0.999,585],[-639.273,577],[-685.273,531],[-685.273,-527],[-639.273,-573],[1,-585],[661,-593]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[715,-563],[715,567],[669,613],[-0.999,585],[-617.545,561],[-663.545,515],[-663.545,-511],[-617.545,-557],[1,-585],[669,-609]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70.834,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[709,-577.4],[709,579],[663,625],[-0.999,585],[-587.709,551.4],[-636.709,505.4],[-636.709,-501.4],[-587.709,-547.4],[1,-585],[663,-623.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":72.5,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[705,-587],[705,587],[659,633],[-0.999,585],[-567.818,545],[-613.818,499],[-613.818,-495],[-567.818,-541],[1,-585],[659,-633]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":73.334,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[697.4,-593.4],[697.4,595],[651.4,641],[-0.999,585],[-553.873,539.4],[-599.873,493.4],[-599.873,-490.2],[-553.873,-536.2],[1,-585],[651.4,-639.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":74.166,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[693.8,-599.8],[693.8,603],[647.8,649],[-0.999,585],[-537.927,533.8],[-583.927,487.8],[-583.927,-485.4],[-537.927,-531.4],[1,-585],[647.8,-645.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":75,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[678.2,-606.2],[678.2,611],[632.2,657],[-0.999,585],[-519.982,528.2],[-565.982,482.2],[-565.982,-480.6],[-519.982,-526.6],[1,-585],[632.2,-652.2]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":75.834,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[666.6,-612.6],[666.6,619],[620.6,665],[-0.999,585],[-499.036,522.6],[-545.036,476.6],[-545.036,-475.8],[-499.036,-521.8],[1,-585],[620.6,-658.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[647,-619],[647,627],[601,673],[-0.999,585],[-478.091,517],[-524.091,471],[-524.091,-471],[-478.091,-517],[1,-585],[601,-665]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":78.334,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.223,2.5],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-22.441,-1.8],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[600.891,-638.2],[600.891,649.545],[561.618,691.182],[-5.908,586.273],[-428.856,507],[-467.174,466.273],[-467.174,-463],[-420.674,-508.5],[1,-585.6],[554.891,-684.2]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":79.166,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.132,3.75],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-20.96,-2.7],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[561.836,-647.8],[561.836,660.818],[525.927,700.273],[-8.363,586.909],[-398.739,502],[-433.216,463.909],[-433.216,-459],[-386.466,-504.25],[1,-585.9],[515.836,-693.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.041,5],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-19.478,-3.6],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[522.782,-657.4],[522.782,672.091],[490.236,709.364],[-10.817,587.546],[-368.621,497],[-399.258,461.546],[-399.258,-455],[-352.258,-500],[1,-586.2],[476.782,-703.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80.834,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-24.95,6.25],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-17.996,-4.5],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[471.727,-667],[471.727,683.364],[442.545,718.455],[-13.272,588.182],[-332.504,492],[-359.3,459.182],[-359.3,-451],[-312.05,-495.75],[1,-586.5],[425.727,-713]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[0.129,-24.588],[0,0],[21.663,1.508],[0,0],[0,0],[0.008,23.224],[0,0],[-21.2,5.875],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-15.329,-4.5],[0,0],[0,-25.405],[0,0],[0,0],[21.8,-2.083]],"v":[[412.352,-679.333],[412.352,694.197],[387.295,726.538],[-16.188,588.015],[-289.754,488.25],[-312.675,457.182],[-312.675,-449],[-272.55,-491.375],[-4.583,-587],[372.894,-720.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":82.5,"s":[{"i":[[0.258,-23.77],[0,0],[17.922,3.015],[0,0],[0,0],[0.017,21.043],[0,0],[-17.45,5.5],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-12.663,-4.5],[0,0],[0,-25.405],[0,0],[0,0],[18.194,-4.167]],"v":[[344.977,-691.667],[344.977,705.03],[324.045,734.621],[-19.105,587.849],[-247.004,484.5],[-266.05,455.182],[-266.05,-447],[-233.05,-487],[-10.166,-587.5],[312.06,-728.333]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":83.334,"s":[{"i":[[0.386,-22.953],[0,0],[14.18,4.523],[0,0],[0,0],[0.025,18.862],[0,0],[-13.7,5.125],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-9.996,-4.5],[0,0],[0,-25.405],[0,0],[0,0],[14.589,-6.25]],"v":[[269.602,-704],[269.602,715.864],[252.795,742.705],[-22.022,587.682],[-204.254,480.75],[-219.425,453.182],[-219.425,-445],[-193.55,-482.625],[-15.75,-588],[243.227,-736]],"c":true}]},{"t":85.833984375,"s":[{"i":[[0.773,-20.5],[0,0],[2.955,9.045],[0,0],[0,0],[0.05,12.318],[0,0],[-2.45,4],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-1.996,-4.5],[0,0],[0,-25.405],[0,0],[0,0],[3.773,-12.5]],"v":[[13.477,-707],[13.477,713.364],[9.045,731.955],[-30.772,587.182],[-58.004,481.5],[-61.55,459.182],[-61.55,-451],[-57.05,-481.5],[-32.5,-581.5],[6.727,-725]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":87,"st":60,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Figure 10","tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":72.5,"s":[100]},{"t":80.833984375,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[206.959,124.54,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[206.959,124.54,0],"to":[-1.125,0,0],"ti":[1.125,0,0]},{"t":85.833984375,"s":[200.209,124.54,0]}],"ix":2,"l":2},"a":{"a":0,"k":[270.209,145.54,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.55,0.55,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,-0.47]},"t":60,"s":[125,125,100]},{"t":110,"s":[125,125,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,0],[-1.939,-1.387],[6.061,-1.3],[0.907,1.308]],"o":[[1.066,0.711],[2.423,1.734],[-4.85,1.04],[0,0]],"v":[[-0.582,-6.237],[5.035,-2.37],[2.01,5.979],[-6.77,3.354]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[0,0],[-1.877,-1.382],[5.87,-1.295],[0.878,1.303]],"o":[[1.032,0.708],[2.346,1.727],[-4.697,1.036],[0,0]],"v":[[-0.131,-5.771],[5.309,-1.919],[2.379,6.397],[-6.124,3.782]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[0,0],[-1.627,-1.365],[5.086,-1.278],[0.761,1.287]],"o":[[0.894,0.699],[2.033,1.706],[-4.07,1.023],[0,0]],"v":[[1.024,-4.856],[5.738,-1.053],[3.2,7.16],[-4.168,4.578]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[0,0],[-0.79,-1.365],[2.613,-0.951],[0.598,1.216]],"o":[[0.434,0.699],[0.987,1.706],[-1.617,0.588],[0,0]],"v":[[-0.98,-5.389],[1.308,-1.586],[-0.339,6.331],[-4.212,3.985]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[0,0],[-0.387,-1.365],[1.28,-0.951],[0.293,1.216]],"o":[[0.213,0.699],[0.483,1.706],[-0.792,0.588],[0,0]],"v":[[-1.492,-5.389],[-0.371,-1.586],[-1.178,6.331],[-3.075,3.985]],"c":false}]},{"t":85.833984375,"s":[{"i":[[0,0],[-0.123,-1.365],[0.408,-0.951],[0.093,1.216]],"o":[[0.068,0.699],[0.154,1.706],[-0.253,0.588],[0,0]],"v":[[-2,-5.389],[-1.642,-1.586],[-1.9,6.331],[-2.505,3.985]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.596078455448,0.321568638086,0.239215686917,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.12,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[272.026,152.086],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[-3.926,7.277],[-8.848,0],[-3.783,2.669],[18.309,-0.059],[0.81,-15.17],[-4.916,0]],"o":[[3.926,7.277],[4.982,0],[-0.845,-15.213],[-18.267,0.059],[3.755,2.604],[8.848,0]],"v":[[-0.106,3.616],[20.359,15.839],[33.729,11.589],[-0.12,-15.891],[-33.793,11.7],[-20.571,15.839]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[-3.803,7.248],[-8.57,0],[-3.664,2.658],[17.733,-0.059],[0.785,-15.11],[-4.761,0]],"o":[[3.803,7.248],[4.825,0],[-0.819,-15.153],[-17.692,0.058],[3.637,2.594],[8.57,0]],"v":[[0.379,4.215],[20.2,16.39],[33.149,12.156],[0.365,-15.216],[-32.248,12.267],[-19.442,16.39]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[-3.295,7.158],[-7.426,0],[-3.175,2.625],[15.365,-0.058],[0.472,-17.15],[-4.125,0]],"o":[[3.295,7.158],[4.181,0],[-0.823,-14.189],[-15.33,0.058],[3.151,2.562],[7.426,0]],"v":[[-0.465,5.074],[17.661,15.91],[29.594,11.253],[1.662,-15.066],[-26.597,13.501],[-16.213,17.098]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[-1.951,7.158],[-4.397,0],[-1.88,2.625],[9.099,-0.058],[0.279,-17.15],[-2.443,0]],"o":[[1.951,7.158],[2.476,0],[-0.487,-14.189],[-9.078,0.058],[1.866,2.562],[4.397,0]],"v":[[-0.259,5.074],[10.475,15.91],[17.542,11.253],[1.001,-15.066],[-15.734,13.501],[-9.585,17.098]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[-0.956,7.158],[-2.154,0],[-0.921,2.625],[4.456,-0.058],[0.137,-17.15],[-1.196,0]],"o":[[0.956,7.158],[1.213,0],[-0.239,-14.189],[-4.446,0.058],[0.914,2.562],[2.154,0]],"v":[[-0.818,5.074],[4.203,15.91],[8.137,11.253],[0.036,-15.066],[-7.924,13.501],[-5.622,16.388]],"c":true}]},{"t":85.833984375,"s":[{"i":[[-0.305,7.158],[-0.687,0],[-0.294,2.625],[1.422,-0.058],[0.044,-17.15],[-0.382,0]],"o":[[0.305,7.158],[0.387,0],[-0.076,-14.189],[-1.419,0.058],[0.292,2.562],[0.687,0]],"v":[[-0.725,5.074],[0.877,15.91],[2.132,11.253],[-0.453,-15.066],[-2.992,13.501],[-2.258,16.388]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,9.805],[9.805,0],[0,-9.805],[-9.805,0]],"o":[[0,-9.805],[-9.805,0],[0,9.805],[9.805,0]],"v":[[-6.59,-22.526],[-24.345,-40.281],[-42.099,-22.526],[-24.345,-4.772]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[0,9.767],[9.497,0],[0,-9.767],[-9.497,0]],"o":[[0,-9.767],[-9.497,0],[0,9.767],[9.497,0]],"v":[[-5.901,-21.825],[-23.097,-39.51],[-40.292,-21.825],[-23.097,-4.14]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[0,9.646],[7.896,0],[0,-9.646],[-7.896,0]],"o":[[0,-9.646],[-7.896,0],[0,9.646],[7.896,0]],"v":[[-4.974,-20.167],[-18.558,-37.632],[-33.567,-20.167],[-19.984,-2.94]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[0,9.646],[4.638,0],[0,-9.646],[-4.639,0]],"o":[[0,-9.646],[-4.638,0],[0,9.646],[4.639,0]],"v":[[-2.828,-18.982],[-10.807,-36.448],[-19.625,-18.982],[-11.645,-1.755]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[0,9.435],[2.13,0],[0,-9.435],[-2.131,0]],"o":[[0,-9.435],[-2.13,0],[0,9.435],[2.131,0]],"v":[[-2.351,-18.418],[-6.016,-35.501],[-10.066,-18.418],[-6.401,-1.568]],"c":true}]},{"t":85.833984375,"s":[{"i":[[0,9.435],[0.68,0],[0,-9.435],[-0.68,0]],"o":[[0,-9.435],[-0.68,0],[0,9.435],[0.68,0]],"v":[[-1.214,-18.418],[-2.384,-35.501],[-3.676,-18.418],[-2.506,-1.568]],"c":true}]}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.337254911661,0.23137255013,0.129411771894,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[270.47,108.276],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Hair","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[-7.446,13.962],[0,0],[0,0],[-0.313,2.62],[0.015,4.351],[0,0],[-18.83,0.06],[-0.051,-15.806],[0,0],[-0.49,-2.669],[-1.873,-4.352],[-0.89,-3.988],[0,-4.307],[8.014,-5.508],[12.335,-0.04],[8.035,5.309],[0,7.994],[0,0]],"o":[[0,0],[0,0],[1.222,-4.479],[0.309,-2.598],[0,0],[-0.051,-15.806],[18.83,-0.06],[0,0],[0.013,4.276],[0.495,2.702],[3.604,8.387],[0.884,3.962],[0,7.459],[-7.997,5.495],[-12.35,0.04],[-8.04,-5.313],[0,0],[0,-6.072]],"v":[[-36.861,-4.598],[-36.804,-4.703],[-36.772,-4.819],[-34.61,-14.535],[-34.324,-24.06],[-34.324,-24.128],[-0.614,-53.129],[33.278,-24.345],[33.278,-24.252],[33.789,-14.641],[37.134,-4.922],[43.41,12.489],[44.307,23.718],[31.53,43.997],[0.092,53.129],[-31.449,44.589],[-44.307,24.009],[-44.307,23.977]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[-7.211,13.907],[0,0],[0,0],[-0.301,2.61],[0.014,4.334],[0,0],[-18.237,0.06],[-0.049,-15.744],[0,0],[-0.473,-2.659],[-1.812,-4.336],[-0.861,-3.972],[0,-4.29],[7.763,-5.486],[11.947,-0.04],[7.782,5.289],[0,7.963],[0,0]],"o":[[0,0],[0,0],[1.183,-4.461],[0.299,-2.588],[0,0],[-0.049,-15.745],[18.237,-0.06],[0,0],[0.012,4.259],[0.479,2.691],[3.491,8.354],[0.856,3.947],[0,7.43],[-7.746,5.474],[-11.961,0.04],[-7.787,-5.292],[0,0],[0,-6.048]],"v":[[-35.236,-4.113],[-35.18,-4.218],[-35.15,-4.333],[-33.055,-14.011],[-32.779,-23.499],[-32.779,-23.567],[-0.13,-52.454],[32.696,-23.783],[32.696,-23.69],[33.191,-14.117],[36.43,-4.435],[42.509,12.908],[43.377,24.093],[31.002,44.292],[0.554,53.389],[-29.994,44.882],[-42.447,24.383],[-42.447,24.35]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[-6.248,13.734],[0,0],[0,0],[-0.261,2.577],[0.012,4.28],[0,0],[-15.802,0.059],[-0.043,-15.549],[0,0],[-0.41,-2.626],[-1.57,-4.282],[-0.746,-3.923],[0,-4.237],[6.726,-5.417],[10.352,-0.039],[6.743,5.223],[0,7.864],[0,0]],"o":[[0,0],[0,0],[1.025,-4.406],[0.259,-2.556],[0,0],[-0.237,-16.662],[15.802,-0.059],[0,0],[0.011,4.206],[0.415,2.658],[3.025,8.25],[0.742,3.898],[0,7.337],[-6.712,5.406],[-10.364,0.039],[-6.747,-5.226],[0,0],[0,-5.973]],"v":[[-29.256,-3.138],[-29.208,-3.242],[-29.181,-3.355],[-27.366,-12.913],[-27.127,-22.283],[-27.127,-22.35],[1.163,-52.304],[29.606,-22.564],[29.606,-22.471],[30.035,-13.018],[32.842,-3.456],[38.109,13.671],[38.861,24.717],[28.139,44.665],[2.706,53.173],[-23.764,44.772],[-34.554,24.528],[-34.554,24.496]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[-3.7,13.734],[0,0],[0,0],[-0.155,2.577],[0.007,4.28],[0,0],[-9.358,0.059],[-0.025,-15.549],[0,0],[-0.243,-2.626],[-0.93,-4.282],[-0.442,-3.923],[0,-4.237],[3.983,-5.417],[6.13,-0.039],[3.993,5.223],[0,7.864],[0,0]],"o":[[0,0],[0,0],[0.607,-4.406],[0.153,-2.556],[0,0],[-0.14,-16.662],[9.358,-0.059],[0,0],[0.006,4.206],[0.246,2.658],[1.791,8.25],[0.439,3.898],[0,7.337],[-3.975,5.406],[-6.138,0.039],[-3.996,-5.226],[0,0],[0,-5.973]],"v":[[-17.523,-3.138],[-17.495,-3.242],[-17.479,-3.355],[-16.405,-12.913],[-16.263,-22.283],[-16.263,-22.35],[0.491,-52.304],[17.334,-22.564],[17.334,-22.471],[17.588,-13.018],[19.251,-3.456],[22.37,13.671],[22.815,24.717],[16.465,44.665],[1.404,53.173],[-14.271,44.772],[-20.661,24.528],[-20.661,24.496]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[-1.812,13.734],[0,0],[0,0],[-0.076,2.577],[0.004,4.28],[0,0],[-4.583,0.059],[-0.012,-15.549],[0,0],[-0.119,-2.626],[-0.455,-4.282],[-0.216,-3.923],[0,-4.237],[1.951,-5.417],[3.002,-0.039],[1.956,5.223],[0,7.864],[0,0]],"o":[[0,0],[0,0],[0.297,-4.406],[0.075,-2.556],[0,0],[-0.069,-16.662],[4.583,-0.059],[0,0],[0.003,4.206],[0.12,2.658],[0.877,8.25],[0.215,3.898],[0,7.337],[-1.947,5.406],[-3.006,0.039],[-1.957,-5.226],[0,0],[0,-5.973]],"v":[[-9.069,-3.138],[-9.055,-3.242],[-9.047,-3.355],[-8.521,-12.913],[-8.451,-22.283],[-8.451,-22.35],[-0.482,-52.304],[8.004,-22.564],[8.004,-22.471],[8.128,-13.018],[8.469,-3.22],[9.997,13.434],[10.215,24.48],[7.578,44.665],[0.202,53.173],[-7.476,44.772],[-10.605,24.528],[-10.605,24.496]],"c":true}]},{"t":85.833984375,"s":[{"i":[[-0.578,13.734],[0,0],[0,0],[-0.024,2.577],[0.001,4.28],[0,0],[-1.462,0.059],[-0.004,-15.549],[0,0],[-0.038,-2.626],[-0.145,-4.282],[-0.069,-3.923],[0,-4.237],[0.622,-5.417],[0.958,-0.039],[0.624,5.223],[0,7.864],[0,0]],"o":[[0,0],[0,0],[0.095,-4.406],[0.024,-2.556],[0,0],[-0.022,-16.662],[1.462,-0.059],[0,0],[0.001,4.206],[0.038,2.658],[0.28,8.25],[0.069,3.898],[0,7.337],[-0.621,5.406],[-0.959,0.039],[-0.624,-5.226],[0,0],[0,-5.973]],"v":[[-3.716,-3.138],[-3.711,-3.242],[-3.709,-3.355],[-3.541,-12.913],[-3.519,-22.283],[-3.519,-22.35],[-0.976,-52.304],[1.731,-22.564],[1.731,-22.471],[1.771,-13.018],[1.88,-3.22],[2.367,13.434],[2.437,24.48],[1.596,44.665],[-0.758,53.173],[-3.207,44.772],[-4.206,24.528],[-4.206,24.496]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[4.598,1.656],[-2.64,7.412],[-4.598,-1.656],[2.64,-7.412]],"o":[[-4.6,-1.657],[2.64,-7.412],[4.6,1.657],[-2.64,7.412]],"v":[[30.635,-0.09],[27.088,-16.51],[40.195,-26.931],[43.742,-10.511]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[4.453,1.649],[-2.557,7.383],[-4.453,-1.649],[2.557,-7.383]],"o":[[-4.455,-1.65],[2.557,-7.383],[4.455,1.65],[-2.557,7.383]],"v":[[30.136,0.377],[26.7,-15.979],[39.395,-26.359],[42.831,-10.003]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[3.958,1.629],[-2.273,7.291],[-3.958,-1.629],[2.273,-7.291]],"o":[[-3.96,-1.63],[2.273,-7.291],[3.96,1.63],[-2.273,7.291]],"v":[[26.809,-1.08],[23.755,-17.233],[35.038,-27.484],[38.092,-11.332]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[2.344,1.629],[-1.346,7.291],[-2.344,-1.629],[1.346,-7.291]],"o":[[-2.345,-1.63],[1.346,-7.291],[2.345,1.63],[-1.346,7.291]],"v":[[15.441,-1.791],[13.633,-17.943],[20.314,-28.195],[22.123,-12.042]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[1.148,1.629],[-0.659,7.291],[-1.148,-1.629],[0.659,-7.291]],"o":[[-1.149,-1.63],[0.659,-7.291],[1.148,1.63],[-0.659,7.291]],"v":[[7.076,-1.791],[6.191,-17.943],[9.463,-28.195],[10.349,-12.042]],"c":true}]},{"t":85.833984375,"s":[{"i":[[0.366,1.629],[-0.21,7.291],[-0.366,-1.629],[0.21,-7.291]],"o":[[-0.366,-1.63],[0.21,-7.291],[0.366,1.63],[-0.21,7.291]],"v":[[1.436,-1.791],[1.153,-17.943],[2.197,-28.195],[2.48,-12.042]],"c":true}]}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[4.598,-1.656],[2.64,7.412],[-4.598,1.656],[-2.64,-7.412]],"o":[[-4.6,1.657],[-2.64,-7.412],[4.6,-1.657],[2.64,7.412]],"v":[[-32.206,-0.025],[-45.315,-10.446],[-41.768,-26.866],[-28.659,-16.445]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[4.453,-1.65],[2.557,7.383],[-4.453,1.65],[-2.557,-7.383]],"o":[[-4.455,1.651],[-2.557,-7.383],[4.455,-1.651],[2.557,7.383]],"v":[[-30.727,0.442],[-43.424,-9.938],[-39.988,-26.294],[-27.292,-15.913]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[3.859,-1.629],[2.216,7.291],[-3.859,1.629],[-2.216,-7.291]],"o":[[-3.86,1.63],[-2.216,-7.291],[3.86,-1.63],[2.216,7.291]],"v":[[-25.349,1.36],[-36.351,-8.891],[-33.374,-25.043],[-22.372,-14.792]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[2.285,-1.629],[1.312,7.291],[-2.285,1.629],[-1.312,-7.291]],"o":[[-2.286,1.63],[-1.312,-7.291],[2.286,-1.63],[1.312,7.291]],"v":[[-14.973,2.071],[-21.488,-8.18],[-19.725,-24.332],[-13.21,-14.081]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[1.119,-1.629],[0.643,7.291],[-1.119,1.629],[-0.643,-7.291]],"o":[[-1.12,1.63],[-0.643,-7.291],[1.12,-1.63],[0.643,7.291]],"v":[[-7.819,2.071],[-11.01,-8.18],[-10.147,-24.332],[-6.956,-14.081]],"c":true}]},{"t":85.833984375,"s":[{"i":[[0.357,-1.629],[0.205,7.291],[-0.357,1.629],[-0.205,-7.291]],"o":[[-0.357,1.63],[-0.205,-7.291],[0.357,-1.63],[0.205,7.291]],"v":[[-3.317,2.071],[-4.335,-8.18],[-4.06,-24.332],[-3.042,-14.081]],"c":true}]}],"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431391716,0.403921574354,0.305882364511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[270.996,145.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Head","np":4,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[32.75,0],[9.05,-8.723],[0,0],[0,0],[12.963,11.172]],"o":[[-36.75,0],[-9.05,8.723],[0,0],[0,0],[-12.963,-11.172]],"v":[[269.5,203.875],[202.375,231.25],[180.49,265.037],[357.49,265.037],[335.375,229.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[31.944,0],[8.827,-8.723],[0,0],[0,0],[12.644,11.172]],"o":[[-35.846,0],[-8.827,8.723],[0,0],[0,0],[-12.644,-11.172]],"v":[[269.549,203.875],[204.076,231.25],[186.694,262.646],[356.307,266.949],[333.803,229.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[29.964,0.368],[8.765,-9.559],[0,0],[0,0],[10.827,12.493]],"o":[[-28.262,-0.347],[-8.765,9.559],[0,0],[0,0],[-10.827,-12.493]],"v":[[266.904,204.072],[217.226,226.01],[201.569,257.67],[350.704,277.632],[328.366,234.753]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[17.745,0.368],[4.241,-8.325],[0,0],[0,0],[6.509,11.788]],"o":[[-16.737,-0.347],[-4.241,8.325],[0,0],[0,0],[-6.509,-11.788]],"v":[[266.953,203.361],[239.192,224.352],[231.398,251.757],[320.169,284.732],[304.709,236.885]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[8.691,0.368],[2.251,-8.436],[0,0],[0,0],[2.817,13.796]],"o":[[-8.197,-0.347],[-2.251,8.436],[0,0],[0,0],[-2.817,-13.796]],"v":[[268.056,203.598],[254.994,222.223],[251.276,250.581],[295.322,287.805],[287.494,239.724]],"c":true}]},{"t":85.833984375,"s":[{"i":[[2.773,0.368],[0.492,-8.596],[0,0],[0,0],[0.699,12.45]],"o":[[-2.615,-0.347],[-0.492,8.596],[0,0],[0,0],[-0.699,-12.45]],"v":[[269.236,203.598],[265.068,222.223],[263.435,250.823],[278.044,286.855],[275.437,239.724]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.105865478516,0.450958251953,0.901947021484,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Body","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[403.855,266.954],[134.263,267.281],[133.746,34.114],[403.983,33.534]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[407.68,272.93],[141.435,262.261],[141.874,38.656],[408.046,28.992]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":72.5,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[405.196,278.275],[150.215,255.604],[150.652,44.48],[407.113,22.696]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[395.085,285.526],[168.051,250.854],[169.441,49.35],[394.738,15.446]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[372.271,290.107],[194.58,245.643],[195.101,57.011],[373.426,8.258]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[351.971,291.685],[212.112,242.326],[211.844,59.063],[352.098,8.576]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[310.1,293.583],[241.912,240.199],[241.881,60.48],[309.976,6.207]],"c":true}]},{"t":85.833984375,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[279.372,300.675],[263.178,242.092],[263.608,59.062],[278.826,1.479]],"c":true}]}],"ix":2},"nm":"Mask","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.815673828125,0.88232421875,0.980377197266,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Background","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":87,"st":60,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"CAMERA_CASING 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.312,149.969,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":83.334,"s":[{"i":[[0,0],[0,13.689],[2.562,0.203],[0,0],[0.235,-13.941],[0,0],[-0.817,-3.47],[0,3.644],[0,21.647],[0,11.447]],"o":[[0,0],[0,-11.515],[-0.278,-0.022],[0,0],[-0.157,9.303],[0,0],[3.866,-2.97],[0,-3.644],[0,-5.657],[0,-17.18]],"v":[[355.812,-582.217],[355.812,-651.939],[349.438,-666.703],[349.561,-458.372],[349.68,-455.309],[349.716,-452.543],[349.634,-442.155],[355.812,-452.231],[355.812,-527.897],[355.812,-557.197]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84,"s":[{"i":[[0,0],[0.181,13.689],[3.692,-3.049],[0,0],[0.235,-13.941],[0,0],[-0.817,-3.47],[0.115,3.644],[0.08,21.647],[0.011,11.447]],"o":[[0,0],[-0.152,-11.515],[-0.215,0.151],[0,0],[-0.157,9.303],[0,0],[3.866,-2.97],[-0.115,-3.644],[-0.021,-5.657],[-0.017,-17.18]],"v":[[291.635,-587.821],[292.523,-742.756],[283.027,-757.519],[282.695,-373.749],[282.814,-370.686],[282.85,-362.418],[282.768,-352.029],[291.612,-362.105],[291.605,-533.5],[291.592,-562.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":85,"s":[{"i":[[0,0],[0.355,13.689],[6.542,-0.922],[0,0],[0.37,-13.941],[0,0],[0.057,-3.845],[-0.362,3.644],[-0.253,21.647],[-0.035,11.447]],"o":[[0,0],[-0.298,-11.515],[-0.366,0.052],[0,0],[-0.247,9.303],[0,0],[5.807,-4.595],[0.362,-3.644],[0.066,-5.657],[0.053,-17.18]],"v":[[193.824,-593.342],[194.689,-728.224],[178.533,-742.988],[177.933,-357.043],[178.12,-353.042],[178.176,-343.402],[177.975,-332.388],[193.487,-347.215],[193.916,-523.397],[193.959,-552.697]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86,"s":[{"i":[[0,0],[0.484,13.689],[8.523,-3.859],[0,0],[0.505,-13.941],[0,0],[-1.753,-3.47],[-0.867,3.644],[-0.607,21.647],[-0.085,11.447]],"o":[[0,0],[-0.407,-11.515],[-0.429,0.194],[0,0],[-0.337,9.303],[0,0],[5.997,-7.47],[0.867,-3.644],[0.159,-5.657],[0.128,-17.18]],"v":[[75.679,-597.849],[75.758,-718.949],[53.719,-733.713],[53.693,-344.334],[53.947,-321.896],[54.024,-312.256],[53.705,-300.617],[75.218,-335.193],[75.9,-512.279],[76.004,-541.578]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87,"s":[{"i":[[0,0],[3.485,11.583],[8.955,-4.433],[-0.062,-3.091],[-3.221,-11.86],[0,0],[-2.544,-3.333],[-1.095,5.54],[-0.607,21.647],[3.44,10.582]],"o":[[0,0],[-3.366,-10.6],[-0.589,2.321],[0.062,3.09],[2.943,8.704],[0,0],[8.347,0.703],[0.794,-8.444],[0.159,-5.657],[-3.981,-16.052]],"v":[[0.007,-686.694],[-13.977,-729.743],[-42.282,-733.704],[-42.419,-351.824],[-35.506,-318.32],[-18.172,-282.143],[-17.207,-281.536],[4.303,-291.511],[7.882,-626.94],[5.009,-672.742]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88,"s":[{"i":[[0,0],[7.233,7.337],[10.246,-6.152],[-0.246,-12.344],[-9.136,-8.623],[0,0],[-4.913,-2.921],[-1.776,11.215],[-0.607,21.647],[9.871,10.62]],"o":[[0,0],[-7.601,-8.337],[-1.067,8.69],[0.246,12.344],[9.086,7.021],[0,0],[8.647,1.223],[0.576,-22.816],[0.159,-5.657],[-9.656,-13.055]],"v":[[-96.036,-712.675],[-118.855,-734.697],[-147.972,-735.599],[-146.863,-351.098],[-130.294,-314.045],[-110.979,-299.591],[-90.581,-288.487],[-69.43,-302.648],[-68.325,-642.738],[-74.855,-686.091]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,0],[9.484,3.793],[8.601,-12.274],[-0.441,-20.809],[-28.549,-9.001],[0,0],[-12.63,-1.571],[-1.485,15.967],[0.068,21.914],[19.53,9.541]],"o":[[0,0],[-23.711,-9.556],[-2.126,56.648],[0.441,20.809],[21.714,6.2],[0,0],[16.655,-5.885],[0.585,-24.671],[-0.31,-11.754],[-24.908,-11.845]],"v":[[-256.274,-730.569],[-286.913,-744.674],[-328.463,-731.93],[-328.345,-350.477],[-292.838,-292.848],[-262.739,-284.553],[-227.494,-275.913],[-199.734,-301.739],[-198.453,-680.107],[-223.793,-715.645]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93,"s":[{"i":[[0,0],[10.869,1.69],[0.025,-8.377],[-0.642,-28.87],[-23.311,-3.439],[0,0],[-22.891,-2.452],[-0.707,20.252],[1.081,22.315],[30.895,7.44]],"o":[[0,0],[-26.095,-4.058],[-0.004,1.43],[0.642,28.87],[18.067,2.665],[0,0],[13.227,-9.056],[0.707,-20.252],[-1.012,-20.899],[-21.169,-6.833]],"v":[[-441.081,-726.063],[-476.791,-733.581],[-512.894,-713.271],[-514.67,-343.981],[-483.267,-295.903],[-442.821,-290.271],[-385.72,-282.768],[-366.552,-319.504],[-367.271,-675.98],[-402.702,-717.321]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95,"s":[{"i":[[0,0],[10.926,1.148],[0.925,-11.98],[-0.642,-28.87],[-25.802,-3.644],[0,0],[-22.916,-2.187],[-0.707,20.252],[1.081,22.315],[31.032,6.862]],"o":[[0,0],[-23.328,-2.596],[-0.085,1.425],[0.642,28.87],[22.11,3.111],[0,0],[17.386,-3.626],[0.707,-20.252],[-1.012,-20.899],[-22.091,-5.737]],"v":[[-499.893,-697.032],[-539.989,-704.551],[-576.438,-678.34],[-576.108,-326.385],[-541.208,-279.351],[-496.322,-272.988],[-445.018,-267.521],[-419.062,-304.309],[-419.608,-647.001],[-459.425,-688.969]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97,"s":[{"i":[[0,0],[10.983,0.606],[1.825,-15.582],[-0.642,-28.87],[-28.293,-3.849],[0,0],[-22.941,-1.921],[-0.707,20.252],[1.081,22.315],[31.169,6.285]],"o":[[0,0],[-20.561,-1.134],[-0.166,1.42],[0.642,28.87],[26.153,3.558],[0,0],[21.546,1.805],[0.707,-20.252],[-1.012,-20.899],[-23.012,-4.64]],"v":[[-551.186,-680.532],[-595.667,-688.051],[-632.464,-655.94],[-635.041,-321.32],[-596.641,-275.331],[-547.318,-268.237],[-501.809,-264.806],[-469.065,-301.646],[-464.426,-630.554],[-508.629,-673.148]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":99,"s":[{"i":[[0,0],[10.983,0.606],[1.825,-15.582],[-0.642,-28.87],[-28.293,-3.849],[0,0],[-22.942,-1.921],[-0.707,20.252],[1.081,22.315],[31.169,6.285]],"o":[[0,0],[-20.561,-1.134],[-0.166,1.42],[0.642,28.87],[26.153,3.558],[0,0],[21.546,1.805],[0.707,-20.252],[-1.012,-20.899],[-23.012,-4.64]],"v":[[-580.634,-669.881],[-625.743,-675.52],[-662.539,-643.409],[-665.116,-312.548],[-626.717,-266.559],[-578.646,-260.091],[-531.884,-256.034],[-499.14,-292.875],[-493.875,-620.529],[-536.825,-663.123]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101,"s":[{"i":[[0,0],[10.983,0.606],[1.825,-15.582],[-0.642,-28.87],[-28.293,-3.849],[0,0],[-22.941,-1.921],[-0.707,20.252],[1.081,22.315],[31.169,6.285]],"o":[[0,0],[-20.561,-1.134],[-0.166,1.42],[0.642,28.87],[26.153,3.558],[0,0],[21.546,1.805],[0.707,-20.252],[-1.012,-20.899],[-23.012,-4.64]],"v":[[-607.577,-656.723],[-648.299,-660.483],[-685.096,-628.371],[-687.672,-308.789],[-649.273,-262.799],[-602.456,-256.958],[-554.441,-252.274],[-521.697,-289.115],[-523.323,-607.997],[-565.02,-650.592]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104,"s":[{"i":[[0,0],[10.983,0.606],[1.825,-15.582],[-0.642,-28.87],[-28.293,-3.849],[0,0],[-21.06,0.551],[-0.707,20.252],[1.081,22.315],[29.62,2.879]],"o":[[0,0],[-20.561,-1.134],[-0.166,1.42],[0.642,28.87],[26.153,3.558],[0,0],[21.614,-0.566],[0.707,-20.252],[-1.012,-20.899],[-23.365,-2.271]],"v":[[-621.361,-629.154],[-663.337,-631.66],[-700.133,-599.549],[-700.203,-298.764],[-661.804,-252.774],[-612.481,-248.187],[-565.719,-246.009],[-535.481,-286.609],[-538.361,-582.935],[-580.058,-625.529]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107,"s":[{"i":[[0,0],[10.983,0.606],[1.825,-15.582],[-0.642,-28.87],[-28.293,-3.849],[0,0],[-21.06,0.551],[-0.707,20.252],[1.081,22.315],[29.62,2.879]],"o":[[0,0],[-20.561,-1.134],[-0.166,1.42],[0.642,28.87],[26.153,3.558],[0,0],[21.614,-0.566],[0.707,-20.252],[-1.012,-20.899],[-23.365,-2.271]],"v":[[-620.108,-617.876],[-663.337,-619.129],[-700.133,-587.018],[-702.71,-292.498],[-664.311,-246.509],[-614.987,-244.427],[-570.731,-243.502],[-537.987,-280.343],[-535.855,-572.91],[-577.551,-615.504]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111,"s":[{"i":[[0,0],[10.983,0.606],[1.825,-15.582],[-0.642,-28.87],[-28.457,-2.338],[0,0],[-21.06,0.551],[-0.707,20.252],[1.081,22.315],[29.62,2.879]],"o":[[0,0],[-20.561,-1.134],[-0.166,1.42],[0.642,28.87],[28.045,2.304],[0,0],[21.614,-0.566],[0.707,-20.252],[-1.012,-20.899],[-23.365,-2.271]],"v":[[-620.108,-605.344],[-663.337,-605.345],[-700.133,-573.233],[-699.589,-281.22],[-661.19,-235.231],[-614.372,-231.896],[-570.117,-230.971],[-537.373,-267.812],[-542.12,-561.631],[-576.298,-602.973]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[0,0],[10.983,0.606],[1.825,-15.582],[-0.642,-28.87],[-28.524,-1.302],[0,0],[-21.06,0.551],[-0.707,20.252],[1.081,22.315],[29.62,2.879]],"o":[[0,0],[-20.561,-1.134],[-0.166,1.42],[0.642,28.87],[23.032,1.051],[0,0],[21.614,-0.566],[0.707,-20.252],[-1.012,-20.899],[-23.365,-2.271]],"v":[[-615.095,-592.813],[-658.324,-592.813],[-695.121,-560.702],[-694.577,-276.207],[-656.177,-230.218],[-610.613,-229.39],[-568.864,-229.718],[-536.12,-266.559],[-535.855,-549.1],[-572.539,-591.695]],"c":true}]},{"t":126,"s":[{"i":[[0,0],[10.983,0.606],[1.825,-15.582],[-0.642,-28.87],[-28.524,-1.302],[0,0],[-21.06,0.551],[-0.707,20.252],[1.081,22.315],[29.62,2.879]],"o":[[0,0],[-20.561,-1.134],[-0.166,1.42],[0.642,28.87],[23.032,1.051],[0,0],[21.614,-0.566],[0.707,-20.252],[-1.012,-20.899],[-23.365,-2.271]],"v":[[-605.07,-581.535],[-648.299,-581.535],[-685.096,-549.424],[-685.805,-263.676],[-647.406,-217.687],[-601.841,-216.858],[-560.092,-217.187],[-527.348,-259.04],[-528.336,-541.582],[-562.514,-580.417]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294118524,0.250980407,0.262745112181,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":84,"op":342,"st":60,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"FRONT_SCREEN 4","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.312,149.969,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[0.25,-9.25],[0,0],[0.25,-0.5],[0,0],[0.5,9.25],[0,0],[-2,-2.5],[0,0]],"o":[[0,0],[0.25,3],[0,0],[-2,2.75],[0,0],[0,-16.016],[0,0],[1.75,3.5]],"v":[[-308,-465.5],[-308,477.25],[-308.25,483],[-356.25,568.25],[-364,573.75],[-364,-560.5],[-354.5,-565],[-310.5,-483]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88.334,"s":[{"i":[[1,-13],[0,0],[5,-5],[0,0],[0,16.016],[0,0],[-18,-17],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-11.25,9],[0,0],[0,-16.016],[0,0],[5.5,4.5]],"v":[[-257,-464],[-257,468],[-270.5,493.062],[-345.5,568.5],[-358.25,553.75],[-357,-542.5],[-338,-559],[-265.5,-485.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":89.166,"s":[{"i":[[0.667,-14.005],[0,0],[8.672,-3.333],[0,0],[0,16.016],[0,0],[-17.339,-11.333],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-12.839,6],[0,0],[0,-16.016],[0,0],[9.005,3]],"v":[[-210.667,-462.333],[-210.667,466],[-229.333,495.708],[-334.833,568.667],[-353,549.167],[-352.167,-541.5],[-329.833,-562.167],[-226,-489.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0.333,-15.011],[0,0],[12.344,-1.667],[0,0],[0,16.016],[0,0],[-16.677,-5.667],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-14.427,3],[0,0],[0,-16.016],[0,0],[12.511,1.5]],"v":[[-167.833,-460.667],[-167.833,464],[-191.667,498.354],[-324.167,568.833],[-347.75,544.583],[-347.333,-540.5],[-321.667,-565.333],[-190,-493.833]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90.834,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-127,-459],[-127,462],[-156,501],[-313.5,569],[-342.5,540],[-342.5,-539.5],[-313.5,-568.5],[-156,-498]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":91.666,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-89.667,-464.667],[-89.667,467.333],[-118.667,503],[-308.667,571.333],[-337.667,542.333],[-337.667,-541.333],[-308.667,-570.333],[-118.667,-500.333]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":92.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-54.833,-470.333],[-54.833,472.667],[-83.833,505],[-303.833,573.667],[-332.833,544.667],[-332.833,-543.167],[-303.833,-572.167],[-83.833,-502.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-24,-476],[-24,478],[-53,507],[-299,576],[-328,547],[-328,-545],[-299,-574],[-53,-505]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":94.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[6.073,-479.4],[6.073,481.5],[-22.927,510.5],[-297.12,577.1],[-326.12,548.1],[-326.12,-546.2],[-297.12,-575.2],[-22.927,-508.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[32.647,-482.8],[32.647,485],[3.647,514],[-295.24,578.2],[-324.24,549.2],[-324.24,-547.4],[-295.24,-576.4],[3.647,-511.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95.834,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[57.22,-486.2],[57.22,488.5],[28.22,517.5],[-293.36,579.3],[-322.36,550.3],[-322.36,-548.6],[-293.36,-577.6],[28.22,-515.2]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96.666,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[79.46,-489.6],[79.46,492],[50.46,521],[-291.48,580.4],[-320.48,551.4],[-320.48,-549.8],[-291.48,-578.8],[50.46,-518.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[99.7,-493],[99.7,495.5],[70.7,524.5],[-289.6,581.5],[-318.6,552.5],[-318.6,-551],[-289.6,-580],[70.7,-522]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":99.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[136.58,-499.8],[136.58,502.5],[107.58,531.5],[-285.84,583.7],[-314.84,554.7],[-314.84,-553.4],[-285.84,-582.4],[107.58,-528.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101.666,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[179.4,-510],[179.4,513],[150.4,542],[-280.2,587],[-309.2,558],[-309.2,-557],[-280.2,-586],[150.4,-539]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":103.334,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[201.28,-516],[201.28,518.8],[172.28,547.8],[-279.44,588.2],[-308.44,559.2],[-308.44,-558],[-279.44,-587],[172.28,-545]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[212.053,-519],[212.053,521.7],[183.053,550.7],[-279.06,588.8],[-308.06,559.8],[-308.06,-558.5],[-279.06,-587.5],[183.053,-548]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[220.827,-522],[220.827,524.6],[191.827,553.6],[-278.68,589.4],[-307.68,560.4],[-307.68,-559],[-278.68,-588],[191.827,-551]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105.834,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[229.1,-525],[229.1,527.5],[200.1,556.5],[-278.3,590],[-307.3,561],[-307.3,-559.5],[-278.3,-588.5],[200.1,-554]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":109.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[256.46,-535.4],[256.46,537.5],[227.46,566.5],[-276.78,592.4],[-305.78,563.4],[-305.78,-561.5],[-276.78,-590.5],[227.46,-564.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":110,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[260.8,-538],[260.8,540],[231.8,569],[-276.4,593],[-305.4,564],[-305.4,-562],[-276.4,-591],[231.8,-567]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":112.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[273.82,-543.7],[273.82,546],[244.82,575],[-276.16,594.2],[-305.16,565.2],[-305.16,-562.6],[-276.16,-591.6],[244.82,-572.7]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":114.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[280.833,-547.5],[280.833,550],[251.833,579],[-276,595],[-305,566],[-305,-563],[-276,-592],[251.833,-576.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[294.867,-555],[294.867,558],[265.867,587],[-275.6,597],[-304.6,568],[-304.6,-564],[-275.6,-593],[265.867,-584]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":122.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[301.9,-559.5],[301.9,563],[272.9,592],[-275.7,597],[-304.7,568],[-304.7,-565],[-275.7,-594],[272.9,-588.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[306.933,-564],[306.933,568],[277.933,597],[-275.8,597],[-304.8,568],[-304.8,-566],[-275.8,-595],[277.933,-593]],"c":true}]},{"t":135,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[310,-568],[310,570],[281,599],[-276,599],[-305,570],[-305,-568],[-276,-597],[281,-597]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.815686285496,0.882352948189,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[384,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[102,102],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":88,"op":342,"st":60,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Figure 7","tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":93.334,"s":[0]},{"t":101.666015625,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[207.459,144.79,0],"to":[0.312,0,0],"ti":[-1.146,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62.779,"s":[209.334,144.79,0],"to":[1.146,0,0],"ti":[-2.125,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65.555,"s":[214.334,144.79,0],"to":[2.125,0,0],"ti":[-2.812,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[222.084,144.79,0],"to":[2.812,0,0],"ti":[-4.396,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":71.111,"s":[231.209,144.79,0],"to":[4.396,0,0],"ti":[-5.104,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[248.459,144.79,0],"to":[5.104,0,0],"ti":[-3.812,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":82.223,"s":[261.834,144.79,0],"to":[3.812,0,0],"ti":[-2.667,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.779,"s":[271.334,144.79,0],"to":[2.667,0,0],"ti":[-1.812,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[277.834,144.79,0],"to":[1.812,0,0],"ti":[-1.083,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":98.889,"s":[282.209,144.79,0],"to":[1.083,0,0],"ti":[-0.458,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.445,"s":[284.334,144.79,0],"to":[0.458,0,0],"ti":[-0.104,0,0]},{"t":110,"s":[284.959,144.79,0]}],"ix":2,"l":2},"a":{"a":0,"k":[270.209,145.54,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.55,0.55,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.001]},"t":60,"s":[108,108,100]},{"t":110,"s":[106.5,106.5,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[0,0],[-0.095,-1.387],[0.299,-1.3],[0.045,1.308]],"o":[[0.052,0.711],[0.119,1.734],[-0.239,1.04],[0,0]],"v":[[-57.799,-6.034],[-57.522,-2.167],[-57.671,6.182],[-58.104,3.557]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[0,0],[-0.232,-1.387],[0.725,-1.3],[0.109,1.308]],"o":[[0.127,0.711],[0.29,1.734],[-0.58,1.04],[0,0]],"v":[[-53.069,-6.034],[-52.397,-2.167],[-52.759,6.182],[-53.809,3.557]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,0],[-0.59,-1.387],[1.846,-1.3],[0.276,1.308]],"o":[[0.325,0.711],[0.738,1.734],[-1.477,1.04],[0,0]],"v":[[-41.097,-6.034],[-39.386,-2.167],[-40.307,6.182],[-42.981,3.557]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[0,0],[-0.953,-1.387],[2.979,-1.3],[0.446,1.308]],"o":[[0.524,0.711],[1.191,1.734],[-2.384,1.04],[0,0]],"v":[[-29.746,-6.034],[-26.986,-2.167],[-28.473,6.182],[-32.788,3.557]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[0,0],[-1.277,-1.387],[3.993,-1.3],[0.598,1.308]],"o":[[0.702,0.711],[1.596,1.734],[-3.195,1.04],[0,0]],"v":[[-19.728,-6.034],[-16.028,-2.167],[-18.021,6.182],[-23.805,3.557]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100.834,"s":[{"i":[[0,0],[-1.471,-1.387],[4.599,-1.3],[0.688,1.308]],"o":[[0.809,0.711],[1.838,1.734],[-3.68,1.04],[0,0]],"v":[[-13.958,-6.034],[-9.695,-2.167],[-11.991,6.182],[-18.654,3.557]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[0,0],[-1.612,-1.387],[5.042,-1.3],[0.754,1.308]],"o":[[0.886,0.711],[2.015,1.734],[-4.034,1.04],[0,0]],"v":[[-9.542,-6.034],[-4.87,-2.167],[-7.386,6.182],[-14.689,3.557]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107.5,"s":[{"i":[[0,0],[-1.826,-1.387],[5.71,-1.3],[0.854,1.308]],"o":[[1.004,0.711],[2.282,1.734],[-4.569,1.04],[0,0]],"v":[[-8.492,-6.268],[-3.201,-2.402],[-6.051,5.947],[-14.321,3.322]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111.666,"s":[{"i":[[0,0],[-1.802,-1.387],[5.635,-1.3],[0.843,1.308]],"o":[[0.991,0.711],[2.252,1.734],[-4.509,1.04],[0,0]],"v":[[-5.336,-6.268],[-0.114,-2.402],[-2.926,5.947],[-11.089,3.322]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[0,0],[-1.899,-1.387],[5.939,-1.3],[0.889,1.308]],"o":[[1.044,0.711],[2.374,1.734],[-4.752,1.04],[0,0]],"v":[[-2.334,-6.268],[3.169,-2.402],[0.205,5.947],[-8.397,3.322]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[0,0],[-1.939,-1.387],[6.061,-1.3],[0.907,1.308]],"o":[[1.066,0.711],[2.423,1.734],[-4.85,1.04],[0,0]],"v":[[-0.934,-6.268],[4.683,-2.402],[1.658,5.947],[-7.122,3.322]],"c":false}]},{"t":135,"s":[{"i":[[0,0],[-1.939,-1.387],[6.061,-1.3],[0.907,1.308]],"o":[[1.066,0.711],[2.423,1.734],[-4.85,1.04],[0,0]],"v":[[-0.582,-6.237],[5.035,-2.37],[2.01,5.979],[-6.77,3.354]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.596078455448,0.321568638086,0.239215686917,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.12,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[272.026,152.086],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[-0.193,7.277],[-0.436,0],[-0.204,3.66],[0.902,-0.059],[0.071,-14.068],[-0.242,0]],"o":[[0.15,8.062],[0.245,0],[-0.171,-14.63],[-0.9,0.059],[0.185,2.604],[0.436,0]],"v":[[-56.249,5.695],[-55.215,18.856],[-54.713,13.904],[-56.412,-10.293],[-57.945,12.606],[-57.189,17.449]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[-0.47,7.277],[-1.059,0],[-0.496,3.66],[2.191,-0.059],[0.174,-14.068],[-0.588,0]],"o":[[0.364,8.062],[0.596,0],[-0.415,-14.63],[-2.185,0.059],[0.449,2.604],[1.059,0]],"v":[[-51.53,5.695],[-49.016,18.856],[-47.797,13.904],[-51.926,-10.293],[-55.649,12.606],[-53.813,17.449]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[-1.196,7.277],[-2.695,0],[-1.262,3.66],[5.577,-0.059],[0.442,-14.068],[-1.497,0]],"o":[[0.928,8.062],[1.517,0],[-1.057,-14.63],[-5.564,0.059],[1.144,2.604],[2.695,0]],"v":[[-39.584,5.695],[-33.184,18.856],[-30.08,13.904],[-40.592,-10.293],[-50.072,12.606],[-45.396,17.449]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[-1.93,7.277],[-4.349,0],[-1.859,2.669],[8.999,-0.059],[0.713,-14.068],[-2.416,0]],"o":[[1.497,8.062],[2.449,0],[-1.705,-14.63],[-8.978,0.059],[1.845,2.604],[4.349,0]],"v":[[-28.26,5.461],[-18.311,18.388],[-12.925,13.904],[-29.887,-10.293],[-45.183,12.606],[-38.394,17.215]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[-2.586,7.277],[-5.829,0],[-2.492,2.669],[12.062,-0.059],[0.956,-14.068],[-3.238,0]],"o":[[2.007,8.062],[3.282,0],[-0.557,-15.213],[-12.034,0.059],[2.474,2.604],[5.829,0]],"v":[[-18.266,5.461],[-4.931,18.388],[2.289,13.904],[-20.446,-11.464],[-40.949,12.606],[-31.849,17.215]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100.834,"s":[{"i":[[-2.979,7.277],[-6.715,0],[-2.871,2.669],[13.894,-0.059],[0.615,-15.17],[-3.73,0]],"o":[[2.311,8.062],[3.781,0],[-0.642,-15.213],[-13.862,0.059],[2.849,2.604],[6.715,0]],"v":[[-12.51,4.992],[2.851,17.685],[11.168,13.904],[-15.292,-12.168],[-38.639,12.606],[-28.157,16.746]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[-3.266,7.277],[-7.36,0],[-3.147,2.669],[15.23,-0.059],[0.674,-15.17],[-4.089,0]],"o":[[2.534,8.062],[4.144,0],[-0.703,-15.213],[-15.194,0.059],[3.123,2.604],[7.36,0]],"v":[[-8.876,4.523],[7.962,17.215],[17.592,12.496],[-10.382,-13.106],[-37.517,11.903],[-26.284,16.511]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107.5,"s":[{"i":[[-3.478,7.277],[-7.839,0],[-3.352,2.669],[16.221,-0.059],[0.718,-15.17],[-4.355,0]],"o":[[3.478,7.277],[4.414,0],[-0.749,-15.213],[-16.184,0.059],[3.327,2.604],[7.839,0]],"v":[[-5.981,4.288],[11.703,16.746],[21.96,12.496],[-7.336,-13.576],[-36.487,11.903],[-24.772,16.511]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111.666,"s":[{"i":[[-3.65,7.277],[-8.226,0],[-3.517,2.669],[17.022,-0.059],[0.753,-15.17],[-4.57,0]],"o":[[3.65,7.277],[4.632,0],[-0.786,-15.213],[-16.983,0.059],[3.491,2.604],[8.226,0]],"v":[[-3.376,3.819],[15.182,16.511],[26.438,12.496],[-4.797,-14.514],[-35.635,11.668],[-23.342,16.511]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[-3.847,7.277],[-8.67,0],[-3.707,2.669],[17.939,-0.059],[0.794,-15.17],[-4.816,0]],"o":[[3.847,7.277],[4.881,0],[-0.828,-15.213],[-17.898,0.059],[3.679,2.604],[8.67,0]],"v":[[-1.836,3.584],[18.215,15.807],[31.315,11.557],[-1.85,-15.923],[-34.843,11.668],[-21.888,15.807]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[-3.926,7.277],[-8.848,0],[-3.783,2.669],[18.309,-0.059],[0.81,-15.17],[-4.916,0]],"o":[[3.926,7.277],[4.982,0],[-0.845,-15.213],[-18.267,0.059],[3.755,2.604],[8.848,0]],"v":[[-0.458,3.584],[20.007,15.807],[33.377,11.557],[-0.472,-15.923],[-34.145,11.668],[-20.923,15.807]],"c":true}]},{"t":135,"s":[{"i":[[-3.926,7.277],[-8.848,0],[-3.783,2.669],[18.309,-0.059],[0.81,-15.17],[-4.916,0]],"o":[[3.926,7.277],[4.982,0],[-0.845,-15.213],[-18.267,0.059],[3.755,2.604],[8.848,0]],"v":[[-0.106,3.616],[20.359,15.839],[33.729,11.589],[-0.12,-15.891],[-33.793,11.7],[-20.571,15.839]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[0,9.283],[0.481,0],[0,-9.283],[-0.481,0]],"o":[[0,-9.283],[-0.481,0],[0,9.283],[0.481,0]],"v":[[-56.552,-17.322],[-57.474,-33.907],[-58.358,-17.994],[-57.435,-0.961]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[0,9.283],[1.168,0],[0,-9.283],[-1.168,0]],"o":[[0,-9.283],[-1.168,0],[0,9.283],[1.168,0]],"v":[[-52.264,-17.322],[-54.505,-33.907],[-56.652,-17.994],[-54.41,-0.961]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,9.283],[2.973,0],[0,-9.283],[-2.973,0]],"o":[[0,-9.283],[-2.973,0],[0,9.283],[2.973,0]],"v":[[-41.453,-17.322],[-47.159,-33.907],[-52.623,-17.994],[-46.917,-0.961]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[0,9.283],[4.797,0],[0,-9.283],[-4.797,0]],"o":[[0,-9.283],[-4.797,0],[0,9.283],[4.797,0]],"v":[[-31.654,-18.258],[-40.861,-34.843],[-49.678,-18.931],[-40.471,-1.898]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[0,9.532],[6.43,0],[0,-9.532],[-6.43,0]],"o":[[0,-9.532],[-6.43,0],[0,9.532],[6.43,0]],"v":[[-22.815,-18.985],[-35.156,-36.015],[-46.974,-19.676],[-34.633,-2.186]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100.834,"s":[{"i":[[0,9.532],[7.407,0],[0,-9.532],[-7.407,0]],"o":[[0,-9.532],[-7.407,0],[0,9.532],[7.407,0]],"v":[[-18.02,-19.689],[-32.236,-36.718],[-45.85,-20.379],[-31.633,-2.89]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[0,9.723],[8.119,0],[0,-9.723],[-8.119,0]],"o":[[0,-9.723],[-8.119,0],[0,9.723],[8.119,0]],"v":[[-14.659,-20.051],[-30.241,-37.422],[-45.163,-20.755],[-29.58,-2.915]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107.5,"s":[{"i":[[0,9.723],[8.648,0],[0,-9.723],[-8.648,0]],"o":[[0,-9.723],[-8.648,0],[0,9.723],[8.648,0]],"v":[[-11.89,-20.755],[-28.488,-38.126],[-44.381,-21.459],[-27.784,-3.619]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111.666,"s":[{"i":[[0,9.723],[9.075,0],[0,-9.723],[-9.075,0]],"o":[[0,-9.723],[-9.075,0],[0,9.723],[9.075,0]],"v":[[-10.316,-21.459],[-26.748,-39.065],[-43.18,-21.459],[-26.748,-3.854]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[0,9.805],[9.607,0],[0,-9.805],[-9.607,0]],"o":[[0,-9.805],[-9.607,0],[0,9.805],[9.607,0]],"v":[[-8.19,-22.558],[-25.585,-40.312],[-42.981,-22.558],[-25.585,-4.804]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[0,9.805],[9.805,0],[0,-9.805],[-9.805,0]],"o":[[0,-9.805],[-9.805,0],[0,9.805],[9.805,0]],"v":[[-6.942,-22.558],[-24.697,-40.312],[-42.451,-22.558],[-24.697,-4.804]],"c":true}]},{"t":135,"s":[{"i":[[0,9.805],[9.805,0],[0,-9.805],[-9.805,0]],"o":[[0,-9.805],[-9.805,0],[0,9.805],[9.805,0]],"v":[[-6.59,-22.526],[-24.345,-40.281],[-42.099,-22.526],[-24.345,-4.772]],"c":true}]}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.337254911661,0.23137255013,0.129411771894,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[270.47,108.276],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Hair","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[-0.312,13.512],[0,0],[0,0],[-0.015,2.62],[0.001,4.351],[0,0],[-0.927,0.06],[-0.139,-15.255],[0,0],[-0.024,-2.669],[-0.092,-4.353],[-0.044,-3.988],[0,-4.307],[0.295,-3.002],[0.482,-0.349],[0.391,4.905],[0,7.994],[0,0]],"o":[[0,0],[0,0],[0.06,-4.479],[0.015,-2.598],[0,0],[-0.003,-15.806],[0.927,-0.06],[0,0],[0.001,4.276],[0.024,2.702],[0.178,8.387],[0.044,3.962],[0,7.459],[-0.49,4.981],[-0.608,0.441],[-0.407,-5.108],[0,0],[0,-6.072]],"v":[[-58.689,-3.691],[-58.726,-4.5],[-58.724,-4.616],[-58.676,-14.567],[-58.471,-23.153],[-58.471,-23.222],[-56.937,-47.531],[-55.247,-22.97],[-55.247,-22.876],[-55.237,-14.204],[-55.068,-5.188],[-54.763,11.754],[-54.743,22.514],[-55.47,41.151],[-56.849,48.639],[-58.387,41.742],[-59.021,24.251],[-59.021,24.218]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[-0.757,13.512],[0,0],[0,0],[-0.037,2.62],[0.002,4.351],[0,0],[-2.253,0.06],[-0.337,-15.255],[0,0],[-0.058,-2.669],[-0.224,-4.353],[-0.106,-3.988],[0,-4.307],[0.717,-3.002],[1.17,-0.349],[0.95,4.905],[0,7.994],[0,0]],"o":[[0,0],[0,0],[0.146,-4.479],[0.037,-2.598],[0,0],[-0.006,-15.806],[2.253,-0.06],[0,0],[0.001,4.276],[0.059,2.702],[0.431,8.387],[0.106,3.962],[0,7.459],[-1.19,4.981],[-1.476,0.441],[-0.989,-5.108],[0,0],[0,-6.072]],"v":[[-56.705,-3.691],[-56.793,-4.5],[-56.789,-4.616],[-56.672,-14.567],[-56.176,-23.153],[-56.176,-23.222],[-52.448,-47.531],[-48.344,-22.97],[-48.344,-22.876],[-48.32,-14.204],[-47.909,-5.188],[-47.168,11.754],[-47.118,22.514],[-48.884,41.151],[-52.234,48.639],[-55.971,41.742],[-57.511,24.251],[-57.511,24.218]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[-1.928,13.512],[0,0],[0,0],[-0.095,2.62],[0.005,4.351],[0,0],[-5.736,0.06],[-0.858,-15.255],[0,0],[-0.149,-2.669],[-0.57,-4.353],[-0.271,-3.988],[0,-4.307],[1.826,-3.002],[2.98,-0.349],[2.418,4.905],[0,7.994],[0,0]],"o":[[0,0],[0,0],[0.372,-4.479],[0.094,-2.598],[0,0],[-0.015,-15.806],[5.736,-0.06],[0,0],[0.004,4.276],[0.151,2.702],[1.098,8.387],[0.269,3.962],[0,7.459],[-3.03,4.981],[-3.758,0.441],[-2.518,-5.108],[0,0],[0,-6.072]],"v":[[-51.946,-3.691],[-52.17,-4.5],[-52.161,-4.616],[-51.862,-14.567],[-50.599,-23.153],[-50.599,-23.222],[-41.108,-47.531],[-30.66,-22.97],[-30.66,-22.876],[-30.598,-14.204],[-29.552,-5.188],[-27.667,11.754],[-27.539,22.514],[-32.036,41.151],[-40.564,48.639],[-50.078,41.742],[-53.999,24.251],[-53.999,24.218]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[-3.112,13.512],[0,0],[0,0],[-0.153,2.62],[0.007,4.351],[0,0],[-9.255,0.06],[-1.384,-15.255],[0,0],[-0.24,-2.669],[-0.919,-4.353],[-0.437,-3.988],[0,-4.307],[2.946,-3.002],[4.808,-0.349],[3.902,4.905],[0,7.994],[0,0]],"o":[[0,0],[0,0],[0.601,-4.479],[0.152,-2.598],[0,0],[-0.025,-15.806],[9.255,-0.06],[0,0],[0.006,4.276],[0.243,2.702],[1.772,8.387],[0.434,3.962],[0,7.459],[-4.889,4.981],[-6.063,0.441],[-4.063,-5.108],[0,0],[0,-6.072]],"v":[[-47.884,-3.691],[-48.247,-4.5],[-48.231,-4.616],[-47.749,-14.567],[-45.712,-23.153],[-45.712,-23.222],[-30.397,-47.531],[-13.538,-22.97],[-13.538,-22.876],[-13.439,-14.204],[-11.75,-5.188],[-8.709,11.754],[-8.502,22.514],[-15.759,41.151],[-29.518,48.639],[-44.87,41.742],[-51.198,24.251],[-51.198,24.218]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[-4.171,13.512],[0,0],[0,0],[-0.205,2.62],[0.01,4.351],[0,0],[-12.405,0.06],[-0.034,-15.806],[0,0],[-0.322,-2.669],[-1.232,-4.353],[-0.586,-3.988],[0,-4.307],[5.048,-4.141],[6.445,-0.349],[5.23,4.905],[0,7.994],[0,0]],"o":[[0,0],[0,0],[0.805,-4.479],[0.203,-2.598],[0,0],[-0.033,-15.806],[12.405,-0.06],[0,0],[0.008,4.276],[0.326,2.702],[2.374,8.387],[0.582,3.962],[0,7.459],[-6.294,5.164],[-8.127,0.441],[-5.446,-5.108],[0,0],[0,-6.072]],"v":[[-44.39,-3.691],[-44.876,-4.5],[-44.856,-4.616],[-44.21,-14.567],[-41.478,-23.153],[-41.478,-23.222],[-20.951,-48.702],[1.646,-22.97],[1.646,-22.876],[1.78,-14.204],[4.043,-5.188],[8.119,11.754],[8.71,22.982],[-1.017,41.619],[-19.46,49.108],[-40.037,42.211],[-48.832,24.251],[-48.832,24.218]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100.834,"s":[{"i":[[-4.804,13.512],[0,0],[0,0],[-0.236,2.62],[0.011,4.351],[0,0],[-14.289,0.06],[-0.039,-15.806],[0,0],[-0.371,-2.669],[-1.42,-4.353],[-0.675,-3.988],[0,-4.307],[6.374,-5.095],[7.424,-0.349],[6.025,4.905],[0,7.994],[0,0]],"o":[[0,0],[0,0],[0.927,-4.479],[0.234,-2.598],[0,0],[-0.038,-15.806],[14.289,-0.06],[0,0],[0.01,4.276],[0.375,2.702],[2.735,8.387],[0.671,3.962],[0,7.459],[-6.899,5.515],[-9.362,0.441],[-6.274,-5.108],[0,0],[0,-6.072]],"v":[[-42.523,-3.691],[-43.083,-4.5],[-43.059,-4.616],[-42.315,-14.567],[-39.169,-23.153],[-39.169,-23.222],[-15.793,-49.406],[10.507,-22.97],[10.507,-22.876],[10.661,-14.204],[13.268,-5.188],[17.964,11.754],[18.644,22.982],[7.44,42.557],[-13.805,50.046],[-37.508,43.149],[-47.64,24.251],[-47.64,24.218]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[-5.266,13.512],[0,0],[0,0],[-0.259,2.62],[0.012,4.351],[0,0],[-15.663,0.06],[-0.042,-15.806],[0,0],[-0.407,-2.669],[-1.556,-4.353],[-0.74,-3.988],[0,-4.307],[6.987,-5.095],[10.261,-0.04],[6.684,5.31],[0,7.994],[0,0]],"o":[[0,0],[0,0],[1.016,-4.479],[0.257,-2.598],[0,0],[-0.042,-15.806],[15.663,-0.06],[0,0],[0.011,4.276],[0.411,2.702],[2.998,8.387],[0.735,3.962],[0,7.459],[-7.562,5.515],[-10.273,0.04],[-6.687,-5.313],[0,0],[0,-6.072]],"v":[[-41.467,-3.925],[-42.08,-4.735],[-42.054,-4.851],[-40.724,-14.567],[-38.047,-23.857],[-38.047,-23.925],[-10.882,-50.344],[16.919,-24.377],[16.919,-24.283],[17.344,-14.204],[19.173,-5.188],[24.835,11.754],[25.58,22.982],[14.071,42.792],[-10.245,50.515],[-36.741,43.149],[-46.56,24.251],[-46.56,24.218]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107.5,"s":[{"i":[[-5.609,13.512],[0,0],[0,0],[-0.276,2.62],[0.013,4.351],[0,0],[-16.682,0.06],[-0.045,-15.806],[0,0],[-0.433,-2.669],[-1.658,-4.353],[-0.788,-3.988],[0,-4.307],[7.101,-5.507],[10.929,-0.04],[7.119,5.31],[0,7.994],[0,0]],"o":[[0,0],[0,0],[1.083,-4.479],[0.273,-2.598],[0,0],[-0.045,-15.806],[16.682,-0.06],[0,0],[0.011,4.276],[0.438,2.702],[3.193,8.387],[0.783,3.962],[0,7.459],[-7.085,5.495],[-10.942,0.04],[-7.123,-5.313],[0,0],[0,-6.072]],"v":[[-40.159,-3.925],[-40.813,-4.735],[-40.785,-4.851],[-38.869,-14.567],[-37.017,-23.857],[-37.017,-23.925],[-7.833,-50.813],[21.277,-24.377],[21.277,-24.283],[21.73,-14.673],[24.928,-5.188],[30.958,11.754],[31.752,22.982],[19.493,43.261],[-7.655,50.984],[-35.376,43.384],[-46.085,24.486],[-46.085,24.453]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111.666,"s":[{"i":[[-5.886,13.512],[0,0],[0,0],[-0.289,2.62],[0.014,4.351],[0,0],[-17.506,0.06],[-0.047,-15.806],[0,0],[-0.454,-2.669],[-1.739,-4.353],[-0.827,-3.988],[0,-4.307],[7.452,-5.507],[11.468,-0.04],[7.47,5.31],[0,7.994],[0,0]],"o":[[0,0],[0,0],[1.136,-4.479],[0.287,-2.598],[0,0],[-0.047,-15.806],[17.506,-0.06],[0,0],[0.012,4.276],[0.46,2.702],[3.351,8.387],[0.822,3.962],[0,7.459],[-7.435,5.495],[-11.482,0.04],[-7.475,-5.313],[0,0],[0,-6.072]],"v":[[-39.463,-4.63],[-39.41,-4.735],[-39.38,-4.851],[-37.37,-14.567],[-36.165,-24.092],[-36.165,-24.16],[-5.294,-51.752],[25.747,-24.377],[25.747,-24.283],[26.223,-14.673],[29.332,-4.954],[35.168,12.458],[36.001,23.687],[24.122,43.965],[-5.107,51.923],[-34.197,43.853],[-45.681,24.016],[-45.681,23.984]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[-7.295,13.962],[0,0],[0,0],[-0.305,2.62],[0.015,4.351],[0,0],[-18.449,0.06],[-0.05,-15.806],[0,0],[-0.479,-2.669],[-1.833,-4.353],[-0.871,-3.988],[0,-4.307],[7.853,-5.507],[12.086,-0.04],[7.873,5.31],[0,7.994],[0,0]],"o":[[0,0],[0,0],[1.197,-4.479],[0.302,-2.598],[0,0],[-0.05,-15.806],[18.449,-0.06],[0,0],[0.012,4.276],[0.485,2.702],[3.532,8.387],[0.866,3.962],[0,7.459],[-7.836,5.495],[-12.101,0.04],[-7.877,-5.313],[0,0],[0,-6.072]],"v":[[-37.86,-4.63],[-37.804,-4.735],[-37.773,-4.851],[-35.654,-14.567],[-35.374,-24.092],[-35.374,-24.16],[-2.345,-53.161],[30.863,-24.377],[30.863,-24.283],[31.364,-14.673],[34.641,-4.954],[40.79,12.458],[41.669,23.687],[29.15,43.965],[-1.653,53.097],[-32.557,44.557],[-45.155,23.978],[-45.155,23.945]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[-7.446,13.962],[0,0],[0,0],[-0.313,2.62],[0.015,4.351],[0,0],[-18.83,0.06],[-0.051,-15.806],[0,0],[-0.49,-2.669],[-1.873,-4.352],[-0.89,-3.988],[0,-4.307],[8.014,-5.508],[12.335,-0.04],[8.035,5.309],[0,7.994],[0,0]],"o":[[0,0],[0,0],[1.222,-4.479],[0.309,-2.598],[0,0],[-0.051,-15.806],[18.83,-0.06],[0,0],[0.013,4.276],[0.495,2.702],[3.604,8.387],[0.884,3.962],[0,7.459],[-7.997,5.495],[-12.35,0.04],[-8.04,-5.313],[0,0],[0,-6.072]],"v":[[-37.213,-4.63],[-37.156,-4.735],[-37.125,-4.851],[-34.962,-14.567],[-34.676,-24.092],[-34.676,-24.16],[-0.966,-53.161],[32.926,-24.377],[32.926,-24.283],[33.437,-14.673],[36.782,-4.954],[43.058,12.458],[43.955,23.687],[31.177,43.965],[-0.26,53.097],[-31.801,44.557],[-44.659,23.978],[-44.659,23.945]],"c":true}]},{"t":135,"s":[{"i":[[-7.446,13.962],[0,0],[0,0],[-0.313,2.62],[0.015,4.351],[0,0],[-18.83,0.06],[-0.051,-15.806],[0,0],[-0.49,-2.669],[-1.873,-4.352],[-0.89,-3.988],[0,-4.307],[8.014,-5.508],[12.335,-0.04],[8.035,5.309],[0,7.994],[0,0]],"o":[[0,0],[0,0],[1.222,-4.479],[0.309,-2.598],[0,0],[-0.051,-15.806],[18.83,-0.06],[0,0],[0.013,4.276],[0.495,2.702],[3.604,8.387],[0.884,3.962],[0,7.459],[-7.997,5.495],[-12.35,0.04],[-8.04,-5.313],[0,0],[0,-6.072]],"v":[[-36.861,-4.598],[-36.804,-4.703],[-36.772,-4.819],[-34.61,-14.535],[-34.324,-24.06],[-34.324,-24.128],[-0.614,-53.129],[33.278,-24.345],[33.278,-24.252],[33.789,-14.641],[37.134,-4.922],[43.41,12.489],[44.307,23.718],[31.53,43.997],[0.092,53.129],[-31.449,44.589],[-44.307,24.009],[-44.307,23.977]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[0.226,1.656],[-0.13,7.412],[-0.226,-1.656],[0.13,-7.412]],"o":[[-0.227,-1.657],[0.13,-7.412],[0.227,1.657],[-0.13,7.412]],"v":[[-55.466,2.927],[-55.616,-13.258],[-54.921,-22.506],[-54.796,-6.555]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[0.55,1.656],[-0.316,7.412],[-0.55,-1.656],[0.316,-7.412]],"o":[[-0.55,-1.657],[0.316,-7.412],[0.55,1.657],[-0.316,7.412]],"v":[[-48.875,2.927],[-49.239,-13.258],[-47.55,-22.506],[-47.247,-6.555]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[1.401,1.656],[-0.804,7.412],[-1.401,-1.656],[0.804,-7.412]],"o":[[-1.401,-1.657],[0.804,-7.412],[1.401,1.657],[-0.804,7.412]],"v":[[-32.013,2.927],[-32.939,-13.258],[-28.639,-22.506],[-27.867,-6.555]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[2.26,1.656],[-1.298,7.412],[-2.26,-1.656],[1.298,-7.412]],"o":[[-2.261,-1.657],[1.298,-7.412],[2.261,1.657],[-1.298,7.412]],"v":[[-15.721,2.927],[-17.217,-13.258],[-10.278,-22.506],[-9.031,-6.555]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[3.029,1.656],[-1.74,7.412],[-3.029,-1.656],[1.739,-7.412]],"o":[[-3.031,-1.657],[1.74,-7.412],[3.03,1.657],[-1.739,7.412]],"v":[[-0.652,2.224],[-2.656,-13.961],[6.643,-23.209],[8.315,-7.258]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100.834,"s":[{"i":[[3.489,1.656],[-2.004,7.412],[-3.489,-1.656],[2.004,-7.412]],"o":[[-3.491,-1.657],[2.004,-7.412],[3.491,1.657],[-2.004,7.412]],"v":[[7.86,1.52],[5.551,-14.665],[16.264,-23.912],[18.189,-7.962]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[3.825,1.656],[-2.196,7.412],[-3.825,-1.656],[2.196,-7.412]],"o":[[-3.826,-1.657],[2.196,-7.412],[3.826,1.657],[-2.196,7.412]],"v":[[14.017,1.051],[11.486,-15.134],[23.228,-24.381],[25.339,-8.431]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107.5,"s":[{"i":[[4.074,1.656],[-2.339,7.412],[-4.074,-1.656],[2.339,-7.412]],"o":[[-4.076,-1.657],[2.339,-7.412],[4.076,1.657],[-2.339,7.412]],"v":[[18.936,-0.122],[16.24,-16.307],[28.747,-25.555],[30.995,-9.604]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111.666,"s":[{"i":[[4.275,1.656],[-2.455,7.412],[-4.275,-1.656],[2.455,-7.412]],"o":[[-4.277,-1.657],[2.455,-7.412],[4.277,1.657],[-2.455,7.412]],"v":[[23.29,-0.122],[20.462,-16.307],[33.587,-25.555],[35.946,-9.604]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[4.505,1.656],[-2.587,7.412],[-4.505,-1.656],[2.587,-7.412]],"o":[[-4.507,-1.657],[2.587,-7.412],[4.507,1.657],[-2.587,7.412]],"v":[[28.274,-0.122],[24.798,-16.542],[37.64,-26.963],[41.116,-10.543]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[4.598,1.656],[-2.64,7.412],[-4.598,-1.656],[2.64,-7.412]],"o":[[-4.6,-1.657],[2.64,-7.412],[4.6,1.657],[-2.64,7.412]],"v":[[30.283,-0.122],[26.736,-16.542],[39.843,-26.963],[43.39,-10.543]],"c":true}]},{"t":135,"s":[{"i":[[4.598,1.656],[-2.64,7.412],[-4.598,-1.656],[2.64,-7.412]],"o":[[-4.6,-1.657],[2.64,-7.412],[4.6,1.657],[-2.64,7.412]],"v":[[30.635,-0.09],[27.088,-16.51],[40.195,-26.931],[43.742,-10.511]],"c":true}]}],"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[0.226,-1.656],[0.13,7.412],[-0.226,1.656],[-0.13,-7.412]],"o":[[-0.227,1.657],[-0.13,-7.412],[0.227,-1.657],[0.13,7.412]],"v":[[-58.448,-0.292],[-59.074,-9.887],[-58.879,-25.49],[-58.273,-15.539]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[0.55,-1.656],[0.316,7.412],[-0.55,1.656],[-0.316,-7.412]],"o":[[-0.55,1.657],[-0.316,-7.412],[0.55,-1.657],[0.316,7.412]],"v":[[-56.118,-0.292],[-57.64,-9.887],[-57.165,-25.49],[-55.694,-15.539]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[1.401,-1.656],[0.804,7.412],[-1.401,1.656],[-0.804,-7.412]],"o":[[-1.401,1.657],[-0.804,-7.412],[1.401,-1.657],[0.804,7.412]],"v":[[-50.453,-0.292],[-54.326,-9.887],[-53.117,-25.49],[-49.372,-15.539]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[2.26,-1.656],[1.298,7.412],[-2.26,1.656],[-1.298,-7.412]],"o":[[-2.261,1.657],[-1.298,-7.412],[2.261,-1.657],[1.298,7.412]],"v":[[-45.475,-0.292],[-51.726,-9.887],[-49.775,-25.49],[-43.732,-15.539]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[3.029,-1.656],[1.739,7.412],[-3.029,1.656],[-1.739,-7.412]],"o":[[-3.03,1.657],[-1.739,-7.412],[3.03,-1.657],[1.739,7.412]],"v":[[-41.476,-0.761],[-49.854,-10.355],[-47.238,-25.959],[-39.139,-16.007]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100.834,"s":[{"i":[[3.489,-1.656],[2.004,7.412],[-3.489,1.656],[-2.004,-7.412]],"o":[[-3.491,1.657],[-2.004,-7.412],[3.491,-1.657],[2.004,7.412]],"v":[[-39.166,-0.761],[-48.816,-10.355],[-45.804,-25.959],[-36.474,-16.007]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[3.825,-1.656],[2.196,7.412],[-3.825,1.656],[-2.196,-7.412]],"o":[[-3.826,1.657],[-2.196,-7.412],[3.826,-1.657],[2.196,7.412]],"v":[[-37.786,0.178],[-47.85,-10.355],[-45.32,-25.959],[-34.835,-16.242]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107.5,"s":[{"i":[[4.074,-1.656],[2.339,7.412],[-4.074,1.656],[-2.339,-7.412]],"o":[[-4.075,1.657],[-2.339,-7.412],[4.075,-1.657],[2.339,7.412]],"v":[[-36.739,-0.057],[-47.459,-10.59],[-44.763,-26.193],[-33.597,-16.477]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111.666,"s":[{"i":[[4.275,-1.656],[2.455,7.412],[-4.275,1.656],[-2.455,-7.412]],"o":[[-4.277,1.657],[-2.455,-7.412],[4.277,-1.657],[2.455,7.412]],"v":[[-35.135,-0.057],[-46.384,-10.59],[-43.555,-26.193],[-31.837,-16.477]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[4.505,-1.656],[2.587,7.412],[-4.505,1.656],[-2.587,-7.412]],"o":[[-4.507,1.657],[-2.587,-7.412],[4.507,-1.657],[2.587,7.412]],"v":[[-33.299,-0.057],[-46.143,-10.478],[-42.667,-26.898],[-29.823,-16.477]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[4.598,-1.656],[2.64,7.412],[-4.598,1.656],[-2.64,-7.412]],"o":[[-4.6,1.657],[-2.64,-7.412],[4.6,-1.657],[2.64,7.412]],"v":[[-32.558,-0.057],[-45.667,-10.478],[-42.12,-26.898],[-29.011,-16.477]],"c":true}]},{"t":135,"s":[{"i":[[4.598,-1.656],[2.64,7.412],[-4.598,1.656],[-2.64,-7.412]],"o":[[-4.6,1.657],[-2.64,-7.412],[4.6,-1.657],[2.64,7.412]],"v":[[-32.206,-0.025],[-45.315,-10.446],[-41.768,-26.866],[-28.659,-16.445]],"c":true}]}],"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431391716,0.403921574354,0.305882364511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[270.996,145.54],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Head","np":4,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[1.662,-1.078],[0.252,-6.5],[0,0],[0,0],[0.006,7.75]],"o":[[-1.809,1.173],[-0.012,11],[0,0],[0,0],[-0.448,-10.219]],"v":[[214.31,197.746],[210.645,231.218],[210.632,265.093],[217.136,244.459],[217.174,217.753]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[4.038,-1.078],[0.613,-6.5],[0,0],[0,0],[0.015,7.75]],"o":[[-4.394,1.173],[-0.03,11],[0,0],[0,0],[-1.088,-10.219]],"v":[[219.157,197.746],[210.255,231.218],[210.225,265.093],[226.024,244.459],[226.116,217.753]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[10.281,-1.078],[1.561,-6.5],[0,0],[0,0],[0.038,7.75]],"o":[[-11.186,1.173],[-0.076,11],[0,0],[0,0],[-2.771,-10.219]],"v":[[231.439,197.746],[208.774,231.218],[208.698,265.093],[248.922,244.459],[249.155,217.753]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[16.589,-1.078],[2.519,-6.5],[0,0],[0,0],[0.061,7.75]],"o":[[-18.049,1.173],[-0.123,11],[0,0],[0,0],[-6.41,-10.783]],"v":[[241.968,198.682],[206.53,231.218],[206.407,265.093],[271.689,247.502],[271.688,217.753]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[22.236,-1.078],[3.376,-6.5],[0,0],[0,0],[0.082,7.75]],"o":[[-24.192,1.173],[-0.165,11],[0,0],[0,0],[-8.592,-10.783]],"v":[[251.88,199.385],[204.38,231.218],[204.215,265.093],[291.799,250.781],[291.744,219.393]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100.834,"s":[{"i":[[25.614,-1.078],[3.889,-6.5],[0,0],[0,0],[0.095,7.75]],"o":[[-27.868,1.173],[-0.19,11],[0,0],[0,0],[-9.898,-10.783]],"v":[[257.047,200.557],[202.871,231.218],[202.681,265.093],[303.634,253.83],[303.602,221.269]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[28.076,-1.078],[4.263,-6.5],[0,0],[0,0],[0.104,7.75]],"o":[[-30.546,1.173],[-0.208,11],[0,0],[0,0],[-11.853,-12.5]],"v":[[259.05,201.261],[201.981,231.218],[201.773,265.093],[312.516,256.879],[312.499,222.442]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107.5,"s":[{"i":[[29.015,0],[4.541,-6.5],[0,0],[0,0],[0.111,7.75]],"o":[[-32.559,0],[-0.221,11],[0,0],[0,0],[-12.625,-12.5]],"v":[[263.029,201.731],[201.994,231.218],[201.773,265.093],[319.85,258.99],[319.818,224.789]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111.666,"s":[{"i":[[30.448,0],[4.765,-6.5],[0,0],[0,0],[0.116,7.75]],"o":[[-34.167,0],[-0.232,11],[0,0],[0,0],[-13.249,-12.5]],"v":[[265.316,202.435],[202.005,231.218],[201.773,265.093],[325.671,261.572],[325.765,226.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[32.089,0],[5.021,-6.5],[0,0],[0,0],[0.122,7.75]],"o":[[-36.008,0],[-0.245,11],[0,0],[0,0],[-13.962,-12.5]],"v":[[268.491,203.374],[202.018,231.218],[201.773,265.093],[332.444,263.215],[332.444,228.779]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[32.75,0],[5.125,-6.5],[0,0],[0,0],[0.125,7.75]],"o":[[-36.75,0],[-0.25,11],[0,0],[0,0],[-14.25,-12.5]],"v":[[269.148,203.843],[202.023,231.218],[201.773,265.093],[335.023,265.093],[335.023,229.718]],"c":true}]},{"t":135,"s":[{"i":[[32.75,0],[5.125,-6.5],[0,0],[0,0],[0.125,7.75]],"o":[[-36.75,0],[-0.25,11],[0,0],[0,0],[-14.25,-12.5]],"v":[[269.5,203.875],[202.375,231.25],[202.125,265.125],[335.375,265.125],[335.375,229.75]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.105865478516,0.450958251953,0.901947021484,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Body","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":86,"op":660,"st":60,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"FRONT_SCREEN 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":93.334,"s":[0]},{"t":101.666015625,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.312,149.969,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[0.25,-9.25],[0,0],[0.25,-0.5],[0,0],[0.5,9.25],[0,0],[-2,-2.5],[0,0]],"o":[[0,0],[0.25,3],[0,0],[-2,2.75],[0,0],[0,-16.016],[0,0],[1.75,3.5]],"v":[[-308,-465.5],[-308,477.25],[-308.25,483],[-356.25,568.25],[-364,573.75],[-364,-560.5],[-354.5,-565],[-310.5,-483]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88.334,"s":[{"i":[[1,-13],[0,0],[5,-5],[0,0],[0,16.016],[0,0],[-18,-17],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-11.25,9],[0,0],[0,-16.016],[0,0],[5.5,4.5]],"v":[[-257,-464],[-257,468],[-270.5,493.062],[-345.5,568.5],[-358.25,553.75],[-357,-542.5],[-338,-559],[-265.5,-485.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":89.166,"s":[{"i":[[0.667,-14.005],[0,0],[8.672,-3.333],[0,0],[0,16.016],[0,0],[-17.339,-11.333],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-12.839,6],[0,0],[0,-16.016],[0,0],[9.005,3]],"v":[[-210.667,-462.333],[-210.667,466],[-229.333,495.708],[-334.833,568.667],[-353,549.167],[-352.167,-541.5],[-329.833,-562.167],[-226,-489.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0.333,-15.011],[0,0],[12.344,-1.667],[0,0],[0,16.016],[0,0],[-16.677,-5.667],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-14.427,3],[0,0],[0,-16.016],[0,0],[12.511,1.5]],"v":[[-167.833,-460.667],[-167.833,464],[-191.667,498.354],[-324.167,568.833],[-347.75,544.583],[-347.333,-540.5],[-321.667,-565.333],[-190,-493.833]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90.834,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-127,-459],[-127,462],[-156,501],[-313.5,569],[-342.5,540],[-342.5,-539.5],[-313.5,-568.5],[-156,-498]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":91.666,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-89.667,-464.667],[-89.667,467.333],[-118.667,503],[-308.667,571.333],[-337.667,542.333],[-337.667,-541.333],[-308.667,-570.333],[-118.667,-500.333]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":92.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-54.833,-470.333],[-54.833,472.667],[-83.833,505],[-303.833,573.667],[-332.833,544.667],[-332.833,-543.167],[-303.833,-572.167],[-83.833,-502.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-24,-476],[-24,478],[-53,507],[-299,576],[-328,547],[-328,-545],[-299,-574],[-53,-505]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":94.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[6.073,-479.4],[6.073,481.5],[-22.927,510.5],[-297.12,577.1],[-326.12,548.1],[-326.12,-546.2],[-297.12,-575.2],[-22.927,-508.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[32.647,-482.8],[32.647,485],[3.647,514],[-295.24,578.2],[-324.24,549.2],[-324.24,-547.4],[-295.24,-576.4],[3.647,-511.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95.834,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[57.22,-486.2],[57.22,488.5],[28.22,517.5],[-293.36,579.3],[-322.36,550.3],[-322.36,-548.6],[-293.36,-577.6],[28.22,-515.2]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96.666,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[79.46,-489.6],[79.46,492],[50.46,521],[-291.48,580.4],[-320.48,551.4],[-320.48,-549.8],[-291.48,-578.8],[50.46,-518.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[99.7,-493],[99.7,495.5],[70.7,524.5],[-289.6,581.5],[-318.6,552.5],[-318.6,-551],[-289.6,-580],[70.7,-522]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":99.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[136.58,-499.8],[136.58,502.5],[107.58,531.5],[-285.84,583.7],[-314.84,554.7],[-314.84,-553.4],[-285.84,-582.4],[107.58,-528.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101.666,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[179.4,-510],[179.4,513],[150.4,542],[-280.2,587],[-309.2,558],[-309.2,-557],[-280.2,-586],[150.4,-539]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":103.334,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[201.28,-516],[201.28,518.8],[172.28,547.8],[-279.44,588.2],[-308.44,559.2],[-308.44,-558],[-279.44,-587],[172.28,-545]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[212.053,-519],[212.053,521.7],[183.053,550.7],[-279.06,588.8],[-308.06,559.8],[-308.06,-558.5],[-279.06,-587.5],[183.053,-548]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[220.827,-522],[220.827,524.6],[191.827,553.6],[-278.68,589.4],[-307.68,560.4],[-307.68,-559],[-278.68,-588],[191.827,-551]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105.834,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[229.1,-525],[229.1,527.5],[200.1,556.5],[-278.3,590],[-307.3,561],[-307.3,-559.5],[-278.3,-588.5],[200.1,-554]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":109.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[256.46,-535.4],[256.46,537.5],[227.46,566.5],[-276.78,592.4],[-305.78,563.4],[-305.78,-561.5],[-276.78,-590.5],[227.46,-564.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":110,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[260.8,-538],[260.8,540],[231.8,569],[-276.4,593],[-305.4,564],[-305.4,-562],[-276.4,-591],[231.8,-567]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":112.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[273.82,-543.7],[273.82,546],[244.82,575],[-276.16,594.2],[-305.16,565.2],[-305.16,-562.6],[-276.16,-591.6],[244.82,-572.7]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":114.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[280.833,-547.5],[280.833,550],[251.833,579],[-276,595],[-305,566],[-305,-563],[-276,-592],[251.833,-576.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[294.867,-555],[294.867,558],[265.867,587],[-275.6,597],[-304.6,568],[-304.6,-564],[-275.6,-593],[265.867,-584]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":122.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[301.9,-559.5],[301.9,563],[272.9,592],[-275.7,597],[-304.7,568],[-304.7,-565],[-275.7,-594],[272.9,-588.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[306.933,-564],[306.933,568],[277.933,597],[-275.8,597],[-304.8,568],[-304.8,-566],[-275.8,-595],[277.933,-593]],"c":true}]},{"t":135,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[310,-568],[310,570],[281,599],[-276,599],[-305,570],[-305,-568],[-276,-597],[281,-597]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.815686285496,0.882352948189,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[384,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[101.75,101.75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":87,"op":342,"st":60,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"FRONT_SCREEN 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.312,149.969,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[0.25,-9.25],[0,0],[0.25,-0.5],[0,0],[0.5,9.25],[0,0],[-2,-2.5],[0,0]],"o":[[0,0],[0.25,3],[0,0],[-2,2.75],[0,0],[0,-16.016],[0,0],[1.75,3.5]],"v":[[-308,-465.5],[-308,477.25],[-308.25,483],[-356.25,568.25],[-364,573.75],[-364,-560.5],[-354.5,-565],[-310.5,-483]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88.334,"s":[{"i":[[1,-13],[0,0],[5,-5],[0,0],[0,16.016],[0,0],[-18,-17],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-11.25,9],[0,0],[0,-16.016],[0,0],[5.5,4.5]],"v":[[-257,-464],[-257,468],[-270.5,493.062],[-345.5,568.5],[-358.25,553.75],[-357,-542.5],[-338,-559],[-265.5,-485.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":89.166,"s":[{"i":[[0.667,-14.005],[0,0],[8.672,-3.333],[0,0],[0,16.016],[0,0],[-17.339,-11.333],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-12.839,6],[0,0],[0,-16.016],[0,0],[9.005,3]],"v":[[-210.667,-462.333],[-210.667,466],[-229.333,495.708],[-334.833,568.667],[-353,549.167],[-352.167,-541.5],[-329.833,-562.167],[-226,-489.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0.333,-15.011],[0,0],[12.344,-1.667],[0,0],[0,16.016],[0,0],[-16.677,-5.667],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-14.427,3],[0,0],[0,-16.016],[0,0],[12.511,1.5]],"v":[[-167.833,-460.667],[-167.833,464],[-191.667,498.354],[-324.167,568.833],[-347.75,544.583],[-347.333,-540.5],[-321.667,-565.333],[-190,-493.833]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90.834,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-127,-459],[-127,462],[-156,501],[-313.5,569],[-342.5,540],[-342.5,-539.5],[-313.5,-568.5],[-156,-498]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":91.666,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-89.667,-464.667],[-89.667,467.333],[-118.667,503],[-308.667,571.333],[-337.667,542.333],[-337.667,-541.333],[-308.667,-570.333],[-118.667,-500.333]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":92.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-54.833,-470.333],[-54.833,472.667],[-83.833,505],[-303.833,573.667],[-332.833,544.667],[-332.833,-543.167],[-303.833,-572.167],[-83.833,-502.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[-24,-476],[-24,478],[-53,507],[-299,576],[-328,547],[-328,-545],[-299,-574],[-53,-505]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":94.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[6.073,-479.4],[6.073,481.5],[-22.927,510.5],[-297.12,577.1],[-326.12,548.1],[-326.12,-546.2],[-297.12,-575.2],[-22.927,-508.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[32.647,-482.8],[32.647,485],[3.647,514],[-295.24,578.2],[-324.24,549.2],[-324.24,-547.4],[-295.24,-576.4],[3.647,-511.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95.834,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[57.22,-486.2],[57.22,488.5],[28.22,517.5],[-293.36,579.3],[-322.36,550.3],[-322.36,-548.6],[-293.36,-577.6],[28.22,-515.2]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":96.666,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[79.46,-489.6],[79.46,492],[50.46,521],[-291.48,580.4],[-320.48,551.4],[-320.48,-549.8],[-291.48,-578.8],[50.46,-518.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[99.7,-493],[99.7,495.5],[70.7,524.5],[-289.6,581.5],[-318.6,552.5],[-318.6,-551],[-289.6,-580],[70.7,-522]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":99.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[136.58,-499.8],[136.58,502.5],[107.58,531.5],[-285.84,583.7],[-314.84,554.7],[-314.84,-553.4],[-285.84,-582.4],[107.58,-528.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101.666,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[179.4,-510],[179.4,513],[150.4,542],[-280.2,587],[-309.2,558],[-309.2,-557],[-280.2,-586],[150.4,-539]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":103.334,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[201.28,-516],[201.28,518.8],[172.28,547.8],[-279.44,588.2],[-308.44,559.2],[-308.44,-558],[-279.44,-587],[172.28,-545]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[212.053,-519],[212.053,521.7],[183.053,550.7],[-279.06,588.8],[-308.06,559.8],[-308.06,-558.5],[-279.06,-587.5],[183.053,-548]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[220.827,-522],[220.827,524.6],[191.827,553.6],[-278.68,589.4],[-307.68,560.4],[-307.68,-559],[-278.68,-588],[191.827,-551]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105.834,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[229.1,-525],[229.1,527.5],[200.1,556.5],[-278.3,590],[-307.3,561],[-307.3,-559.5],[-278.3,-588.5],[200.1,-554]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":109.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[256.46,-535.4],[256.46,537.5],[227.46,566.5],[-276.78,592.4],[-305.78,563.4],[-305.78,-561.5],[-276.78,-590.5],[227.46,-564.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":110,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[260.8,-538],[260.8,540],[231.8,569],[-276.4,593],[-305.4,564],[-305.4,-562],[-276.4,-591],[231.8,-567]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":112.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[273.82,-543.7],[273.82,546],[244.82,575],[-276.16,594.2],[-305.16,565.2],[-305.16,-562.6],[-276.16,-591.6],[244.82,-572.7]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":114.166,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[280.833,-547.5],[280.833,550],[251.833,579],[-276,595],[-305,566],[-305,-563],[-276,-592],[251.833,-576.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118.334,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[294.867,-555],[294.867,558],[265.867,587],[-275.6,597],[-304.6,568],[-304.6,-564],[-275.6,-593],[265.867,-584]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":122.5,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[301.9,-559.5],[301.9,563],[272.9,592],[-275.7,597],[-304.7,568],[-304.7,-565],[-275.7,-594],[272.9,-588.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[306.933,-564],[306.933,568],[277.933,597],[-275.8,597],[-304.8,568],[-304.8,-566],[-275.8,-595],[277.933,-593]],"c":true}]},{"t":135,"s":[{"i":[[0,-16.016],[0,0],[16.016,0],[0,0],[0,16.016],[0,0],[-16.016,0],[0,0]],"o":[[0,0],[0,16.016],[0,0],[-16.016,0],[0,0],[0,-16.016],[0,0],[16.016,0]],"v":[[310,-568],[310,570],[281,599],[-276,599],[-305,570],[-305,-568],[-276,-597],[281,-597]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[384,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[101.75,101.75],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":87,"op":342,"st":60,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"CENTER_SCREEN 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.312,149.969,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[699,-539],[699,539],[653,585],[-0.999,585],[-653,585],[-699,539],[-699,-539],[-653,-585],[1,-585],[653,-585]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":64.166,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[707,-547],[707,547],[661,593],[-0.999,585],[-639.273,577],[-685.273,531],[-685.273,-527],[-639.273,-573],[1,-585],[661,-593]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":68.334,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[715,-563],[715,567],[669,613],[-0.999,585],[-617.545,561],[-663.545,515],[-663.545,-511],[-617.545,-557],[1,-585],[669,-609]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70.834,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[709,-577.4],[709,579],[663,625],[-0.999,585],[-587.709,551.4],[-636.709,505.4],[-636.709,-501.4],[-587.709,-547.4],[1,-585],[663,-623.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":72.5,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[705,-587],[705,587],[659,633],[-0.999,585],[-567.818,545],[-613.818,499],[-613.818,-495],[-567.818,-541],[1,-585],[659,-633]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":73.334,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[697.4,-593.4],[697.4,595],[651.4,641],[-0.999,585],[-553.873,539.4],[-599.873,493.4],[-599.873,-490.2],[-553.873,-536.2],[1,-585],[651.4,-639.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":74.166,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[693.8,-599.8],[693.8,603],[647.8,649],[-0.999,585],[-537.927,533.8],[-583.927,487.8],[-583.927,-485.4],[-537.927,-531.4],[1,-585],[647.8,-645.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":75,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[678.2,-606.2],[678.2,611],[632.2,657],[-0.999,585],[-519.982,528.2],[-565.982,482.2],[-565.982,-480.6],[-519.982,-526.6],[1,-585],[632.2,-652.2]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":75.834,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[666.6,-612.6],[666.6,619],[620.6,665],[-0.999,585],[-499.036,522.6],[-545.036,476.6],[-545.036,-475.8],[-499.036,-521.8],[1,-585],[620.6,-658.6]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.405,0],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-25.405,0],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[647,-619],[647,627],[601,673],[-0.999,585],[-478.091,517],[-524.091,471],[-524.091,-471],[-478.091,-517],[1,-585],[601,-665]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":78.334,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.223,2.5],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-22.441,-1.8],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[600.891,-638.2],[600.891,649.545],[561.618,691.182],[-5.908,586.273],[-428.856,507],[-467.174,466.273],[-467.174,-463],[-420.674,-508.5],[1,-585.6],[554.891,-684.2]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":79.166,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.132,3.75],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-20.96,-2.7],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[561.836,-647.8],[561.836,660.818],[525.927,700.273],[-8.363,586.909],[-398.739,502],[-433.216,463.909],[-433.216,-459],[-386.466,-504.25],[1,-585.9],[515.836,-693.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-25.041,5],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-19.478,-3.6],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[522.782,-657.4],[522.782,672.091],[490.236,709.364],[-10.817,587.546],[-368.621,497],[-399.258,461.546],[-399.258,-455],[-352.258,-500],[1,-586.2],[476.782,-703.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80.834,"s":[{"i":[[0,-25.405],[0,0],[25.405,0],[0,0],[0,0],[0,25.405],[0,0],[-24.95,6.25],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-17.996,-4.5],[0,0],[0,-25.405],[0,0],[0,0],[25.405,0]],"v":[[471.727,-667],[471.727,683.364],[442.545,718.455],[-13.272,588.182],[-332.504,492],[-359.3,459.182],[-359.3,-451],[-312.05,-495.75],[1,-586.5],[425.727,-713]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[0.129,-24.588],[0,0],[21.663,1.508],[0,0],[0,0],[0.008,23.224],[0,0],[-21.2,5.875],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-15.329,-4.5],[0,0],[0,-25.405],[0,0],[0,0],[21.8,-2.083]],"v":[[412.352,-679.333],[412.352,694.197],[387.295,726.538],[-16.188,588.015],[-289.754,488.25],[-312.675,457.182],[-312.675,-449],[-272.55,-491.375],[-4.583,-587],[372.894,-720.667]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":82.5,"s":[{"i":[[0.258,-23.77],[0,0],[17.922,3.015],[0,0],[0,0],[0.017,21.043],[0,0],[-17.45,5.5],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-12.663,-4.5],[0,0],[0,-25.405],[0,0],[0,0],[18.194,-4.167]],"v":[[344.977,-691.667],[344.977,705.03],[324.045,734.621],[-19.105,587.849],[-247.004,484.5],[-266.05,455.182],[-266.05,-447],[-233.05,-487],[-10.166,-587.5],[312.06,-728.333]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":83.334,"s":[{"i":[[0.386,-22.953],[0,0],[14.18,4.523],[0,0],[0,0],[0.025,18.862],[0,0],[-13.7,5.125],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-9.996,-4.5],[0,0],[0,-25.405],[0,0],[0,0],[14.589,-6.25]],"v":[[269.602,-704],[269.602,715.864],[252.795,742.705],[-22.022,587.682],[-204.254,480.75],[-219.425,453.182],[-219.425,-445],[-193.55,-482.625],[-15.75,-588],[243.227,-736]],"c":true}]},{"t":85.833984375,"s":[{"i":[[0.773,-20.5],[0,0],[2.955,9.045],[0,0],[0,0],[0.05,12.318],[0,0],[-2.45,4],[0,0],[0,0]],"o":[[0,0],[0,25.405],[0,0],[0,0],[-1.996,-4.5],[0,0],[0,-25.405],[0,0],[0,0],[3.773,-12.5]],"v":[[13.477,-707],[13.477,713.364],[9.045,731.955],[-30.772,587.182],[-58.004,481.5],[-61.55,459.182],[-61.55,-451],[-57.05,-481.5],[-32.5,-581.5],[6.727,-725]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":87,"st":60,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"FOLDABLE_BODY 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206.312,149.969,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[19.95,19.95,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[{"i":[[0,-40.869],[0,0],[40.869,0],[0,0],[0,0],[0,40.869],[0,0],[-40.869,0],[0,0],[0,0]],"o":[[0,0],[0,40.869],[0,0],[0,0],[-40.869,0],[0,0],[0,-40.869],[0,0],[0,0],[40.869,0]],"v":[[733,-567],[733,567],[659,641],[5,643],[-659,641],[-733,567],[-733,-567],[-659,-641],[3.001,-641],[659,-641]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":65,"s":[{"i":[[0,-40.869],[0,0],[40.869,0],[0,0],[0,0],[-0.091,49.495],[0,0],[-40.869,0],[0,0],[0,0]],"o":[[0,0],[0,40.869],[0,0],[0,0],[-46.341,0.245],[0,0],[-1.795,-49.895],[0,0],[0,0],[40.869,0]],"v":[[748.332,-581.845],[747.391,584.136],[673.391,658.136],[5,644.125],[-640.409,628.505],[-714.409,554.505],[-714.455,-550.855],[-640.455,-627.105],[3.001,-643.25],[674.332,-655.845]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":69.166,"s":[{"i":[[0,-40.869],[0,0],[40.869,0],[0,0],[0,0],[0,40.869],[0,0],[-40.869,0],[0,0],[0,0]],"o":[[0,0],[0,40.869],[0,0],[0,0],[-40.869,0],[0,0],[0,-40.869],[0,0],[0,0],[40.869,0]],"v":[[755.15,-598.8],[754.8,603],[680.8,677],[5,643],[-610.25,613.05],[-684.25,539.05],[-685.25,-537.4],[-611.25,-611.4],[3.001,-641],[681.15,-672.8]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70.834,"s":[{"i":[[0,-40.869],[0,0],[42.179,0.978],[0,0],[0,0],[0,40.869],[0,0],[-43.221,1.138],[0,0],[0,0]],"o":[[0,0],[-0.542,42.899],[0,0],[0,0],[-46.299,-2.698],[0,0],[0,-40.869],[0,0],[0,0],[40.869,0]],"v":[[752.228,-610.767],[752.022,614.022],[675.022,688.422],[5,642.2],[-587.372,604.128],[-663.797,530.128],[-665.175,-528.378],[-588.75,-602.378],[3.001,-641],[678.228,-684.767]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":73.334,"s":[{"i":[[0,-40.869],[0,0],[44.144,2.444],[0,0],[0,0],[0,40.869],[0,0],[-46.75,2.844],[0,0],[0,0]],"o":[[0,0],[-1.356,45.944],[0,0],[0,0],[-54.444,-6.745],[0,0],[0,-40.869],[0,0],[0,0],[40.869,0]],"v":[[747.844,-628.717],[747.856,630.556],[666.356,705.556],[5,641],[-553.056,590.745],[-625.618,516.745],[-627.562,-514.844],[-555,-588.844],[3.001,-641],[673.844,-702.717]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":75,"s":[{"i":[[0,-40.869],[0,0],[42.507,1.222],[0,0],[0,0],[0,40.869],[0,0],[-50.375,3.922],[0,0],[0,0]],"o":[[0,0],[-0.678,43.407],[0,0],[0,0],[-54.222,-7.622],[0,0],[0,-40.869],[0,0],[0,0],[46.435,-1.875]],"v":[[734.172,-645.858],[734.428,648.778],[650.678,723.278],[5,642.75],[-513.528,581.122],[-590.559,507.372],[-591.531,-504.922],[-514.5,-578.922],[3.001,-641.875],[649.172,-720.233]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":76.666,"s":[{"i":[[0,-40.869],[0,0],[40.869,0],[0,0],[0,0],[0,40.869],[0,0],[-54,5],[0,0],[0,0]],"o":[[0,0],[0,40.869],[0,0],[0,0],[-54,-8.5],[0,0],[0,-40.869],[0,0],[0,0],[52,-3.75]],"v":[[708.5,-663],[709,667],[635,741],[5,644.5],[-474,571.5],[-545.5,498],[-545.5,-495],[-474,-569],[3.001,-642.75],[624.5,-737.75]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":79.166,"s":[{"i":[[0.9,-49.498],[0,0],[45.297,5.4],[0,0],[0,0],[0,40.869],[0,0],[-40.05,6.8],[0,0],[0,0]],"o":[[0,0],[0.3,47.208],[0,0],[0,0],[-39.15,-8.5],[0,0],[-0.3,-34.008],[0,0],[0,0],[52.45,-11.925]],"v":[[636.05,-692.05],[634.9,699.675],[545.45,770.9],[-7.675,640.525],[-400.05,552.55],[-455.15,479.65],[-453.6,-480.1],[-404,-549.2],[-14.199,-644.325],[537.55,-767.925]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":80.834,"s":[{"i":[[1.5,-55.25],[0,0],[48.25,9],[0,0],[0,0],[0,40.869],[0,0],[-30.75,8],[0,0],[0,0]],"o":[[0,0],[0.5,51.435],[0,0],[0,0],[-29.25,-8.5],[0,0],[-0.5,-29.435],[0,0],[0,0],[52.75,-17.375]],"v":[[549,-717.25],[549.25,723.125],[453.25,792.5],[-16.125,637.875],[-330.75,540.75],[-370.75,480.75],[-372.75,-481],[-331.5,-538.5],[-21.499,-639.125],[448.75,-787.625]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81.666,"s":[{"i":[[2.15,-55.283],[0,0],[43.55,12.75],[0,0],[0,0],[-0.25,36.729],[0,0],[-24.85,7.933],[0,0],[0,0]],"o":[[0,0],[1.8,55.206],[0,0],[0,0],[-23.033,-8.15],[0,0],[-0.6,-27.148],[0,0],[0,0],[50.633,-18.367]],"v":[[493.267,-729.3],[493.367,733.5],[388.433,800.9],[-21.017,638.133],[-291.55,534.233],[-324,473.767],[-324.933,-478.317],[-290.983,-532.1],[-25.783,-636.917],[386.317,-797.433]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":82.5,"s":[{"i":[[2.8,-55.317],[0,0],[38.85,16.5],[0,0],[0,0],[-0.5,32.59],[0,0],[-18.95,7.867],[0,0],[0,0]],"o":[[0,0],[3.1,58.978],[0,0],[0,0],[-16.817,-7.8],[0,0],[-0.7,-24.861],[0,0],[0,0],[48.517,-19.358]],"v":[[427.533,-741.35],[427.483,743.875],[323.617,809.3],[-25.908,638.392],[-252.35,527.717],[-277.25,466.783],[-277.117,-475.633],[-250.467,-525.7],[-30.066,-634.708],[323.883,-807.242]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":83.334,"s":[{"i":[[3.45,-55.35],[0,0],[34.15,20.25],[0,0],[0,0],[-0.75,28.45],[0,0],[-13.05,7.8],[0,0],[0,0]],"o":[[0,0],[4.4,62.75],[0,0],[0,0],[-10.6,-7.45],[0,0],[-0.8,-22.574],[0,0],[0,0],[46.4,-20.35]],"v":[[351.8,-753.4],[351.6,754.25],[258.8,817.7],[-30.8,638.65],[-213.15,521.2],[-230.5,459.8],[-229.3,-472.95],[-209.95,-519.3],[-34.349,-632.5],[261.45,-817.05]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":84.166,"s":[{"i":[[3.225,-52.425],[0,0],[29.45,24],[0,0],[0,0],[-0.375,34.66],[0,0],[-10.275,9.4],[0,0],[0,0]],"o":[[0,0],[2.7,62.375],[0,0],[0,0],[-7.55,-7.975],[0,0],[-0.9,-20.287],[0,0],[0,0],[45.7,-22.425]],"v":[[269.9,-765.45],[269.55,764.625],[176.9,819.85],[-34.025,634.95],[-163.325,517.6],[-176.25,463.65],[-175.65,-472.975],[-160.475,-516.65],[-39.674,-626.75],[180.475,-821.025]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":85,"s":[{"i":[[3,-49.5],[0,0],[24.75,27.75],[0,0],[0,0],[0,40.869],[0,0],[-7.5,11],[0,0],[0,0]],"o":[[0,0],[1,62],[0,0],[0,0],[-4.5,-8.5],[0,0],[-1,-18],[0,0],[0,0],[45,-24.5]],"v":[[182,-777.5],[181.5,775],[95,822],[-37.25,631.25],[-113.5,514],[-122,467.5],[-122,-473],[-111,-514],[-44.999,-621],[99.5,-825]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":85.834,"s":[{"i":[[2.115,-52.053],[0,0],[14.076,24.655],[0,0],[0,0],[0.15,39.565],[0,0],[-4.448,8.548],[0,0],[0,0]],"o":[[0,0],[-2.465,27.536],[0,0],[0,0],[-2.458,-8.114],[0,0],[-0.9,-26.528],[0,0],[0,0],[26.131,-22.782]],"v":[[89.385,-789.28],[89.465,814.464],[14.257,825.512],[-39.14,629.206],[-64.364,505.574],[-68.567,459.152],[-66.85,-455.222],[-64.968,-503.548],[-33.506,-639.074],[16.369,-824.885]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":86.666,"s":[{"i":[[0.346,-57.159],[0,0],[-7.272,18.464],[0,0],[0,0],[0.451,36.956],[0,0],[1.656,3.645],[0,0],[0,0]],"o":[[0,0],[0.605,52.109],[0,0],[0,0],[1.626,-7.341],[0,0],[-0.7,-43.583],[0,0],[0,0],[-11.608,-19.345]],"v":[[-77.846,-794.841],[-76.605,801.391],[5.772,824.536],[14.08,628.118],[25.909,512.723],[30.299,466.457],[32.45,-470.667],[16.094,-533.645],[13.482,-630.223],[3.108,-827.655]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":87.5,"s":[{"i":[[0.346,-57.159],[0,0],[-7.272,18.464],[0,0],[0,0],[0.451,36.956],[0,0],[1.656,3.645],[0,0],[0,0]],"o":[[0,0],[0.605,52.109],[0,0],[0,0],[1.626,-7.341],[0,0],[-0.7,-43.583],[0,0],[0,0],[-11.608,-19.345]],"v":[[-161.846,-788.841],[-160.605,797.391],[-78.228,820.536],[14.08,628.118],[75.909,512.723],[80.299,466.457],[82.45,-470.667],[66.094,-533.645],[13.482,-630.223],[-80.892,-821.655]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88.334,"s":[{"i":[[0.679,-54.879],[0,0],[-13.362,17.618],[0,0],[0,0],[0.601,35.652],[0,0],[2.96,5.097],[0,0],[0,0]],"o":[[0,0],[0.139,54.646],[0,0],[0,0],[3.668,-6.954],[0,0],[-0.709,-37.134],[0,0],[0,0],[-28.144,-20.461]],"v":[[-243.346,-774.288],[-241.973,784.854],[-150.304,818.382],[12.523,630.074],[120.379,515.297],[132.565,465.957],[134.267,-469.042],[115.459,-527.361],[14.143,-629.964],[-152.023,-818.206]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":89.166,"s":[{"i":[[1.012,-52.598],[0,0],[-19.453,16.773],[0,0],[0,0],[0.752,34.347],[0,0],[4.265,6.548],[0,0],[0,0]],"o":[[0,0],[-0.326,57.182],[0,0],[0,0],[5.71,-6.568],[0,0],[-0.718,-30.686],[0,0],[0,0],[-44.68,-21.576]],"v":[[-317.846,-759.735],[-316.341,772.318],[-229.381,816.227],[10.967,632.03],[164.848,517.871],[180.831,465.457],[182.083,-467.416],[164.824,-521.076],[14.803,-629.705],[-230.153,-814.758]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[1.346,-50.318],[0,0],[-25.543,15.927],[0,0],[0,0],[0.902,33.043],[0,0],[5.57,8],[0,0],[0,0]],"o":[[0,0],[-0.791,59.718],[0,0],[0,0],[7.752,-6.182],[0,0],[-0.727,-24.237],[0,0],[0,0],[-61.216,-22.691]],"v":[[-385.346,-745.182],[-383.709,759.782],[-294.457,814.073],[9.41,633.986],[209.318,520.445],[226.598,464.957],[227.4,-465.791],[214.189,-514.791],[15.464,-629.445],[-294.284,-811.309]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90.834,"s":[{"i":[[1.418,-49.133],[0,0],[-31.384,13.082],[0,0],[0,0],[0.677,35],[0,0],[7.748,7.5],[0,0],[0,0]],"o":[[0,0],[-0.457,57.887],[0,0],[0,0],[9.794,-5.795],[0,0],[-0.682,-25.276],[0,0],[0,0],[-56.377,-19.098]],"v":[[-443.237,-737.295],[-442.077,748.496],[-351.866,806.918],[10.729,633.609],[244.163,523.811],[267.781,467.033],[268.3,-467.923],[248.074,-518.673],[13.874,-629.811],[-352.623,-804.027]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":91.666,"s":[{"i":[[1.491,-47.948],[0,0],[-37.224,10.236],[0,0],[0,0],[0.451,36.956],[0,0],[9.927,7],[0,0],[0,0]],"o":[[0,0],[-0.123,56.057],[0,0],[0,0],[11.836,-5.409],[0,0],[-0.636,-26.316],[0,0],[0,0],[-51.538,-15.505]],"v":[[-496.127,-729.409],[-495.445,737.209],[-404.276,799.764],[12.047,633.232],[279.007,527.177],[306.964,469.109],[307.2,-470.055],[281.96,-522.555],[12.285,-630.177],[-405.962,-796.745]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":92.5,"s":[{"i":[[1.564,-46.762],[0,0],[-43.065,7.391],[0,0],[0,0],[0.226,38.913],[0,0],[12.105,6.5],[0,0],[0,0]],"o":[[0,0],[0.211,54.226],[0,0],[0,0],[13.878,-5.023],[0,0],[-0.591,-27.356],[0,0],[0,0],[-46.699,-11.911]],"v":[[-539.018,-721.523],[-538.814,725.923],[-446.685,792.609],[13.365,632.855],[313.852,530.543],[342.647,471.185],[342.6,-472.186],[315.845,-526.436],[10.695,-630.543],[-449.301,-789.464]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93.334,"s":[{"i":[[1.636,-45.577],[0,0],[-48.906,4.545],[0,0],[0,0],[0,40.869],[0,0],[14.283,6],[0,0],[0,0]],"o":[[0,0],[0.545,52.395],[0,0],[0,0],[15.919,-4.636],[0,0],[-0.545,-28.395],[0,0],[0,0],[-41.86,-8.318]],"v":[[-576.909,-713.636],[-577.182,714.636],[-484.094,785.455],[14.684,632.477],[348.697,533.909],[377.33,473.261],[377,-474.318],[349.731,-530.318],[9.106,-630.909],[-487.64,-782.182]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":94.166,"s":[{"i":[[1.5,-45.185],[0,0],[-48.199,4.167],[0,0],[0,0],[0,40.869],[0,0],[16.461,5.5],[0,0],[0,0]],"o":[[0,0],[0.5,51.435],[0,0],[0,0],[17.961,-4.25],[0,0],[-0.5,-29.435],[0,0],[0,0],[-41.74,-7.625]],"v":[[-609.5,-705.25],[-609.25,706.583],[-516.07,777.667],[13.048,633.104],[372.373,539.146],[405.552,476.99],[405.333,-478.125],[373.571,-535.625],[8.099,-632],[-520.404,-774.333]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":95.834,"s":[{"i":[[1.227,-44.4],[0,0],[-46.785,3.409],[0,0],[0,0],[0,40.869],[0,0],[20.818,4.5],[0,0],[0,0]],"o":[[0,0],[0.409,49.514],[0,0],[0,0],[22.045,-3.477],[0,0],[-0.409,-31.514],[0,0],[0,0],[-41.501,-6.239]],"v":[[-659.682,-688.477],[-658.386,690.477],[-565.023,762.091],[9.776,634.358],[419.724,549.619],[461.997,484.446],[462,-485.739],[421.25,-546.239],[6.087,-634.182],[-570.932,-758.636]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":97.5,"s":[{"i":[[0.955,-43.615],[0,0],[-45.371,2.652],[0,0],[0,0],[0,40.869],[0,0],[25.175,3.5],[0,0],[0,0]],"o":[[0,0],[0.318,47.593],[0,0],[0,0],[26.129,-2.705],[0,0],[-0.318,-33.592],[0,0],[0,0],[-41.261,-4.852]],"v":[[-693.197,-672.705],[-692.189,674.705],[-601.975,746.515],[6.505,635.612],[457.076,558.093],[507.109,489.902],[506.667,-490.686],[457.596,-554.186],[4.075,-633.03],[-608.126,-744.273]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":98.334,"s":[{"i":[[0.818,-43.223],[0,0],[-44.664,2.273],[0,0],[0,0],[0,40.869],[0,0],[27.353,3],[0,0],[0,0]],"o":[[0,0],[0.273,46.632],[0,0],[0,0],[28.171,-2.318],[0,0],[-0.273,-34.632],[0,0],[0,0],[-41.142,-4.159]],"v":[[-705.455,-664.818],[-704.591,666.818],[-615.951,738.727],[4.869,636.239],[475.752,562.33],[526.665,492.631],[526,-493.159],[475.769,-558.159],[3.069,-632.455],[-622.224,-737.091]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100,"s":[{"i":[[0.545,-42.438],[0,0],[-43.251,1.515],[0,0],[0,0],[0,40.869],[0,0],[31.71,2],[0,0],[0,0]],"o":[[0,0],[0.182,44.711],[0,0],[0,0],[32.255,-1.545],[0,0],[-0.182,-36.711],[0,0],[0,0],[-40.902,-2.773]],"v":[[-723.303,-651.879],[-722.727,654.212],[-635.57,726.485],[1.598,637.492],[498.104,569.803],[562.443,499.754],[561.667,-498.773],[497.782,-566.773],[1.056,-632.97],[-643.418,-724.727]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":100.834,"s":[{"i":[[0.409,-42.046],[0,0],[-42.544,1.136],[0,0],[0,0],[0,40.869],[0,0],[33.888,1.5],[0,0],[0,0]],"o":[[0,0],[0.136,43.75],[0,0],[0,0],[34.297,-1.159],[0,0],[-0.136,-37.751],[0,0],[0,0],[-40.782,-2.08]],"v":[[-731.727,-645.409],[-731.295,647.909],[-644.879,720.364],[-0.038,638.119],[509.28,573.54],[577.332,503.315],[576.5,-501.58],[508.788,-571.08],[0.05,-633.227],[-653.516,-718.545]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":101.666,"s":[{"i":[[0.273,-41.654],[0,0],[-41.837,0.758],[0,0],[0,0],[0,40.869],[0,0],[36.066,1],[0,0],[0,0]],"o":[[0,0],[0.091,42.79],[0,0],[0,0],[36.339,-0.773],[0,0],[-0.091,-38.79],[0,0],[0,0],[-40.663,-1.386]],"v":[[-737.152,-638.939],[-736.864,641.606],[-651.189,714.242],[-1.674,638.746],[520.456,577.277],[590.222,506.877],[589.333,-504.386],[519.795,-575.386],[-0.956,-633.485],[-660.613,-712.364]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":103.334,"s":[{"i":[[0,-40.869],[0,0],[-40.423,0],[0,0],[0,0],[0,40.869],[0,0],[40.423,0],[0,0],[0,0]],"o":[[0,0],[0,40.869],[0,0],[0,0],[40.423,0],[0,0],[0,-40.869],[0,0],[0,0],[-40.423,0]],"v":[[-744,-626],[-744,629],[-659.808,702],[-4.946,640],[542.808,584.75],[616,514],[615,-510],[541.808,-584],[-2.968,-634],[-670.808,-700]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":105.834,"s":[{"i":[[0,-40.869],[0,0],[-40.423,0],[0,0],[0,0],[0,40.869],[0,0],[40.423,0],[0,0],[0,0]],"o":[[0,0],[0,40.869],[0,0],[0,0],[40.423,0],[0,0],[0,-40.869],[0,0],[0,0],[-40.423,0]],"v":[[-744.842,-614.959],[-744.6,619.497],[-661.426,691.301],[-2.779,639.354],[564.646,592.557],[643.41,522.301],[642.956,-517.867],[564.192,-591.867],[-2.968,-634.219],[-671.649,-688.959]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":109.166,"s":[{"i":[[0,-40.869],[0,0],[-40.423,0],[0,0],[0,0],[0,40.869],[0,0],[40.423,0],[0,0],[0,0]],"o":[[0,0],[0,40.869],[0,0],[0,0],[40.423,0],[0,0],[0,-40.869],[0,0],[0,0],[-40.423,0]],"v":[[-745.964,-600.237],[-745.4,606.826],[-663.584,677.035],[0.11,638.493],[593.764,602.967],[672.957,533.369],[673.23,-528.356],[594.037,-602.356],[-2.968,-634.512],[-672.771,-674.237]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111.666,"s":[{"i":[[0,-40.869],[0,0],[-40.423,0],[0,0],[0,0],[0,40.869],[0,0],[40.423,0],[0,0],[0,0]],"o":[[0,0],[0,40.869],[0,0],[0,0],[40.423,0],[0,0],[0,-40.869],[0,0],[0,0],[-40.423,0]],"v":[[-746.806,-589.196],[-746,597.323],[-665.202,666.336],[2.276,637.847],[615.603,610.775],[688.795,541.67],[689.614,-536.222],[616.422,-610.222],[-2.968,-634.731],[-673.613,-663.196]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":118,"s":[{"i":[[0,-40.869],[0,0],[-46.482,1.895],[0,0],[0,0],[0,40.869],[0,0],[40.423,0],[0,0],[0,0]],"o":[[0,0],[-1,44.868],[0,0],[0,0],[40.423,0],[0,0],[-0.105,-46.5],[0,0],[0,0],[-40.423,0]],"v":[[-736,-575.053],[-735,579.632],[-656.018,651.105],[8.054,637.474],[642.439,625.645],[714.632,557.105],[715.105,-550],[641.913,-624],[-2.968,-635.316],[-662.808,-649.053]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":126,"s":[{"i":[[0,-40.869],[0,0],[-44.048,1],[0,0],[0,0],[3,43.078],[0,0],[40.423,0],[0,0],[0,0]],"o":[[0,0],[0,40.869],[0,0],[0,0],[43.733,1],[0,0],[1.328,-42.15],[0,0],[0,0],[-40.423,0]],"v":[[-725.575,-563.749],[-725.75,566.434],[-649.952,640.197],[0.904,637.851],[657.767,633.903],[730.959,560.672],[730.922,-559.6],[657.73,-633.6],[-2.968,-638.442],[-656.133,-637.749]],"c":true}]},{"t":135,"s":[{"i":[[0,-40.869],[0,0],[-40.423,0],[0,0],[0,0],[0,40.869],[0,0],[40.423,0],[0,0],[0,0]],"o":[[0,0],[0,40.869],[0,0],[0,0],[40.423,0],[0,0],[0,-40.869],[0,0],[0,0],[-40.423,0]],"v":[[-725,-571],[-725.945,569],[-652.753,640],[-5.891,640],[660.862,640],[734.055,569],[735,-571],[661.808,-639],[-2.968,-639],[-651.808,-639]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-3,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":386,"st":60,"bm":10}],"markers":[]} \ 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 15423af9f02c..11483d43abd1 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Stembystand"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-kodeskandeerder"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Ontsluit"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Toestel is gesluit"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skandeer tans gesig"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Stuur"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Kan nie gesig herken nie"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Gebruik eerder vingerafdruk"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Gesigslot is onbeskikbaar"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth gekoppel."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batterypersentasie is onbekend."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Gekoppel aan <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Vliegtuigmodus."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN aan."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Battery <xliff:g id="NUMBER">%d</xliff:g> persent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Battery is op <xliff:g id="PERCENTAGE">%1$d</xliff:g> persent; <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Battery laai tans, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Battery is op <xliff:g id="PERCENTAGE">%d</xliff:g> persent; laaiproses is onderbreek om die battery te beskerm."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Battery is op <xliff:g id="PERCENTAGE">%1$d</xliff:g> persent; <xliff:g id="TIME">%2$s</xliff:g>, laaiproses is onderbreek om die battery te beskerm."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Sien alle kennisgewings"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter geaktiveer."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Luitoestel-vibreer."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontsluit om te gebruik"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kon nie jou kaarte kry nie; probeer later weer"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Sluitskerminstellings"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-kodeskandeerder"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Dateer tans op"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Jy sal nie jou volgende wekker <xliff:g id="WHEN">%1$s</xliff:g> hoor nie"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EE. d MMM."</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Maak <xliff:g id="APPNAME">%1$s</xliff:g> oop"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Om die <xliff:g id="APPNAME">%1$s</xliff:g>-app as ’n kortpad by te voeg, moet jy seker maak dat"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Die app opgestel is"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Minstens een kaart by Wallet gevoeg is"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Installeer ’n kamera-app"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Die app opgestel is"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Minstens een toestel beskikbaar is"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 6c9b07f033c0..93bb9e81faa5 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"የድምጽ እርዳታ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"የQR ኮድ መቃኛ"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"ተከፍቷል"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"መሣሪያ ተቆልፏል"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"የቅኝት ፊት"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ላክ"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"መልክን መለየት አልተቻለም"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"በምትኩ የጣት አሻራን ይጠቀሙ"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"በመልክ መክፈት አይገኝም"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ብሉቱዝ ተያይዟል።"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"የባትሪ መቶኛ አይታወቅም።"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"ከ<xliff:g id="BLUETOOTH">%s</xliff:g> ጋር ተገናኝቷል።"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"የአውሮፕላን ሁነታ።"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"ቪፒኤን በርቷል።"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"የባትሪ <xliff:g id="NUMBER">%d</xliff:g> መቶኛ።"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"የባትሪ <xliff:g id="PERCENTAGE">%1$d</xliff:g> መቶኛ፣ <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"ባትሪ ኃይል በመሙላት ላይ፣ <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"የባትሪ <xliff:g id="PERCENTAGE">%d</xliff:g> መቶኛ፣ ለባትሪ ጥበቃ ኃይል መሙላት ለአፍታ ቆሟል።"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"የባትሪ <xliff:g id="PERCENTAGE">%1$d</xliff:g> መቶኛ፣ <xliff:g id="TIME">%2$s</xliff:g>፣ ለባትሪ ጥበቃ ኃይል መሙላት ለአፍታ ቆሟል።"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"ሁሉንም ማሳወቂያዎች ይመልከቱ"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter ነቅቷል።"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"የስልክ ጥሪ ይንዘር።"</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ለማየት ይክፈቱ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"የእርስዎን ካርዶች ማግኘት ላይ ችግር ነበር፣ እባክዎ ቆይተው እንደገና ይሞክሩ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"የገጽ መቆለፊያ ቅንብሮች"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"የQR ኮድ መቃኛ"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"የስራ መገለጫ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"የአውሮፕላን ሁነታ"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE፣ MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 384087b4da84..6b80cd15806a 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"المساعد الصوتي"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"محفظة"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"الماسح الضوئي لرمز الاستجابة السريعة"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"تم فتح القفل"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"الجهاز مُقفل."</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"مسح الوجه"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"إرسال"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"يتعذّر التعرّف على الوجه."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"يمكنك استخدام بصمة إصبعك."</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ميزة \"فتح الجهاز بالتعرف على الوجه\" غير متاحة."</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"تم توصيل البلوتوث."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"نسبة شحن البطارية غير معروفة."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"متصل بـ <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"وضع الطيران."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"‏الشبكة الافتراضية الخاصة (VPN) قيد التفعيل."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"مستوى البطارية <xliff:g id="NUMBER">%d</xliff:g> في المائة."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"نسبة شحن البطارية <xliff:g id="PERCENTAGE">%1$d</xliff:g> في المائة؛ <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"جارٍ شحن البطارية، <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"نسبة شحن البطارية <xliff:g id="PERCENTAGE">%d</xliff:g> في المئة؛ تم إيقاف الشحن مؤقتًا لحماية البطارية."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"نسبة شحن البطارية <xliff:g id="PERCENTAGE">%1$d</xliff:g> في المئة؛ <xliff:g id="TIME">%2$s</xliff:g>؛ تم إيقاف الشحن مؤقتًا لحماية البطارية."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"الاطّلاع على جميع الإشعارات"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"تم تفعيل المبرقة الكاتبة."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"رنين مع الاهتزاز."</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"فتح القفل للاستخدام"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"حدثت مشكلة أثناء الحصول على البطاقات، يُرجى إعادة المحاولة لاحقًا."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"إعدادات شاشة القفل"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"ماسح ضوئي لرمز الاستجابة السريعة"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"‏EEE،‏ d‏ MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 411419c280ad..8fc1190a8ee0 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"কণ্ঠধ্বনিৰে সহায়"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"ৱালেট"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"কিউআৰ ক’ড স্কেনাৰ"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"আনলক কৰা আছে"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ডিভাইচটো লক হৈ আছে"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"চেহেৰা স্কেন কৰি থকা হৈছে"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"পঠিয়াওক"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"মুখাৱয়ব চিনিব নোৱাৰি"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ইয়াৰ সলনি ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ফেচ আনলক সুবিধা উপলব্ধ নহয়"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ব্লুটুথ সংযোগ হ’ল।"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"বেটাৰীৰ চাৰ্জৰ শতাংশ অজ্ঞাত।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>ৰ লগত সংযোগ কৰা হ’ল।"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"এয়াৰপ্লে’ন ম’ড।"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"ভিপিএন অন অৱস্থাত আছে।"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> শতাংশ বেটাৰী।"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> শতাংশ বেটাৰী, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"বেটাৰী চাৰ্জ হৈ আছে, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> শতাংশ।"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"বেটাৰী <xliff:g id="PERCENTAGE">%d</xliff:g> শতাংশ, বেটাৰীৰ সুৰক্ষাৰ বাবে চাৰ্জিং পজ কৰা হৈছে।"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"বেটাৰী <xliff:g id="PERCENTAGE">%1$d</xliff:g> শতাংশ, <xliff:g id="TIME">%2$s</xliff:g>, বেটাৰীৰ সুৰক্ষাৰ বাবে চাৰ্জিং পজ কৰা হৈছে।"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"আটাইবোৰ জাননী চাওক"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter সক্ষম কৰা হ\'ল৷"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"ৰিংগাৰ কম্পন অৱস্থাত আছে৷"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ব্যৱহাৰ কৰিবলৈ আনলক কৰক"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"আপোনাৰ কাৰ্ড লাভ কৰোঁতে এটা সমস্যা হৈছে, অনুগ্ৰহ কৰি পাছত পুনৰ চেষ্টা কৰক"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"লক স্ক্ৰীনৰ ছেটিং"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"কিউআৰ ক’ড স্কেনাৰ"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"আপডে’ট কৰি থকা হৈছে"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"এয়াৰপ্লে’ন ম’ড"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"আপুনি আপোনাৰ পিছৰটো এলাৰ্ম <xliff:g id="WHEN">%1$s</xliff:g> বজাত শুনা নাপাব"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> খোলক"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> এপ্‌টোক এটা শ্বৰ্টকাট হিচাপে যোগ দিবলৈ, এইকেইটা কথা নিশ্চিত কৰক"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• এপ্‌টো ছেট আপ কৰা হৈছে"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Walletত অতি কমেও এখন কাৰ্ড যোগ দিয়া হৈছে"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• এটা কেমেৰা এপ্ ইনষ্টল কৰক"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• এপ্‌টো ছেট আপ কৰা হৈছে"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• অতি কমেও এটা ডিভাইচ উপলব্ধ"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index f3e281fe49a4..9783430ba9be 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Səs Yardımçısı"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Pulqabı"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR Kodu Skaneri"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Kiliddən çıxarılmış"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Cihaz kilidlənib"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Üzün skan edilməsi"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Göndərin"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Üzü tanımaq olmur"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Barmaq izi istifadə edin"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Üz ilə kiliddən çıxarma əlçatan deyil"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth qoşulub."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batareyanın faizi naməlumdur."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> üzərindən qoşuldu."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Uçuş rejimi"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN aktivdir."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Batareya <xliff:g id="NUMBER">%d</xliff:g> faizdir."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batareya <xliff:g id="PERCENTAGE">%1$d</xliff:g> faizdir, <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batareya doldurulur, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%% faiz."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batareya <xliff:g id="PERCENTAGE">%d</xliff:g> faizdir, batareyanın qorunması üçün şarj durdurulub."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batareya <xliff:g id="PERCENTAGE">%1$d</xliff:g> faizdir, <xliff:g id="TIME">%2$s</xliff:g>, batareyanın qorunması üçün şarj durdurulub."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Bütün bildirişlərə baxın"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter aktivləşdirilib."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Zəng vibrasiyası"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"İstifadə etmək üçün kiliddən çıxarın"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kartların əldə edilməsində problem oldu, sonra yenidən cəhd edin"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Kilid ekranı ayarları"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR kod skaneri"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Güncəllənir"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Təyyarə rejimi"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> zaman növbəti xəbərdarlığınızı eşitməyəcəksiniz"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"HHH, AAA g"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"s:dd"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"ss:dd"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> tətbiqini açın"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> tətbiqini qısayol kimi əlavə etmək üçün bunları təmin edin:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Tətbiq ayarlanmalıdır"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Pulqabına ən azı bir kart əlavə edilməlidir"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Kamera tətbiqini quraşdırın"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Tətbiq ayarlanmalıdır"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ən azı bir cihaz əlçatandır"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 0fc6331e6752..8478adbb9964 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovna pomoć"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Novčanik"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Skener QR koda"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Otključano"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skeniranje lica"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošalji"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Lice nije prepoznato"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Koristite otisak prsta"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Otključavanje licem nije dostupno"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth je priključen."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Procenat napunjenosti baterije nije poznat."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezani ste sa <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Režim rada u avionu."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN je uključen."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Baterija je na <xliff:g id="NUMBER">%d</xliff:g> posto."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Baterija je na <xliff:g id="PERCENTAGE">%1$d</xliff:g> posto, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Baterija se puni, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> posto."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Baterija je na <xliff:g id="PERCENTAGE">%d</xliff:g> posto, punjenje je pauzirano da bi se zaštitila baterija."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Baterija je na <xliff:g id="PERCENTAGE">%1$d</xliff:g> posto, <xliff:g id="TIME">%2$s</xliff:g>, punjenje je pauzirano da bi se zaštitila baterija."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Pogledajte sva obaveštenja"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter je omogućen."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibracija zvona."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključaj radi korišćenja"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Došlo je do problema pri preuzimanju kartica. Probajte ponovo kasnije"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Podešavanja zaključanog ekrana"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Skener QR koda"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ažurira se"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim rada u avionu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sledeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"DDD, d. MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"s:min"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"č:min"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvorite: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Da biste dodali aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g> kao prečicu, uverite se:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• da je aplikacija podešena"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• da je u Novčanik dodata barem jedna kartica"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• da ste instalirali aplikaciju za kameru"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• da je aplikacija podešena"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• da je dostupan barem jedan uređaj"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 85458bf2216e..44ad22765a1a 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Галасавая дапамога"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Кашалёк"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Сканер QR-кодаў"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Разблакіравана"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Прылада заблакіравана"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Сканіраванне твару"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Адправіць"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Твар не распазнаны"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Скарыстайце адбітак пальца"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Распазнаванне твару не працуе"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-сувязь."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Працэнт зараду акумулятара невядомы."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Падлучаны да <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Рэжым палёту."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN уключана."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Працэнт зараду акумулятара: <xliff:g id="NUMBER">%d</xliff:g>."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Працэнт зараду акумулятара: <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Акумулятар зараджаецца. Бягучы зарад: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Працэнт зараду акумулятара: <xliff:g id="PERCENTAGE">%d</xliff:g>. Дзеля зберажэння акумулятара зарадка прыпынена."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Працэнт зараду акумулятара: <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>. Дзеля зберажэння акумулятара зарадка прыпынена."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Паказаць усе апавяшчэнні"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter уключаны."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Выклік з вібрацыяй."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Разблакіраваць для выкарыстання"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Узнікла праблема з загрузкай вашых карт. Паўтарыце спробу пазней"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Налады экрана блакіроўкі"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Сканер QR-кодаў"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Абнаўленне…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Працоўны профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Рэжым палёту"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Вы не пачуеце наступны будзільнік <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Адкрыць праграму \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Каб дадаць ярлык для праграмы \"<xliff:g id="APPNAME">%1$s</xliff:g>\", патрабуюцца наступныя ўмовы:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Праграма наладжана."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• У Кашалёк дададзена хаця б адна картка."</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Усталявана праграма \"Камера\"."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Праграма наладжана."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Даступная хаця б адна прылада."</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 44ee85c06d5c..240342a1cb9e 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Гласова помощ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Портфейл"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Скенер за QR кодове"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Отключено"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Устройството е заключено"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Извършва се сканиране на лице"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Изпращане"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Лицето не е разпознато"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Използвайте отпечатък"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"„Отключване с лице“ не е налице"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth е включен."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Процентът на батерията е неизвестен."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Има връзка с <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Самолетен режим."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"Функцията за виртуална частна мрежа (VPN) е включена."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> процента батерия."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Батерията е на <xliff:g id="PERCENTAGE">%1$d</xliff:g> процента, до изтощаването ѝ остава <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Батерията се зарежда, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Батерията е на <xliff:g id="PERCENTAGE">%d</xliff:g> процента. Зареждането е поставено на пауза с цел да се запази батерията."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Батерията е на <xliff:g id="PERCENTAGE">%1$d</xliff:g> процента, до изтощаването ѝ остава <xliff:g id="TIME">%2$s</xliff:g>. Зареждането е поставено на пауза с цел да се запази батерията."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Вижте всички известия"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter бе активиран."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Вибрира при звънене."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Отключване с цел използване"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"При извличането на картите ви възникна проблем. Моля, опитайте отново по-късно"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Настройки за заключения екран"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"скенер за QR кодове"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Актуализира се"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Потребителски профил в Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Самолетен режим"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Няма да чуете следващия си будилник в <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Отваряне на <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"За да добавите пряк път към приложението <xliff:g id="APPNAME">%1$s</xliff:g>, трябва да се уверите в следното:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Приложението е настроено."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• В Wallet е добавена поне една карта."</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Инсталирано е приложение за камера."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Приложението е настроено."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Налице е поне едно устройство."</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 5c4013d0f995..5f009d763b33 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ভয়েস সহায়তা"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR কোড স্ক্যানার"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"আনলক করা হয়েছে"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ডিভাইস লক করা আছে"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ফেস স্ক্যান করা হচ্ছে"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"পাঠান"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ফেস শনাক্ত করা যায়নি"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"পরিবর্তে ফিঙ্গারপ্রিন্ট ব্যবহার করুন"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"\'ফেস আনলক\' উপলভ্য নেই"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ব্লুটুথ সংযুক্ত হয়েছে৷"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ব্যাটারি কত শতাংশ আছে তা জানা যায়নি।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>এ সংযুক্ত হয়ে আছে।"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"বিমান মোড৷"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN চালু আছে।"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> শতাংশ ব্যাটারি রয়েছে৷"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"ব্যাটারির চার্জ <xliff:g id="PERCENTAGE">%1$d</xliff:g> শতাংশ আছে, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"ব্যাটারি চার্জ হচ্ছে, এখন <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> শতাংশ চার্জ আছে৷"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"ব্যাটারির চার্জ <xliff:g id="PERCENTAGE">%d</xliff:g> শতাংশ আছে, ব্যাটারির সুরক্ষার জন্য চার্জ দেওয়া পজ করা হয়েছে।"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"ব্যাটারির চার্জ <xliff:g id="PERCENTAGE">%1$d</xliff:g> শতাংশ আছে, <xliff:g id="TIME">%2$s</xliff:g>, ব্যাটারির সুরক্ষার জন্য চার্জ দেওয়া পজ করা হয়েছে।"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"সমস্ত বিজ্ঞপ্তি দেখুন"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"টেলি টাইপরাইটার সক্ষম করা আছে৷"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"রিং বাজার সাথে স্পন্দিত করুন৷"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ব্যবহার করতে আনলক করুন"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"আপনার কার্ড সংক্রান্ত তথ্য পেতে সমস্যা হয়েছে, পরে আবার চেষ্টা করুন"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"লক স্ক্রিন সেটিংস"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR কোড স্ক্যানার খুলুন"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"আপডেট করা হচ্ছে"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কাজের প্রোফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"বিমান মোড"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"আপনি আপনার পরবর্তী <xliff:g id="WHEN">%1$s</xliff:g> অ্যালার্ম শুনতে পাবেন না"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> খুলুন"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"শর্টকাট হিসেবে <xliff:g id="APPNAME">%1$s</xliff:g> অ্যাপ যোগ করতে, এই বিষয়গুলি নিশ্চিত করুন"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• অ্যাপ সেট-আপ করা হয়ে গেছে"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• অন্তত একটি কার্ড Wallet-এ যোগ করা হয়েছে"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• ক্যামেরা অ্যাপ ইনস্টল করুন"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• অ্যাপ সেট-আপ করা হয়ে গেছে"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• অন্তত একটি ডিভাইস উপলভ্য"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 6b3c8952a0f2..4b3da55564be 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovna pomoć"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Novčanik"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Skener QR koda"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Otključano"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skeniranje lica"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošalji"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Nije moguće prepoznati lice"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Koristite otisak prsta"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Otključavanje licem je nedostupno"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth je povezan."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Postotak napunjenosti baterije nije poznat"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezan na <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Način rada u avionu."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN uključen."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Baterija na <xliff:g id="NUMBER">%d</xliff:g> posto."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Baterija je napunjena <xliff:g id="PERCENTAGE">%1$d</xliff:g> posto. <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Punjenje baterije, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Baterija je napunjena <xliff:g id="PERCENTAGE">%d</xliff:g> posto. Punjenje je pauzirano radi njene zaštite."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Baterija je napunjena <xliff:g id="PERCENTAGE">%1$d</xliff:g> posto. <xliff:g id="TIME">%2$s</xliff:g>. Punjenje je pauzirano radi njene zaštite."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Vidite sva obavještenja"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Omogućena opcija TeleTypewriter."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Zvuk zvona na vibraciji."</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključajte da koristite"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Došlo je do problema prilikom preuzimanja vaših kartica. Pokušajte ponovo kasnije"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Postavke zaključavanja ekrana"</string>
- <string name="qr_code_scanner_title" msgid="1938155688725760702">"Čitač QR koda"</string>
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Skener QR koda"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ažuriranje"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil za posao"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u avionu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"DDD, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvori aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Da dodate aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g> kao prečicu, pobrinite se za sljedeće"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Aplikacija je postavljena"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Najmanje jedna kartica je dodana u Novčanik"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Instalirajte aplikaciju kamere"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Aplikacija je postavljena"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Dostupan je najmanje jedan uređaj"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 9892d26f0c25..25106d6386f3 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistència per veu"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Escàner de codis QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Desbloquejat"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositiu bloquejat"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"S\'està escanejant la cara"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Envia"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"No es reconeix la cara"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Utilitza l\'empremta digital"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Desbloqueig facial no està disponible"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connectat."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Es desconeix el percentatge de bateria."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"S\'ha connectat a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Mode d\'avió."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN activada"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> per cent de bateria."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> per cent de bateria fins a les <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"La bateria s\'està carregant (<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%)."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"<xliff:g id="PERCENTAGE">%d</xliff:g> per cent de bateria; la càrrega s\'ha posat en pausa per protegir la bateria."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> per cent de bateria fins a les <xliff:g id="TIME">%2$s</xliff:g>; la càrrega s\'ha posat en pausa per protegir la bateria."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Mostra totes les notificacions"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletip activat."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Mode vibració."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloqueja per utilitzar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Hi ha hagut un problema en obtenir les teves targetes; torna-ho a provar més tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuració de la pantalla de bloqueig"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"escàner de codis QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"S\'està actualitzant"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de treball"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode d\'avió"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> no sentiràs la pròxima alarma"</string>
@@ -548,7 +543,7 @@
<string name="notification_unblockable_call_desc" msgid="5907328164696532169">"Les notificacions de trucades no es poden modificar."</string>
<string name="notification_multichannel_desc" msgid="7414593090056236179">"Aquest grup de notificacions no es pot configurar aquí"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"Notificació mitjançant aplicació intermediària"</string>
- <string name="notification_channel_dialog_title" msgid="6856514143093200019">"Totes les notificacions de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="notification_channel_dialog_title" msgid="6856514143093200019">"Totes les notificacions de: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="see_more_title" msgid="7409317011708185729">"Mostra\'n més"</string>
<string name="feedback_alerted" msgid="5192459808484271208">"El sistema &lt;b&gt;ha augmentat a Predeterminat&lt;/b&gt; el nivell d\'aquesta notificació."</string>
<string name="feedback_silenced" msgid="9116540317466126457">"El sistema &lt;b&gt;ha disminuït a Silenci&lt;/b&gt; el nivell d\'aquesta notificació."</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"hh:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Obre <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Per afegir l\'aplicació <xliff:g id="APPNAME">%1$s</xliff:g> com a drecera, assegura\'t que:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• L\'aplicació està configurada."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Almenys s\'ha afegit una targeta a Wallet."</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Tens una aplicació de càmera."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• L\'aplicació està configurada."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Almenys un dispositiu està disponible."</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 18894d270aec..5c5d6372c0a2 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Hlasová asistence"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Peněženka"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Čtečka QR kódů"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Odemknuto"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Zařízení uzamčeno"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skenování obličeje"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Odeslat"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Obličej nelze rozpoznat"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Použijte otisk prstu"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Odemknutí obličejem není k dispozici"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Rozhraní Bluetooth je připojeno."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Procento baterie není známé."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Připojeno k zařízení <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Režim Letadlo."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN je zapnuto."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Stav baterie: <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Baterie je na <xliff:g id="PERCENTAGE">%1$d</xliff:g> procentech, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Baterie se nabíjí. Nabito: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Baterie je na <xliff:g id="PERCENTAGE">%d</xliff:g> procentech, nabíjení pozastaveno kvůli ochraně baterie."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Baterie je na <xliff:g id="PERCENTAGE">%1$d</xliff:g> procentech, <xliff:g id="TIME">%2$s</xliff:g>, nabíjení pozastaveno kvůli ochraně baterie."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Zobrazit všechna oznámení"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Rozhraní TeleTypewriter zapnuto."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibrační vyzvánění."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odemknout a použít"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Při načítání karet došlo k problému, zkuste to později"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavení obrazovky uzamčení"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Čtečka QR kódů"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Probíhá aktualizace"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovní profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim Letadlo"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Svůj další budík <xliff:g id="WHEN">%1$s</xliff:g> neuslyšíte"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d. MMMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"H:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otevřít <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Podmínky pro to, aby aplikaci <xliff:g id="APPNAME">%1$s</xliff:g> bylo možné přidat jako zkratku:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Aplikace je nastavena"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Do Peněženky byla přidána alespoň jedna karta"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Je nainstalována aplikace pro fotoaparát"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Aplikace je nastavena"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Je k dispozici alespoň jedno zařízení"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 8dbe9786f17f..5831983beed1 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Taleassistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-kodescanner"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Låst op"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheden er låst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanner ansigt"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Ansigt kan ikke genkendes"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Brug fingeraftryk i stedet"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Ansigtslås er utilgængelig"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth tilsluttet."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batteriniveauet er ukendt."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Tilsluttet <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Flytilstand."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN er slået til."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Batteri <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batteriniveauet er på <xliff:g id="PERCENTAGE">%1$d</xliff:g> procent, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batteriet oplades. <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batteriniveauet er på <xliff:g id="PERCENTAGE">%d</xliff:g> procent, opladning er sat på pause for at beskytte batteriet."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batteriniveauet er på <xliff:g id="PERCENTAGE">%1$d</xliff:g> procent, <xliff:g id="TIME">%2$s</xliff:g>, opladning er sat på pause for at beskytte batteriet."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Se alle notifikationer"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter aktiveret."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Ringervibration."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås op for at bruge"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Dine kort kunne ikke hentes. Prøv igen senere."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lås skærmindstillinger"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-kodescanner"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Opdaterer"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbejdsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flytilstand"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Du vil ikke kunne høre din næste alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d. MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"tt.mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk.mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Åbn <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Før du tilføjer <xliff:g id="APPNAME">%1$s</xliff:g>-appen som en genvej, skal du sørge for følgende:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Appen er konfigureret"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Mindst ét kort er føjet til Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Installer en kameraapp"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Appen er konfigureret"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Mindst én enhed er tilgængelig"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 5191b47a2c68..a0a18d211c81 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Sprachassistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-Code-Scanner"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Entsperrt"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Gerät gesperrt"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Gesicht wird gescannt"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Senden"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Gesicht nicht erkannt"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Fingerabdruck verwenden"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Entsperrung per Gesichtserkennung nicht verfügbar"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Mit Bluetooth verbunden"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akkustand unbekannt."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Mit <xliff:g id="BLUETOOTH">%s</xliff:g> verbunden"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Flugmodus"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN an."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Akku bei <xliff:g id="NUMBER">%d</xliff:g> Prozent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Akku bei <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Akku wird aufgeladen, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> Prozent."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Akku bei <xliff:g id="PERCENTAGE">%d</xliff:g>. Zum Schutz des Akkus wurde das Laden pausiert."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Akku bei <xliff:g id="PERCENTAGE">%1$d</xliff:g>. <xliff:g id="TIME">%2$s</xliff:g>. Zum Schutz des Akkus wurde das Laden pausiert."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Alle Benachrichtigungen ansehen"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Schreibtelefonie aktiviert"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Klingeltonmodus \"Vibration\""</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Zum Verwenden entsperren"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Beim Abrufen deiner Karten ist ein Fehler aufgetreten – bitte versuch es später noch einmal"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Einstellungen für den Sperrbildschirm"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-Code-Scanner"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Wird aktualisiert…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbeitsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugmodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Lautloser Weckruf <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> öffnen"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Wenn du die <xliff:g id="APPNAME">%1$s</xliff:g> App als eine Verknüpfung hinzufügen möchtest, müssen folgende Bedingungen erfüllt sein"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Die App ist eingerichtet"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Wallet wurde mindestens eine Karte hinzugefügt"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Kamera-App ist installiert"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Die App ist eingerichtet"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Mindestens ein Gerät ist verfügbar"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 448e1e03acb5..bd63f4132685 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Φωνητική υποβοήθηση"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Πορτοφόλι"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Σάρωση κωδικών QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Ξεκλειδώθηκε"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Η συσκευή κλειδώθηκε"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Σάρωση προσώπου"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Αποστολή"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Αδύνατη η αναγν. προσώπου"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Χρησιμ. δακτυλ. αποτύπ."</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Ξεκλ. με πρόσωπο μη διαθ."</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Το Bluetooth είναι συνδεδεμένο."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Άγνωστο ποσοστό μπαταρίας."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Συνδέθηκε στο <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Λειτουργία πτήσης."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ενεργό."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Μπαταρία <xliff:g id="NUMBER">%d</xliff:g> τοις εκατό."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Μπαταρία <xliff:g id="PERCENTAGE">%1$d</xliff:g> τοις εκατό, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Φόρτιση μπαταρίας: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Μπαταρία <xliff:g id="PERCENTAGE">%d</xliff:g> τοις εκατό, η φόρτιση τέθηκε σε παύση για την προστασία της μπαταρίας."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Μπαταρία <xliff:g id="PERCENTAGE">%1$d</xliff:g> τοις εκατό, <xliff:g id="TIME">%2$s</xliff:g>, η φόρτιση τέθηκε σε παύση για την προστασία της μπαταρίας."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Δείτε όλες τις ειδοποιήσεις"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Το TeleTypewriter ενεργοποιήθηκε."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Δόνηση ειδοποίησης ήχου."</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"Παρουσιάστηκε πρόβλημα με τη λήψη των καρτών σας. Δοκιμάστε ξανά αργότερα"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ρυθμίσεις κλειδώματος οθόνης"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"Σάρωση κωδικών QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ενημέρωση"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Προφίλ εργασίας"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Λειτουργία πτήσης"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Δεν θα ακούσετε το επόμενο ξυπνητήρι σας <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"ΗΗΗ, ΜΜΜ η"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"ώ:λλ"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:λλ"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Άνοιγμα <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Για να προσθέσετε την εφαρμογή <xliff:g id="APPNAME">%1$s</xliff:g> ως συντόμευση, βεβαιωθείτε ότι"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Η εφαρμογή έχει ρυθμιστεί"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Έχει προστεθεί τουλάχιστον μία κάρτα στο Πορτοφόλι"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Εγκαταστήσατε μια εφαρμογή κάμερας"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Η εφαρμογή έχει ρυθμιστεί"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Είναι διαθέσιμη τουλάχιστον μία συσκευή"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index bfa63f6e0a59..f9bdb0f682a7 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -168,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Can’t recognise face"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use fingerprint instead"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Face Unlock unavailable"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -180,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Aeroplane mode"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN on."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Battery <xliff:g id="NUMBER">%d</xliff:g> per cent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Battery <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cent; <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Battery charging, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> percent."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Battery <xliff:g id="PERCENTAGE">%d</xliff:g> per cent; charging paused for battery protection."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Battery <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cent; <xliff:g id="TIME">%2$s</xliff:g>, charging paused for battery protection."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"See all notifications"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter enabled."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Ringer vibrate."</string>
@@ -501,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"QR code scanner"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -994,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"To add the <xliff:g id="APPNAME">%1$s</xliff:g> app as a shortcut, make sure"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• The app is set up"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• At least one card has been added to Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Install a camera app"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• The app is set up"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• At least one device is available"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancel"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Flip now"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Unfold phone for a better selfie"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 879d88b8a7e6..8a55b0be2fe9 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -168,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Can’t recognize face"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use fingerprint instead"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Face Unlock unavailable"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -180,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Airplane mode."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN on."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Battery <xliff:g id="NUMBER">%d</xliff:g> percent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Battery <xliff:g id="PERCENTAGE">%1$d</xliff:g> percent, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Battery charging, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> percent."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Battery <xliff:g id="PERCENTAGE">%d</xliff:g> percent, charging paused for battery protection."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Battery <xliff:g id="PERCENTAGE">%1$d</xliff:g> percent, <xliff:g id="TIME">%2$s</xliff:g>, charging paused for battery protection."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"See all notifications"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter enabled."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Ringer vibrate."</string>
@@ -370,7 +366,7 @@
<string name="user_remove_user_message" msgid="6702834122128031833">"All apps and data of this user will be deleted."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Remove"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play."</string>
- <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play."</string>
+ <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that\'s visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"Allow <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> to share or record?"</string>
@@ -501,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards, please try again later"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"QR code scanner"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -994,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"To add the <xliff:g id="APPNAME">%1$s</xliff:g> app as a shortcut, make sure"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• The app is set up"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• At least one card has been added to Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Install a camera app"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• The app is set up"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• At least one device is available"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancel"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Flip now"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Unfold phone for a better selfie"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index bfa63f6e0a59..f9bdb0f682a7 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -168,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Can’t recognise face"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use fingerprint instead"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Face Unlock unavailable"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -180,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Aeroplane mode"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN on."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Battery <xliff:g id="NUMBER">%d</xliff:g> per cent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Battery <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cent; <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Battery charging, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> percent."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Battery <xliff:g id="PERCENTAGE">%d</xliff:g> per cent; charging paused for battery protection."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Battery <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cent; <xliff:g id="TIME">%2$s</xliff:g>, charging paused for battery protection."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"See all notifications"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter enabled."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Ringer vibrate."</string>
@@ -501,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"QR code scanner"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -994,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"To add the <xliff:g id="APPNAME">%1$s</xliff:g> app as a shortcut, make sure"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• The app is set up"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• At least one card has been added to Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Install a camera app"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• The app is set up"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• At least one device is available"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancel"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Flip now"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Unfold phone for a better selfie"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index bfa63f6e0a59..f9bdb0f682a7 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -168,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Can’t recognise face"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use fingerprint instead"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Face Unlock unavailable"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connected."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Battery percentage unknown."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -180,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Aeroplane mode"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN on."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Battery <xliff:g id="NUMBER">%d</xliff:g> per cent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Battery <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cent; <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Battery charging, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> percent."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Battery <xliff:g id="PERCENTAGE">%d</xliff:g> per cent; charging paused for battery protection."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Battery <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cent; <xliff:g id="TIME">%2$s</xliff:g>, charging paused for battery protection."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"See all notifications"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter enabled."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Ringer vibrate."</string>
@@ -501,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"QR code scanner"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updating"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -994,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Open <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"To add the <xliff:g id="APPNAME">%1$s</xliff:g> app as a shortcut, make sure"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• The app is set up"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• At least one card has been added to Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Install a camera app"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• The app is set up"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• At least one device is available"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancel"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Flip now"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Unfold phone for a better selfie"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Flip to front display for a better selfie?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use the rear-facing camera for a wider photo with higher resolution."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ This screen will turn off"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 5f0be61c7228..0c4c62a40707 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -168,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‏‎Can’t recognize face‎‏‎‎‏‎"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‎‎Use fingerprint instead‎‏‎‎‏‎"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎Face Unlock unavailable‎‏‎‎‏‎"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎Bluetooth connected.‎‏‎‎‏‎"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎Battery percentage unknown.‎‏‎‎‏‎"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‎Connected to ‎‏‎‎‏‏‎<xliff:g id="BLUETOOTH">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
@@ -180,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎Airplane mode.‎‏‎‎‏‎"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‏‏‎‏‏‎VPN on.‎‏‎‎‏‎"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎Battery ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%d</xliff:g>‎‏‎‎‏‏‏‎ percent.‎‏‎‎‏‎"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‏‎Battery ‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%1$d</xliff:g>‎‏‎‎‏‏‏‎ percent, ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‎Battery charging, ‎‏‎‎‏‏‎<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>‎‏‎‎‏‏‏‎ percent.‎‏‎‎‏‎"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‎‏‎‏‏‎Battery ‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%d</xliff:g>‎‏‎‎‏‏‏‎ percent, charging paused for battery protection.‎‏‎‎‏‎"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‏‎‎Battery ‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%1$d</xliff:g>‎‏‎‎‏‏‏‎ percent, ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎, charging paused for battery protection.‎‏‎‎‏‎"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎See all notifications‎‏‎‎‏‎"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‎TeleTypewriter enabled.‎‏‎‎‏‎"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎Ringer vibrate.‎‏‎‎‏‎"</string>
@@ -501,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎There was a problem getting your cards, please try again later‎‏‎‎‏‎"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎Lock screen settings‎‏‎‎‏‎"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎QR code scanner‎‏‎‎‏‎"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎Updating‎‏‎‎‏‎"</string>
<string name="status_bar_work" msgid="5238641949837091056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎Work profile‎‏‎‎‏‎"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎Airplane mode‎‏‎‎‏‎"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎You won\'t hear your next alarm ‎‏‎‎‏‏‎<xliff:g id="WHEN">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
@@ -994,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎EEE, MMM d‎‏‎‎‏‎"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎‏‎‎‏‎‎‏‎h:mm‎‏‎‎‏‎"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎kk:mm‎‏‎‎‏‎"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‎Open ‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎‏‎To add the ‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ app as a shortcut, make sure‎‏‎‎‏‎"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‎• The app is set up‎‏‎‎‏‎"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎• At least one card has been added to Wallet‎‏‎‎‏‎"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‎• Install a camera app‎‏‎‎‏‎"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎• The app is set up‎‏‎‎‏‎"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎• At least one device is available‎‏‎‎‏‎"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‎Cancel‎‏‎‎‏‎"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‎Flip now‎‏‎‎‏‎"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎Unfold phone for a better selfie‎‏‎‎‏‎"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎Flip to front display for a better selfie?‎‏‎‎‏‎"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‎‎‎Use the rear-facing camera for a wider photo with higher resolution.‎‏‎‎‏‎"</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎✱ This screen will turn off‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 639e50d0b22e..d0908ab88c03 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistente voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Billetera"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Escáner de código QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Desbloqueado"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Escaneando rostro"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"No se reconoce el rostro"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Usa la huella dactilar"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Desbloqueo facial no disponible"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Se desconoce el porcentaje de la batería."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Modo de avión"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN activada"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Batería <xliff:g id="NUMBER">%d</xliff:g> por ciento"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batería: <xliff:g id="PERCENTAGE">%1$d</xliff:g> por ciento; <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batería cargando: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batería: <xliff:g id="PERCENTAGE">%d</xliff:g> por ciento. Se pausó la carga para proteger la batería."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batería: <xliff:g id="PERCENTAGE">%1$d</xliff:g> por ciento. <xliff:g id="TIME">%2$s</xliff:g>. Se pausó la carga para proteger la batería."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Ver todas las notificaciones"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletipo habilitado"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Timbre en vibración"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocurrió un problema al obtener las tarjetas; vuelve a intentarlo más tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración de pantalla de bloqueo"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Escáner de código QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Actualizando"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avión"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma a la(s) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Para agregar la app <xliff:g id="APPNAME">%1$s</xliff:g> como acceso directo, asegúrate que se cumplan los siguientes requisitos:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Se configuró la app."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Se agregó al menos una tarjeta a la Billetera."</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Se instaló la app de Cámara."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Se configuró la app."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Hay al menos un dispositivo disponible."</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 9acbe6f1b884..bd8ef36d56e1 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistente voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Escáner de códigos QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Desbloqueado"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Escaneando cara"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"No se reconoce la cara"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Usa la huella digital"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Desbloqueo facial no disponible"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Porcentaje de batería desconocido."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Modo Avión"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"La red VPN está activada."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> por ciento de batería"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batería al <xliff:g id="PERCENTAGE">%1$d</xliff:g> por ciento, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batería cargándose (<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%)."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batería al <xliff:g id="PERCENTAGE">%d</xliff:g> por ciento, carga pausada para proteger la batería."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batería al <xliff:g id="PERCENTAGE">%1$d</xliff:g> por ciento, <xliff:g id="TIME">%2$s</xliff:g>, carga pausada para proteger la batería."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Ver todas las notificaciones"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletipo habilitado"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Modo vibración"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Se ha producido un problema al obtener tus tarjetas. Inténtalo de nuevo más tarde."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ajustes de pantalla de bloqueo"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Escáner de códigos QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Actualizando"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo Avión"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Para añadir la aplicación <xliff:g id="APPNAME">%1$s</xliff:g> como acceso directo:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• La aplicación debe estar configurada"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Se debe haber añadido al menos una tarjeta a Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Debes instalar una aplicación de cámara"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• La aplicación debe estar configurada"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Al menos un dispositivo debe estar disponible"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 55ace2faa439..5a783c4284a7 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Häälabi"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-koodi skanner"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Avatud"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Seade on lukustatud"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Näo skannimine"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Saada"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Nägu ei õnnestu tuvastada"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Kasutage sõrmejälge"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Näoga avamine pole saadaval"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth on ühendatud."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Aku laetuse protsent on teadmata."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ühendatud: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Lennukirežiim."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN on sees."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Aku: <xliff:g id="NUMBER">%d</xliff:g> protsenti."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Aku: <xliff:g id="PERCENTAGE">%1$d</xliff:g> protsenti; akutoite kestus: <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Akut laetakse (<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%)."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Aku on <xliff:g id="PERCENTAGE">%d</xliff:g> protsenti laetud, laadimine on aku kaitsmiseks peatatud."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Aku on <xliff:g id="PERCENTAGE">%1$d</xliff:g> protsenti laetud; akutoite kestus: <xliff:g id="TIME">%2$s</xliff:g>; laadimine on aku kaitsmiseks peatatud."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Kõikide märguannete kuvamine"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter lubatud."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibreeriv kõlisti."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Avage kasutamiseks"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Teie kaartide hankimisel ilmnes probleem, proovige hiljem uuesti"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lukustuskuva seaded"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-koodi skanner"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Värskendamine"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Tööprofiil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lennukirežiim"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Te ei kuule järgmist äratust kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ava <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> otsetee lisamiseks veenduge järgmises."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Rakendus on seadistatud"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Vähemalt üks kaart on Walletisse lisatud"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Installige kaamerarakendus"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Rakendus on seadistatud"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Vähemalt üks seade on saadaval"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 89f1ebe163a9..d45bf6fda63e 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ahots-laguntza"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Diru-zorroa"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR kodeen eskanerra"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Desblokeatuta"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Gailua blokeatuta dago"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Aurpegia eskaneatzen"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Bidali"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Ezin da ezagutu aurpegia"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Erabili hatz-marka"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Aurpegi bidez desblokeatzeko eginbidea ez dago erabilgarri"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetootha konektatuta."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Bateriaren ehunekoa ezezaguna da."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> gailura konektatuta."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Hegaldi-modua"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN eginbidea aktibatuta."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Bateriaren karga: <xliff:g id="NUMBER">%d</xliff:g>."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Bateriak ehuneko <xliff:g id="PERCENTAGE">%1$d</xliff:g> dauka, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Kargatzen ari da bateria. Ehuneko <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> arte kargatu da oraingoz."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Bateriak ehuneko <xliff:g id="PERCENTAGE">%d</xliff:g> dauka, kargatze-prozesua pausatuta dago bateria babesteko."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Bateriak ehuneko <xliff:g id="PERCENTAGE">%1$d</xliff:g> dauka, <xliff:g id="TIME">%2$s</xliff:g>, kargatze-prozesua pausatuta dago bateria babesteko."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Ikusi jakinarazpen guztiak"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter gaituta."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Tonu-jotzailea dardara moduan."</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desblokeatu erabiltzeko"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Arazo bat izan da txartelak eskuratzean. Saiatu berriro geroago."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Pantaila blokeatuaren ezarpenak"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR kodeen eskanerra"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"Work profila"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hegaldi modua"</string>
@@ -996,4 +992,24 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Utzi"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Irauli"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Ireki telefonoa autoargazki hobeak ateratzeko"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Telefonoa irauli nahi duzu autoargazki hobeak ateratzeko?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Erabili atzeko kamera kalitate handiagoko argazki zabalago bat ateratzeko."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Pantaila itzali egingo da"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 1a8553cf0e5c..3d4e38b4e3aa 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"دستیار صوتی"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"کیف پول"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"کدخوان پاسخ‌سریع"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"قفل‌نشده"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"دستگاه قفل است"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"درحال اسکن کردن چهره"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ارسال"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"چهره شناسایی نشد"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"از اثر انگشت استفاده کنید"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"«قفل‌گشایی با چهره» دردسترس نیست"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"بلوتوث متصل است."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"درصد شارژ باتری مشخص نیست."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"به <xliff:g id="BLUETOOTH">%s</xliff:g> متصل شد."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"حالت هواپیما."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"‏VPN روشن است."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"باتری <xliff:g id="NUMBER">%d</xliff:g> درصد."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"درصد شارژ باتری: <xliff:g id="PERCENTAGE">%1$d</xliff:g>، <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"در حال شارژ باتری، <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> درصد"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"درصد شارژ باتری: <xliff:g id="PERCENTAGE">%d</xliff:g>، شارژ شدن برای محافظت از باتری موقتاً متوقف شد."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"درصد شارژ باتری: <xliff:g id="PERCENTAGE">%1$d</xliff:g>، <xliff:g id="TIME">%2$s</xliff:g>، شارژ شدن برای محافظت از باتری موقتاً متوقف شد."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"دیدن همه اعلان‌ها"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"تله‌تایپ فعال شد."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"زنگ لرزشی."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"برای استفاده، قفل را باز کنید"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"هنگام دریافت کارت‌ها مشکلی پیش آمد، لطفاً بعداً دوباره امتحان کنید"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"تنظیمات صفحه قفل"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"کدخوان پاسخ‌سریع"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"درحال به‌روزرسانی"</string>
<string name="status_bar_work" msgid="5238641949837091056">"نمایه کاری"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"حالت هواپیما"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"در ساعت <xliff:g id="WHEN">%1$s</xliff:g>، دیگر صدای زنگ ساعت را نمی‌شنوید"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"باز کردن <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"برای افزودن برنامه <xliff:g id="APPNAME">%1$s</xliff:g> به‌عنوان میان‌بر، مطمئن شوید"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• برنامه راه‌اندازی شده باشد"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• حداقل یک کارت به «کیف پول» اضافه شده باشد"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• برنامه دوربین نصب شده باشد"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• برنامه راه‌اندازی شده باشد"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• حداقل یک دستگاه دردسترس باشد"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 12e9797e783b..e228c0a786ff 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ääniapuri"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-koodiskanneri"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Lukitus avattu"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Laite lukittu"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Kasvojen skannaus"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Lähetä"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Kasvoja ei voi tunnistaa"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Käytä sormenjälkeä"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Kasvojentunnistusavaus ei ole saatavilla"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth yhdistetty."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akun varaustaso ei tiedossa."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Yhteys: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Lentokonetila."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN päällä"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Akun virta <xliff:g id="NUMBER">%d</xliff:g> prosenttia."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Akun virta <xliff:g id="PERCENTAGE">%1$d</xliff:g> prosenttia, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Akku latautuu: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> prosenttia"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Akun virta <xliff:g id="PERCENTAGE">%d</xliff:g> prosenttia, lataaminen keskeytetty akun suojelemiseksi."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Akun virta <xliff:g id="PERCENTAGE">%1$d</xliff:g> prosenttia, <xliff:g id="TIME">%2$s</xliff:g>, lataaminen keskeytetty akun suojelemiseksi."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Näytä kaikki ilmoitukset"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Tekstipuhelin käytössä."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Soittoääni: värinä."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Avaa lukitus ja käytä"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Korttien noutamisessa oli ongelma, yritä myöhemmin uudelleen"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lukitusnäytön asetukset"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-koodiskanneri"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Päivitetään"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Työprofiili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lentokonetila"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Et kuule seuraavaa hälytystäsi (<xliff:g id="WHEN">%1$s</xliff:g>)."</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"VKP, KKK p"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Avaa <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Jos haluat, että <xliff:g id="APPNAME">%1$s</xliff:g> lisätään pikakuvakkeeksi, varmista nämä:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Sovellus on otettu käyttöön"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Ainakin yksi kortti on lisätty Walletiin"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Asenna kamerasovellus"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Sovellus on otettu käyttöön"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ainakin yksi laite on käytettävissä"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 99e8c7fce656..6ec97d7b9d78 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistance vocale"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portefeuille"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Lecteur de code QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Déverrouillé"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Appareil verrouillé"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Numérisation du visage"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Envoyer"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Visage non reconnu"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Utiliser l\'empreinte digitale"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Déverrouillage par reconnaissance faciale inaccessible."</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connecté"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Pourcentage de la pile inconnu."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connecté à : <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Mode Avion"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"RPV activé."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Pile : <xliff:g id="NUMBER">%d</xliff:g> pour cent"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Pile à <xliff:g id="PERCENTAGE">%1$d</xliff:g>pour cent, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Pile en charge : <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Pile à <xliff:g id="PERCENTAGE">%d</xliff:g> pour cent, recharge interrompue pour protéger la pile."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Pile à <xliff:g id="PERCENTAGE">%1$d</xliff:g> pour cent, <xliff:g id="TIME">%2$s</xliff:g>, recharge interrompue pour protéger la pile."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Afficher toutes les notifications"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Téléscripteur activé"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Sonnerie en mode vibreur"</string>
@@ -371,7 +366,7 @@
<string name="user_remove_user_message" msgid="6702834122128031833">"Toutes les applications et les données de cet utilisateur seront supprimées."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Supprimer"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toute l\'information visible sur votre écran ou qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et le contenu audio que vous faites jouer."</string>
- <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Le service offrant cette fonction aura accès à toute l\'information qui est visible sur votre écran ou sur ce qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et le contenu audio que vous faites jouer."</string>
+ <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Le service offrant cette fonction aura accès à toute l\'information qui est visible sur votre écran ou à ce qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et le contenu audio que vous faites jouer."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Commencer à enregistrer ou à diffuser?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Commencer à enregistrer ou à diffuser avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_permission_dialog_title" msgid="7130975432309482596">"Autoriser <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> à partager ou à enregistrer?"</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"Un problème est survenu lors de la récupération de vos cartes, veuillez réessayer plus tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"lecteur de code QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mise à jour en cours…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ouvrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Pour ajouter l\'application <xliff:g id="APPNAME">%1$s</xliff:g> en tant que raccourci, assurez-vous"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• que cette application est configurée;"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• qu\'au moins une carte a été ajoutée à Portefeuille."</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• qu\'une application de caméra est installée;"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• que cette application est configurée;"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• qu\'au moins un appareil est utilisable;"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 68fae123e7fc..3c2a59b95901 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistance vocale"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Lecteur de code QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Déverrouillé"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Appareil verrouillé"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Analyse du visage en cours"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Envoyer"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Visage non reconnu"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Utilisez empreinte digit."</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Déverrouillage par reconnaissance faciale indisponible"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth connecté"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Pourcentage de la batterie inconnu."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connecté à : <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Mode Avion"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"Le VPN est activé."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Batterie : <xliff:g id="NUMBER">%d</xliff:g> pour cent"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batterie : <xliff:g id="PERCENTAGE">%1$d</xliff:g> pour cent. Utilisable <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batterie en charge : <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batterie : <xliff:g id="PERCENTAGE">%d</xliff:g> pour cent. Recharge interrompue pour protéger la batterie."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batterie : <xliff:g id="PERCENTAGE">%1$d</xliff:g> pour cent. Utilisable <xliff:g id="TIME">%2$s</xliff:g>. Recharge interrompue pour protéger la batterie."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Afficher toutes les notifications"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Téléscripteur activé"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Sonnerie en mode vibreur"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Problème de récupération de vos cartes. Réessayez plus tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Lecteur de code QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mise à jour"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme <xliff:g id="WHEN">%1$s</xliff:g>."</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM j"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"hh:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ouvrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Pour ajouter l\'appli <xliff:g id="APPNAME">%1$s</xliff:g> comme raccourci, procédez aux vérifications suivantes"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• L\'appli est configurée"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Au moins une carte a été ajoutée à Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Installer une appli d\'appareil photo"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• L\'appli est configurée"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Au moins un appareil est disponible"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index fd554badf49d..fb255e4be0b6 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistente de voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Escáner de código QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Desbloqueouse"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Analizando cara"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Non se recoñeceu a cara"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Usa a impresión dixital"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"O desbloqueo facial non está dispoñible"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Descoñécese a porcentaxe da batería."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Modo avión"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"A VPN está activada."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Carga da batería: <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Carga da batería: <xliff:g id="PERCENTAGE">%1$d</xliff:g> por cento (<xliff:g id="TIME">%2$s</xliff:g>)"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batería cargando. Nivel: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> por cento."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Carga da batería: <xliff:g id="PERCENTAGE">%d</xliff:g> por cento. Púxose en pausa a carga para protexer a batería."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Carga da batería: <xliff:g id="PERCENTAGE">%1$d</xliff:g> por cento (<xliff:g id="TIME">%2$s</xliff:g>). Púxose en pausa a carga para protexer a batería."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Ver todas as notificacións"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletipo activado"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Timbre en vibración"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Produciuse un problema ao obter as tarxetas. Téntao de novo máis tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración da pantalla de bloqueo"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Escáner de códigos QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Actualizando"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de traballo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Non escoitarás a alarma seguinte <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -531,7 +526,7 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sen son nin vibración"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sen son nin vibración, e aparecen máis abaixo na sección de conversas"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Poderían facer que o teléfono soe ou vibre en función da súa configuración"</string>
+ <string name="notification_channel_summary_default" msgid="3282930979307248890">"O teléfono pode soar ou vibrar en función da súa configuración"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Poderían facer que o teléfono soe ou vibre en función da súa configuración. Conversas desde a burbulla da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fai que o sistema determine se a notificación debe emitir un son ou unha vibración"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Estado:&lt;/b&gt; ascendeuse a Predeterminada"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Para engadir a aplicación <xliff:g id="APPNAME">%1$s</xliff:g> como atallo, asegúrate de que se cumpra o seguinte:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• A aplicación debe estar configurada"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Ten que haber polo menos unha tarxeta engadida a Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Debes instalar a aplicación de cámara"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• A aplicación debe estar configurada"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ten que haber polo menos un dispositivo dispoñible"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 72c535b34a3c..2df50433036b 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"વૉઇસ સહાય"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR કોડ સ્કૅનર"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"અનલૉક કર્યું છે"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ડિવાઇસ લૉક કરેલું છે"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ચહેરો સ્કૅન કરવો"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"મોકલો"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ચહેરો ઓળખાતો નથી"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"તો ફિંગરપ્રિન્ટ વાપરો"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ફેસ અનલૉક સુવિધા ઉપલબ્ધ નથી"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"બ્લૂટૂથ કનેક્ટ થયું."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"બૅટરીની ટકાવારી અજાણ છે."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> થી કનેક્ટ થયાં."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"એરપ્લેન મોડ."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ચાલુ છે."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"બૅટરી <xliff:g id="NUMBER">%d</xliff:g> ટકા."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"બૅટરી <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"બૅટરી ચાર્જ થઈ રહી છે, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"બૅટરી <xliff:g id="PERCENTAGE">%d</xliff:g>, બૅટરીની સુરક્ષા માટે ચાર્જિંગ થોભાવ્યું છે."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"બૅટરી <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>, બૅટરીની સુરક્ષા માટે ચાર્જિંગ થોભાવ્યું છે."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"બધી સૂચના જુઓ"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"ટેલિટાઇપરાઇટર સક્ષમ કર્યું."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"રિંગર વાઇબ્રેટ."</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ઉપયોગ કરવા માટે અનલૉક કરો"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"તમારા કાર્ડની માહિતી મેળવવામાં સમસ્યા આવી હતી, કૃપા કરીને થોડા સમય પછી ફરી પ્રયાસ કરો"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"લૉક સ્ક્રીનના સેટિંગ"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR કોડ સ્કૅનર"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"ઑફિસની પ્રોફાઇલ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"એરપ્લેન મોડ"</string>
@@ -996,4 +992,24 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"રદ કરો"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"હમણાં જ ફ્લિપ કરો"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"બહેતર સેલ્ફી લેવા માટે ફોન ખોલો"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"બહેતર સેલ્ફી લેવા ફ્રન્ટ ડિસ્પ્લે પર ફ્લિપ કરીએ?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"વધુ ઉચ્ચ રિઝોલ્યુશનવાળો વિશાળ ફોટો લેવા માટે પાછલા કૅમેરાનો ઉપયોગ કરો."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ આ સ્ક્રીન બંધ થઈ જશે"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-h700dp/dimens.xml b/packages/SystemUI/res/values-h700dp/dimens.xml
new file mode 100644
index 000000000000..055308f17776
--- /dev/null
+++ b/packages/SystemUI/res/values-h700dp/dimens.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <!-- Margin above the ambient indication container -->
+ <dimen name="ambient_indication_container_margin_top">15dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index 8efd6f0b7995..3a71994e07e2 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -17,4 +17,7 @@
<resources>
<!-- With the large clock, move up slightly from the center -->
<dimen name="keyguard_large_clock_top_margin">-112dp</dimen>
+
+ <!-- Margin above the ambient indication container -->
+ <dimen name="ambient_indication_container_margin_top">20dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index a49c8d08172f..6882daf73fe7 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"आवाज़ से डिवाइस का इस्तेमाल"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"क्यूआर कोड स्कैनर"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"अनलॉक है"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"डिवाइस लॉक है"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"डिवाइस अनलॉक करने के लिए चेहरा स्कैन किया जाता है"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"भेजें"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"चेहरे की पहचान नहीं हुई"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"फ़िंगरप्रिंट इस्तेमाल करें"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"फ़ेस अनलॉक की सुविधा उपलब्ध नहीं है"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ब्लूटूथ कनेक्ट किया गया."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"इस बारे में जानकारी नहीं है कि अभी बैटरी कितने प्रतिशत चार्ज है."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> से कनेक्ट किया गया."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"हवाई जहाज़ मोड."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN चालू."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> प्रति‍शत बैटरी."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"बैटरी <xliff:g id="PERCENTAGE">%1$d</xliff:g> प्रतिशत चार्ज है, जो कि <xliff:g id="TIME">%2$s</xliff:g> चल जाएगी"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"बैटरी चार्ज हो रही है, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> प्रतिशत."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"बैटरी <xliff:g id="PERCENTAGE">%d</xliff:g> प्रतिशत चार्ज हुई. बैटरी खराब होने से बचाने के लिए, चार्जिंग रोक दी गई है."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"बैटरी <xliff:g id="PERCENTAGE">%1$d</xliff:g> प्रतिशत चार्ज हुई, जो कि <xliff:g id="TIME">%2$s</xliff:g> चल जाएगी. बैटरी खराब होने से बचाने के लिए, चार्जिंग रोक दी गई है."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"पूरी सूचनाएं देखें"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"टेलीटाइपराइटर सक्षम."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"रिंगर कंपन (वाइब्रेशन)."</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"इस्तेमाल करने के लिए, डिवाइस अनलॉक करें"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"आपके कार्ड की जानकारी पाने में कोई समस्या हुई है. कृपया बाद में कोशिश करें"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लॉक स्क्रीन की सेटिंग"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"क्यूआर कोड स्कैनर"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"वर्क प्रोफ़ाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाई जहाज़ मोड"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 71ba52c99691..20942747a357 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovna pomoć"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Čitač QR koda"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Otključano"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skeniranje lica"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošalji"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Lice nije prepoznato"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Upotrijebite otisak prsta"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Otključavanje licem nije dostupno"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth povezan."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Postotak baterije nije poznat."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Spojen na <xliff:g id="BLUETOOTH">%s</xliff:g> ."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Način rada u zrakoplovu"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN uključen."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Baterija <xliff:g id="NUMBER">%d</xliff:g> posto."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Postotak baterije: <xliff:g id="PERCENTAGE">%1$d</xliff:g>, vrijeme: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Baterija se puni, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> posto."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Postotak baterije <xliff:g id="PERCENTAGE">%d</xliff:g>, punjenje je pauzirano radi zaštite baterije."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Postotak baterije <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>, punjenje je pauzirano radi zaštite baterije"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Pogledajte sve obavijesti"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter je omogućen."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibracija softvera zvona."</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"Pojavio se problem prilikom dohvaćanja kartica, pokušajte ponovo kasnije"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Postavke zaključanog zaslona"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"Čitač QR koda"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ažuriranje"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u zrakoplovu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE., d. MMM."</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvorite <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Da biste dodali aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g> kao prečac, provjerite sljedeće"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Aplikacija je postavljena"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Najmanje jedna kartica dodana je u Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Instalirajte aplikaciju fotoaparata"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Aplikacija je postavljena"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Dostupan je najmanje jedan uređaj"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index d9c438ceb8a2..1b9dc9a98582 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Hangsegéd"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-kód-szkennelő"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Feloldva"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Az eszköz zárolva van"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Arc keresése"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Küldés"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Az arc nem ismerhető fel"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Használjon ujjlenyomatot"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Nem áll rendelkezésre az Arcalapú feloldás"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth csatlakoztatva."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Az akkumulátor töltöttségi szintje ismeretlen."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Csatlakoztatva a következőhöz: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Repülőgép üzemmód."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN bekapcsolva."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Akkumulátor <xliff:g id="NUMBER">%d</xliff:g> százalék."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Az akkumulátor töltöttségi szinte <xliff:g id="PERCENTAGE">%1$d</xliff:g> százalék; <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Tölt az akkumulátor, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> százalék."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Az akkumulátor töltöttségi szintje <xliff:g id="PERCENTAGE">%d</xliff:g> százalék; töltés szüneteltetve az akkumulátor védelme érdekében."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Az akkumulátor töltöttségi szintje <xliff:g id="PERCENTAGE">%1$d</xliff:g> százalék; <xliff:g id="TIME">%2$s</xliff:g>. Töltés szüneteltetve az akkumulátor védelme érdekében."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Összes értesítés megtekintése"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter engedélyezve."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Csengő rezeg."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Oldja fel a használathoz"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Probléma merült fel a kártyák lekérésekor, próbálja újra később"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lezárási képernyő beállításai"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-kód-szkennelő"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Frissítés…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Munkahelyi profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Repülős üzemmód"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nem fogja hallani az ébresztést ekkor: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, HHH n"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"ó:pp"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"óó:pp"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> megnyitása"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Ha szeretné felvenni a(z) <xliff:g id="APPNAME">%1$s</xliff:g> alkalmazást parancsikonként, gondoskodjon a következőkről:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Az alkalmazás be van állítva"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Legalább egy kártya hozzá lett adva a Wallethez"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Kameraalkalmazás telepítése"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Az alkalmazás be van állítva"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Legalább egy eszköz rendelkezésre áll"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 820b66669dda..840bc261e9dc 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ձայնային հուշումներ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR կոդերի սկաներ"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Ապակողպված է"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Սարքը կողպված է"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Դեմքի սկանավորում"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Ուղարկել"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Դեմքը չի ճանաչվել"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Օգտագործեք մատնահետք"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Դեմքով ապակողպումն անհասանելի է"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-ը միացված է:"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Մարտկոցի լիցքի մակարդակն անհայտ է։"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Միացված է <xliff:g id="BLUETOOTH">%s</xliff:g>-ին:"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Ավիառեժիմ"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"Միացնել VPN-ը։"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Մարտկոցը <xliff:g id="NUMBER">%d</xliff:g> տոկոս է:"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Մարտկոցի լիցքը <xliff:g id="PERCENTAGE">%1$d</xliff:g> տոկոս է։ Այն կաշխատի <xliff:g id="TIME">%2$s</xliff:g>։"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Մարտկոցը լիցքավորվում է: Լիցքը <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> տոկոս է:"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Մարտկոցի լիցքը <xliff:g id="PERCENTAGE">%d</xliff:g> է։ Լիցքավորումը դադարեցվել է՝ մարտկոցը պաշտպանելու համար։"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Մարտկոցի լիցքը <xliff:g id="PERCENTAGE">%1$d</xliff:g> է։ Այն կաշխատի <xliff:g id="TIME">%2$s</xliff:g>։ Լիցքավորումը դադարեցվել է՝ մարտկոցը պաշտպանելու նպատակով։"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Տեսնել բոլոր ծանուցումները"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Հեռատիպը միացված է:"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Թրթռազանգ:"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ապակողպել՝ օգտագործելու համար"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Չհաջողվեց բեռնել քարտերը։ Նորից փորձեք։"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Կողպէկրանի կարգավորումներ"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR կոդերի սկաներ"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Թարմացում"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Android for Work-ի պրոֆիլ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ավիառեժիմ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ժամը <xliff:g id="WHEN">%1$s</xliff:g>-ի զարթուցիչը չի զանգի"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Բացել <xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածի դյուրանցումն ավելացնելու համար համոզվեք, որ՝"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Հավելվածը կարգավորված է"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Առնվազն մեկ քարտ ավելացված է Wallet-ում"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• «Տեսախցիկ» հավելվածը տեղադրված է"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Հավելվածը կարգավորված է"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Հասանելի է առնվազն մեկ սարք"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index a5ff4915c3bd..351d5e651b7b 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Bantuan Suara"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Pemindai Kode QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Tidak terkunci"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Perangkat terkunci"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Memindai wajah"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Kirim"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Tidak mengenali wajah"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Gunakan sidik jari"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Buka dengan Wajah tidak tersedia"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth terhubung."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Persentase baterai tidak diketahui."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Terhubung ke <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Mode pesawat."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN aktif."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Baterai <xliff:g id="NUMBER">%d</xliff:g> persen."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Baterai <xliff:g id="PERCENTAGE">%1$d</xliff:g> persen, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Mengisi daya baterai, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> persen."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Baterai <xliff:g id="PERCENTAGE">%d</xliff:g> persen, pengisian daya dijeda untuk melindungi baterai."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Baterai <xliff:g id="PERCENTAGE">%1$d</xliff:g> persen, <xliff:g id="TIME">%2$s</xliff:g>, pengisian daya dijeda untuk melindungi baterai."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Lihat semua notifikasi"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter diaktifkan."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Pendering bergetar."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Terjadi error saat mendapatkan kartu Anda, coba lagi nanti"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setelan layar kunci"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Pemindai kode QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mengupdate"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode pesawat"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar alarm berikutnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Buka <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Untuk menambahkan aplikasi <xliff:g id="APPNAME">%1$s</xliff:g> sebagai pintasan, pastikan"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Aplikasi disiapkan"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Minimal satu kartu telah ditambahkan ke Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Menginstal aplikasi kamera"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Aplikasi disiapkan"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Tersedia minimal satu perangkat"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index bc84dc937dc0..973f17ad5c03 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Raddaðstoð"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Veski"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-kóðaskanni"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Ólæst"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Tækið er læst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Andlit skannað"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Senda"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Andlit þekkist ekki"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Nota fingrafar í staðinn"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Andlitskenni ekki í boði"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth tengt."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Staða rafhlöðu óþekkt."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Tengt við <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Flugstilling"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"Kveikt á VPN."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> prósent á rafhlöðu."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Hleðsla rafhlöðu er <xliff:g id="PERCENTAGE">%1$d</xliff:g> prósent, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Rafhlaða í hleðslu, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Hleðsla rafhlöðu er <xliff:g id="PERCENTAGE">%d</xliff:g> prósent, hlé gert á hleðslu til að vernda rafhlöðuna."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Hleðsla rafhlöðu er <xliff:g id="PERCENTAGE">%1$d</xliff:g> prósent, <xliff:g id="TIME">%2$s</xliff:g>, hlé gert á hleðslu til að vernda rafhlöðuna."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Sjá allar tilkynningar"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Fjarriti virkur."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Titrar við hringingu."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Taktu úr lás til að nota"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Vandamál kom upp við að sækja kortin þín. Reyndu aftur síðar"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Stillingar fyrir læstan skjá"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-kóðaskanni"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Uppfærir"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Vinnusnið"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugstilling"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ekki mun heyrast í vekjaranum <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"k:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Opna <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Til að bæta forritinu <xliff:g id="APPNAME">%1$s</xliff:g> við sem flýtileið skaltu ganga úr skugga um að"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Forritið er uppsett"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Að minnsta kosti einu korti var bætt við Veski"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Setja upp myndavélarforrit"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Forritið er uppsett"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Að minnsta kosti eitt tæki er tiltækt"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 0d2c3b1bc46b..ab18d29c60b7 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Scanner codici QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Sbloccato"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloccato"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scansione del viso"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Invia"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Volto non riconosciuto"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Usa l\'impronta"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Sblocco con il volto non disponibile"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth collegato."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Percentuale della batteria sconosciuta."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connesso a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Modalità aereo."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN attiva."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Batteria: <xliff:g id="NUMBER">%d</xliff:g> percento."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batteria a <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cento, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batteria in carica, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batteria a <xliff:g id="PERCENTAGE">%d</xliff:g> per cento; ricarica in pausa per proteggere la batteria."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batteria a <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cento, <xliff:g id="TIME">%2$s</xliff:g>; ricarica in pausa per proteggere la batteria."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Visualizza tutte le notifiche"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Telescrivente abilitata."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Suoneria vibrazione."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Sblocca per usare"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Si è verificato un problema durante il recupero delle tue carte. Riprova più tardi."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Impostazioni schermata di blocco"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Scanner codici QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Aggiornamento in corso…"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profilo di lavoro"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modalità aereo"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Non sentirai la tua prossima sveglia <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM g"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Apri <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Per aggiungere l\'app <xliff:g id="APPNAME">%1$s</xliff:g> come scorciatoia, assicurati che:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• L\'app sia configurata"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Sia stata aggiunta almeno una carta a Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Sia installata un\'app fotocamera"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• L\'app sia configurata"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ci sia almeno un dispositivo disponibile"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Annulla"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Gira ora"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Apri il telefono per un selfie migliore"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Girare su display frontale per un selfie migliore?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Utilizza la fotocamera posteriore per una foto più ampia con maggiore risoluzione."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Questo schermo verrà disattivato"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 453e8d604d4f..c5df1690c91c 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -168,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"לא ניתן לזהות את הפנים"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"שימוש בטביעת אצבע במקום זאת"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"אי אפשר לפתוח בזיהוי פנים"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"‏Bluetooth מחובר."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"אחוז טעינת הסוללה לא ידוע."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"התבצע חיבור אל <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -180,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"מצב טיסה"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"‏VPN פועל."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> אחוזים של סוללה."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"רמת הטעינה של הסוללה היא <xliff:g id="PERCENTAGE">%1$d</xliff:g> אחוזים, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"הסוללה בטעינה, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"רמת הטעינה של הסוללה היא <xliff:g id="PERCENTAGE">%d</xliff:g> אחוזים. הטעינה הושהתה כדי להגן על הסוללה."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"רמת הטעינה של הסוללה היא <xliff:g id="PERCENTAGE">%1$d</xliff:g> אחוזים, <xliff:g id="TIME">%2$s</xliff:g>. הטעינה הושהתה כדי להגן על הסוללה."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"הצגת כל ההתראות"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"‏TeleTypewriter מופעל"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"צלצול ורטט."</string>
@@ -500,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"יש לבטל את הנעילה כדי להשתמש"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"הייתה בעיה בקבלת הכרטיסים שלך. כדאי לנסות שוב מאוחר יותר"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"הגדרות מסך הנעילה"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"‏סורק קודי QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"מתבצע עדכון"</string>
<string name="status_bar_work" msgid="5238641949837091056">"פרופיל עבודה"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"מצב טיסה"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"לא ניתן יהיה לשמוע את ההתראה הבאה שלך <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -995,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"‏יום EEE,‏ d בMMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"פתיחת <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"כדי להוסיף את האפליקציה <xliff:g id="APPNAME">%1$s</xliff:g> כקיצור דרך, צריך לוודא"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• האפליקציה מוגדרת"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"‏• לפחות כרטיס אחד נוסף ל-Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• התקנה של אפליקציית מצלמה"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• האפליקציה מוגדרת"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• יש לפחות מכשיר אחד זמין"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ביטול"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"הפכת את המכשיר"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"כדי לצלם תמונת סלפי טובה יותר, פותחים את הטלפון"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"להפוך למסך הקדמי כדי לצלם תמונת סלפי טובה יותר?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"במצלמה האחורית אפשר לצלם תמונה רחבה יותר ברזולוציה גבוהה יותר."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ המסך יכבה"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7efa1a29dbc0..c1edf400a5a5 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -168,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"顔を認識できません"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"指紋認証をお使いください"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"顔認証を利用できません"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetoothに接続済み。"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"バッテリー残量は不明です。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>に接続しました。"</string>
@@ -180,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"機内モード。"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN は ON です。"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"バッテリー残量: <xliff:g id="NUMBER">%d</xliff:g>パーセント"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"バッテリー残量 <xliff:g id="PERCENTAGE">%1$d</xliff:g>、<xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"電池充電中: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>パーセント"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"バッテリー残量 <xliff:g id="PERCENTAGE">%d</xliff:g>: バッテリー保護のため、充電を一時停止しました。"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"バッテリー残量 <xliff:g id="PERCENTAGE">%1$d</xliff:g>、<xliff:g id="TIME">%2$s</xliff:g>: バッテリー保護のため、充電を一時停止しました。"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"通知をすべて表示"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"テレタイプライターが有効です。"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"バイブレーション着信。"</string>
@@ -501,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"カードの取得中に問題が発生しました。しばらくしてからもう一度お試しください"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ロック画面の設定"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"QR コードスキャナ"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"更新しています"</string>
<string name="status_bar_work" msgid="5238641949837091056">"仕事用プロファイル"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"機内モード"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"次回のアラーム(<xliff:g id="WHEN">%1$s</xliff:g>)は鳴りません"</string>
@@ -994,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> を開く"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> アプリをショートカットとして追加するための手順"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• アプリが設定されている"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• ウォーレットに追加されているカードが 1 枚以上ある"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• カメラアプリをインストールする"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• アプリが設定されている"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• 利用できるデバイスが 1 台以上ある"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"キャンセル"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"切り替えましょう"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"高画質で撮るにはスマートフォンを開いてください"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"前面ディスプレイに切り替えて綺麗に撮りましょう"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"高解像度で広い範囲を撮影するには、背面カメラを使用してください。"</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱この画面は OFF になります"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 292aaa4a83ef..3fd445443c20 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ხმოვანი დახმარება"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"საფულე"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR კოდის სკანერი"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"გახსნილი"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"მოწყობილობა ჩაკეტილია"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"მიმდინარეობს სახის სკანირება"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"გაგზავნა"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"სახის ამოცნობა შეუძლებ."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"გამოიყენეთ თითის ანაბეჭდი"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"სახით განბლოკვა მიუწვდომელია."</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth დაკავშირებულია."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ბატარეის პროცენტული მაჩვენებელი უცნობია."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"დაკავშირებულია <xliff:g id="BLUETOOTH">%s</xliff:g>-თან."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"თვითმფრინავის რეჟიმი"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ჩართულია."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"ბატარეა: <xliff:g id="NUMBER">%d</xliff:g> პროცენტი."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"ბატარეა: <xliff:g id="PERCENTAGE">%1$d</xliff:g> პროცენტი, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"ბატარეა იტენება. ამჟამად არის <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> პროცენტი."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"ბატარეა: <xliff:g id="PERCENTAGE">%d</xliff:g> პროცენტი, დატენვა შეჩერებულია ბატარეის დასაცავად."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"ბატარეა: <xliff:g id="PERCENTAGE">%1$d</xliff:g> პროცენტი, <xliff:g id="TIME">%2$s</xliff:g>, დატენვა შეჩერებულია ბატარეის დასაცავად."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"ყველა შეტყობინების ნახვა"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"ტელეტაიპი ჩართულია."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"ვიბრაციის რეჟიმი."</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"თქვენი ბარათების მიღებისას პრობლემა წარმოიშვა. ცადეთ ხელახლა მოგვიანებით"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ჩაკეტილი ეკრანის პარამეტრები"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"QR კოდის სკანერი"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"მიმდინარეობს განახლება"</string>
<string name="status_bar_work" msgid="5238641949837091056">"სამსახურის პროფილი"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"თვითმფრინავის რეჟიმი"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ვერ გაიგონებთ მომდევნო მაღვიძარას <xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"დდდ, თთთ თ"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"სთ:წთ"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"სთ:წთ"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> აპის გახსნა"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> აპი რომ მალსახმობის სახით დაამატოთ, დარწმუნდით, რომ"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• აპი დაყენებულია"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• საფულეში დამატებულია მინიმუმ ერთი ბარათი"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• დააინსტალირეთ კამერის აპი"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• აპი დაყენებულია"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ხელმისაწვდომია მინიმუმ ერთი მოწყობილობა"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index bffda32ea284..d14fbed2276f 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Дауыс көмекшісі"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR кодын сканерлеу қолданбасы"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Құлпы ашылған"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Құрылғы құлыпталды."</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Бетті сканерлеу"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Жіберу"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Бет танылмады."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Орнына саусақ ізін пайдаланыңыз."</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Бет тану функциясы қолжетімсіз."</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth қосылған."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарея зарядының мөлшері белгісіз."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> қосылған."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Ұшақ режимі."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN қосулы."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Батарея <xliff:g id="NUMBER">%d</xliff:g> пайыз."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Батарея заряды: <xliff:g id="PERCENTAGE">%1$d</xliff:g> пайыз. Жететін мерзімі: <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Батарея зарядталуда, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Батарея заряды: <xliff:g id="PERCENTAGE">%d</xliff:g> пайыз. Батареяны қорғау үшін зарядтау кідіртілді."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Батарея заряды: <xliff:g id="PERCENTAGE">%1$d</xliff:g> пайыз. Жететін мерзімі: <xliff:g id="TIME">%2$s</xliff:g>. Батареяны қорғау үшін зарядтау кідіртілді."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Барлық хабарландыруды қарау"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Телетайп қосылған."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Қоңырау тербелісі."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Пайдалану үшін құлыпты ашу"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Карталарыңыз алынбады, кейінірек қайталап көріңіз."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Экран құлпының параметрлері"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR кодының сканері"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Жаңартылып жатыр"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жұмыс профилі"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ұшақ режимі"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Келесі <xliff:g id="WHEN">%1$s</xliff:g> дабылыңызды есітпейсіз"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"d MMM EEEE"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ашу"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> қолданбасын таңбаша ретінде қосу үшін келесі әрекеттерді орындауды ұмытпаңыз:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Қолданба реттелген"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Wallet-ке кемінде бір карта қосылған"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Камера қолданбасын орнатыңыз"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Қолданба реттелген"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Кемінде бір құрылғы қолжетімді"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 6d2e5a8e48d1..3ace0e6a34b9 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ជំនួយសំឡេង"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"កម្មវិធីស្កេនកូដ QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"បានដោះសោ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"បានចាក់សោ​ឧបករណ៍"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ការ​ស្កេន​មុខ"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ផ្ញើ"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"មិនអាចសម្គាល់មុខបានទេ"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ប្រើស្នាមម្រាមដៃជំនួសវិញ​"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"មិនអាចប្រើការដោះសោតាមទម្រង់មុខបានទេ"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"បាន​តភ្ជាប់​ប៊្លូធូស។"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"មិនដឹងអំពី​ភាគរយថ្មទេ។"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"បាន​ភ្ជាប់​ទៅ <xliff:g id="BLUETOOTH">%s</xliff:g> ។"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"ពេល​ជិះ​យន្តហោះ"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"បើក VPN ។"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"ថ្ម <xliff:g id="NUMBER">%d</xliff:g> ភាគរយ។"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"ថ្ម <xliff:g id="PERCENTAGE">%1$d</xliff:g> ភាគរយ, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"កំពុងសាកថ្ម <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ភាគរយ"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"ថ្ម <xliff:g id="PERCENTAGE">%d</xliff:g> ភាគរយ, ការសាកថ្មបានផ្អាកដើម្បីការការពារថ្ម។"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"ថ្ម <xliff:g id="PERCENTAGE">%1$d</xliff:g> ភាគរយ, <xliff:g id="TIME">%2$s</xliff:g>, ការសាកថ្មបានផ្អាកដើម្បីការការពារថ្ម។"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"មើល​ការជូនដំណឹង​ទាំងអស់"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលេខ​"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"កម្មវិធី​រោទ៍​ញ័រ។"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ដោះសោដើម្បីប្រើប្រាស់"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"មានបញ្ហា​ក្នុងការទាញយក​កាត​របស់អ្នក សូម​ព្យាយាមម្ដងទៀត​នៅពេលក្រោយ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ការកំណត់អេក្រង់ចាក់សោ"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"កម្មវិធី​ស្កេនកូដ QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"កំពុងដំឡើង​កំណែ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ប្រវត្តិរូបការងារ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ពេលជិះយន្តហោះ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"អ្នកនឹងមិនលឺម៉ោងរោទ៍ <xliff:g id="WHEN">%1$s</xliff:g> បន្ទាប់របស់អ្នកទេ"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"បើក <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"ដើម្បីបញ្ចូលកម្មវិធី <xliff:g id="APPNAME">%1$s</xliff:g> ជាផ្លូវកាត់ សូមប្រាកដថា"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• កម្មវិធីត្រូវបានរៀបចំ"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• កាតយ៉ាងតិចមួយត្រូវបានបញ្ចូលទៅក្នុង Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• ដំឡើងកម្មវិធីកាមេរ៉ា"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• កម្មវិធីត្រូវបានរៀបចំ"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ឧបករណ៍យ៉ាងតិចមួយអាចប្រើបាន"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 099139f27185..27691e1970ed 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ಧ್ವನಿ ಸಹಾಯಕ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"ವಾಲೆಟ್"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR ಕೋಡ್ ಸ್ಕ್ಯಾನರ್"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"ಅನ್‌ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ಸಾಧನ ಲಾಕ್ ಆಗಿದೆ"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ಮುಖವನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ಕಳುಹಿಸಿ"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ಮುಖ ಗುರುತಿಸಲಾಗುತ್ತಿಲ್ಲ"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ಬದಲಿಗೆ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಬಳಸಿ"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ಬ್ಲೂಟೂತ್‌‌ ಸಂಪರ್ಕಗೊಂಡಿದೆ."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ಬ್ಯಾಟರಿ ಶೇಕಡಾವಾರು ತಿಳಿದಿಲ್ಲ."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ಗೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"ಏರೋಪ್ಲೇನ್‌ ಮೋಡ್‌"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"ನಲ್ಲಿ VPN"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"ಬ್ಯಾಟರಿ <xliff:g id="NUMBER">%d</xliff:g> ಪ್ರತಿಶತ."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"ಬ್ಯಾಟರಿ <xliff:g id="PERCENTAGE">%1$d</xliff:g> ರಷ್ಟಿದ್ದು <xliff:g id="TIME">%2$s</xliff:g> ಗಳ ಕಾಲ ಇರುತ್ತದೆ"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"ಬ್ಯಾಟರಿ <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ಪ್ರತಿಶತ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"ಬ್ಯಾಟರಿ <xliff:g id="PERCENTAGE">%d</xliff:g> ರಷ್ಟಿದೆ, ಬ್ಯಾಟರಿ ರಕ್ಷಣೆಗಾಗಿ ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"ಬ್ಯಾಟರಿ <xliff:g id="PERCENTAGE">%1$d</xliff:g> ರಷ್ಟಿದ್ದು <xliff:g id="TIME">%2$s</xliff:g> ಗಳ ಕಾಲ ಇರುತ್ತದೆ, ಬ್ಯಾಟರಿ ರಕ್ಷಣೆಗಾಗಿ ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ನೋಡಿ"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"ಟೆಲಿಟೈಪ್‌ರೈಟರ್ ಸಕ್ರಿಯವಾಗಿದೆ."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"ರಿಂಗರ್ ವೈಬ್ರೇಟ್‌."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ಬಳಸಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ನಿಮ್ಮ ಕಾರ್ಡ್‌ಗಳನ್ನು ಪಡೆಯುವಾಗ ಸಮಸ್ಯೆ ಉಂಟಾಗಿದೆ, ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ಲಾಕ್ ಸ್ಕ್ರ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR ಕೋಡ್ ಸ್ಕ್ಯಾನರ್"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ಅಪ್‌ಡೇಟ್‌‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ನಿಮ್ಮ ಮುಂದಿನ <xliff:g id="WHEN">%1$s</xliff:g> ಅಲಾರಮ್ ಅನ್ನು ನೀವು ಆಲಿಸುವುದಿಲ್ಲ"</string>
@@ -996,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಿರಿ"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> ಆ್ಯಪ್ ಅನ್ನು ಶಾರ್ಟ್‌ಕಟ್ ಆಗಿ ಸೇರಿಸಲು ಕೆಳಗಿನವುಗಳನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• ಆ್ಯಪ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• ವಾಲೆಟ್‌ಗೆ ಕನಿಷ್ಠ ಒಂದು ಕಾರ್ಡ್ ಅನ್ನು ಸೇರಿಸಲಾಗಿದೆ"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• ಕ್ಯಾಮರಾ ಆ್ಯಪ್ ಒಂದನ್ನು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• ಆ್ಯಪ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ಕನಿಷ್ಠ ಒಂದು ಸಾಧನ ಲಭ್ಯವಿದೆ"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ರದ್ದುಗೊಳಿಸಿ"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ಈಗ ಫ್ಲಿಪ್ ಮಾಡಿ"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"ಉತ್ತಮ ಸೆಲ್ಫೀಗಾಗಿ ಫೋನ್ ಅನ್ನು ಅನ್‌ಫೋಲ್ಡ್ ಮಾಡಿ"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"ಉತ್ತಮ ಸೆಲ್ಫೀಗಾಗಿ ಮುಂಭಾಗದ ಕ್ಯಾಮರಾಗೆ ಫ್ಲಿಪ್ ಮಾಡಬೇಕೆ?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ಹೆಚ್ಚಿನ ರೆಸಲ್ಯೂಷನ್ ಹೊಂದಿರುವ ವಿಶಾಲವಾದ ಫೋಟೋಗಾಗಿ ಹಿಂಭಾಗದ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸಿ."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ಈ ಸ್ಕ್ರೀನ್ ಆಫ್ ಆಗುತ್ತದೆ"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 9e3d6c36aef0..a0f0b1d677e2 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"음성 지원"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"월렛"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR 코드 스캐너"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"잠금 해제됨"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"기기 잠김"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"얼굴 스캔 중"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"보내기"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"얼굴을 인식할 수 없습니다."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"대신 지문을 사용하세요."</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"얼굴 인식 잠금 해제를 사용할 수 없습니다."</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"블루투스가 연결되었습니다."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"배터리 잔량을 알 수 없습니다."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>에 연결되었습니다."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"비행기 모드입니다."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN 켜짐"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"배터리 <xliff:g id="NUMBER">%d</xliff:g>퍼센트"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"배터리가 <xliff:g id="PERCENTAGE">%1$d</xliff:g>퍼센트로 <xliff:g id="TIME">%2$s</xliff:g> 동안 사용 가능합니다."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"배터리 충전 중, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%입니다."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"배터리가 <xliff:g id="PERCENTAGE">%d</xliff:g>퍼센트 입니다. 배터리 보호를 위해 충전이 일시중지되었습니다."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"배터리가 <xliff:g id="PERCENTAGE">%1$d</xliff:g>퍼센트로 <xliff:g id="TIME">%2$s</xliff:g> 동안 사용 가능합니다. 배터리 보호를 위해 충전이 일시중지되었습니다."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"모든 알림 보기"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"전신 타자기(TTY)가 사용 설정되었습니다."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"벨소리가 진동입니다."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"잠금 해제하여 사용"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"카드를 가져오는 중에 문제가 발생했습니다. 나중에 다시 시도해 보세요."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"잠금 화면 설정"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR 코드 스캐너"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"업데이트 중"</string>
<string name="status_bar_work" msgid="5238641949837091056">"직장 프로필"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"비행기 모드"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>에 다음 알람을 들을 수 없습니다."</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d일 EEE"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> 열기"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> 앱을 바로가기로 추가하려면 다음을 확인하세요."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• 앱이 설정되어 있습니다."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• 1개 이상의 카드가 월렛에 추가되어 있습니다."</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• 카메라 앱이 설치되어 있습니다."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• 앱이 설정되어 있습니다."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• 1대 이상의 기기를 사용할 수 있습니다."</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index bfa6bb8cfcfb..8d6a0e51cbbe 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Үн жардамчысы"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Капчык"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR коддорунун сканери"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Кулпусу ачылды"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Түзмөк кулпуланды"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Жүз скандалууда"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Жөнөтүү"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Жүз таанылбай жатат"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Манжа изин колдонуңуз"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"\"Жүзүнөн таанып ачуу\" жеткиликсиз"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth байланышта"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарея кубатынын деңгээли белгисиз."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> менен туташкан."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Учак тартиби."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN күйүк."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Батарея <xliff:g id="NUMBER">%d</xliff:g> пайыз."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Батареянын кубаты <xliff:g id="PERCENTAGE">%1$d</xliff:g> пайыз, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Батарея кубатталууда, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Батареянын кубаты <xliff:g id="PERCENTAGE">%d</xliff:g> пайыз. Батареяны коргоо үчүн кубаттоо тындырылды."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Батареянын кубаты <xliff:g id="PERCENTAGE">%1$d</xliff:g> пайыз, <xliff:g id="TIME">%2$s</xliff:g>. Батареяны коргоо үчүн кубаттоо тындырылды."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Бардык билдирмелерди көрүү"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"ТелеТайп терүүсү жандырылган."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Шыңгыраганда титирөө."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Колдонуу үчүн кулпусун ачыңыз"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Кыйытмаларды алууда ката кетти. Бир аздан кийин кайталап көрүңүз."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Экранды кулпулоо параметрлери"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR кодунун сканери"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Жаңырууда"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жумуш профили"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Учак режими"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> боло турган кийинки эскертмени укпайсыз"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ачуу"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> колдонмосун ыкчам баскыч катары кошуу үчүн төмөнкүлөрдү аткарыңыз:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Колдонмо туураланды"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Капчыкка кеминде бир карта кошулду"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Камера колдонмосун орнотуу"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Колдонмо туураланды"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Кеминде бир түзмөк жеткиликтүү"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index e3a9079b6b80..a22bf1fc7deb 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ຊ່ວຍ​ເຫຼືອ​ທາງ​ສຽງ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"ຕົວສະແກນລະຫັດ QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"ປົດລັອກແລ້ວ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ອຸປະກອນຖືກລັອກໄວ້"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ການສະແກນໜ້າ"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ສົ່ງ"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ບໍ່ສາມາດຈຳແນກໃບໜ້າໄດ້"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ກະລຸນາໃຊ້ລາຍນິ້ວມືແທນ"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ໃຊ້ການປົດລັອກດ້ວຍໜ້າບໍ່ໄດ້"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ເຊື່ອມຕໍ່ Bluetooth ແລ້ວ."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ບໍ່ຮູ້ເປີເຊັນແບັດເຕີຣີ."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"ເຊື່ອມ​ຕໍ່​ຫາ <xliff:g id="BLUETOOTH">%s</xliff:g> ແລ້ວ."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"ໂໝດໃນຍົນ."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ເປີດ."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"ແບັດເຕີຣີ <xliff:g id="NUMBER">%d</xliff:g> ເປີເຊັນ."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"ແບັດເຕີຣີ <xliff:g id="PERCENTAGE">%1$d</xliff:g> ເປີເຊັນ, ໃຊ້ໄດ້ຈົນເຖິງ <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"ກຳລັງສາກແບັດເຕີຣີ, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ເປີເຊັນ."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"ແບັດເຕີຣີ <xliff:g id="PERCENTAGE">%d</xliff:g> ເປີເຊັນ, ການສາກຢຸດໄວ້ຊົ່ວຄາວເພື່ອການຮັກສາແບັດເຕີຣີ."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"ແບັດເຕີຣີ <xliff:g id="PERCENTAGE">%1$d</xliff:g> ເປີເຊັນ, ໃຊ້ໄດ້ຈົນເຖິງ <xliff:g id="TIME">%2$s</xliff:g>, ການສາກຢຸດໄວ້ຊົ່ວຄາວເພື່ອການຮັກສາແບັດເຕີຣີ."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"ເບິ່ງການແຈ້ງເຕືອນທັງໝົດ"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter ຖືກເປີດຢູ່."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"ສັ່ນເຕືອນພ້ອມສຽງເອີ້ນເຂົ້າ."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ປົດລັອກເພື່ອໃຊ້"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ເກີດບັນຫາໃນການໂຫຼດບັດຂອງທ່ານ, ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ການຕັ້ງຄ່າໜ້າຈໍລັອກ"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"ຕົວສະແກນລະຫັດ QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ກຳລັງອັບເດດ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"​ໂປຣ​ໄຟລ໌​ບ່ອນ​ເຮັດ​ວຽກ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ໂໝດເຮືອ​ບິນ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ທ່ານ​ຈະ​ບໍ່​ໄດ້​ຍິນ​ສຽງ​ໂມງ​ປ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"ຊມ:ນທ"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"ຊມ:ນທ"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"ເປີດ <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"ເພື່ອເພີ່ມແອັບ <xliff:g id="APPNAME">%1$s</xliff:g> ເປັນທາງລັດ, ກະລຸນາກວດສອບໃຫ້ແນ່ໃຈວ່າ"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• ແອັບໄດ້ຮັບການຕັ້ງຄ່າແລ້ວ"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• ມີການເພີ່ມຢ່າງໜ້ອຍ 1 ບັດໃສ່ໃນ Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• ຕິດຕັ້ງແອັບກ້ອງຖ່າຍຮູບ"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• ແອັບໄດ້ຮັບການຕັ້ງຄ່າແລ້ວ"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ມີຢ່າງໜ້ອຍ 1 ອຸປະກອນພ້ອມໃຫ້ນຳໃຊ້"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 7daedbd20d2d..ebb6f3f09665 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR kodų skaitytuvas"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Atrakinta"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Įrenginys užrakintas"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Nuskaitomas veidas"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Siųsti"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Veidas neatpažintas"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Naudoti piršto antspaudą"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Atrakinimo pagal veidą funkcija nepasiekiama"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"„Bluetooth“ prijungtas."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akumuliatoriaus energija procentais nežinoma."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Prisijungta prie „<xliff:g id="BLUETOOTH">%s</xliff:g>“."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Lėktuvo režimas."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN įjungtas."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Akumuliatorius: <xliff:g id="NUMBER">%d</xliff:g> proc."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Akumuliatoriaus įkrova <xliff:g id="PERCENTAGE">%1$d</xliff:g> proc., <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Įkraunamas akumuliatorius, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Akumuliatoriaus įkrova <xliff:g id="PERCENTAGE">%d</xliff:g> proc., įkrovimas pristabdytas siekiant apsaugoti akumuliatorių."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Akumuliatoriaus įkrova <xliff:g id="PERCENTAGE">%1$d</xliff:g> proc., <xliff:g id="TIME">%2$s</xliff:g>, įkrovimas pristabdytas siekiant apsaugoti akumuliatorių."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Žr. visus pranešimus"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"„TeleTypewriter“ įgalinta."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibracija skambinant."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Atrakinti, kad būtų galima naudoti"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Gaunant korteles kilo problema, bandykite dar kartą vėliau"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Užrakinimo ekrano nustatymai"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR kodų skaitytuvas"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Atnaujinama"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darbo profilis"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lėktuvo režimas"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Negirdėsite kito signalo <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Atidaryti „<xliff:g id="APPNAME">%1$s</xliff:g>“"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Jei norite pridėti programą „<xliff:g id="APPNAME">%1$s</xliff:g>“ kaip šaukinį, įsitikinkite, kad atitinkate reikalavimus."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Programa nustatyta"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Prie „Wallet“ pridėta bent viena kortelė"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Įdiekite Fotoaparato programą"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Programa nustatyta"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Pasiekiamas bent vienas įrenginys"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Atšaukti"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Apversti dabar"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Užfiksuokite geresnę asmenukę atlenkę telefoną"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Užfiksuoti geresnę asmenukę įjungus priekinį rodinį?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Naudokite galinį fotoaparatą, kad nuotrauka būtų platesnė ir didesnės skyros."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Šis ekranas išsijungs"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index d431233e299e..93f703fd8811 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Balss palīgs"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Maks"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Kvadrātkoda skeneris"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Atbloķēta"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Ierīce ir bloķēta"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Sejas skenēšana"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Sūtīt"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Nevar atpazīt seju"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Lietot pirksta nospiedumu"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Autorizācija pēc sejas nav pieejama"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth savienojums ir izveidots."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Akumulatora uzlādes līmenis procentos nav zināms."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ir izveidots savienojum ar <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Lidmašīnas režīms."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ieslēgts"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Akumulators: <xliff:g id="NUMBER">%d</xliff:g> procenti"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Akumulatora uzlādes līmenis: <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Notiek akumulatora uzlāde, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Akumulatora uzlādes līmenis: <xliff:g id="PERCENTAGE">%d</xliff:g>, uzlāde ir apturēta, lai aizsargātu akumulatoru."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Akumulatora uzlādes līmenis: <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>, uzlāde ir apturēta, lai aizsargātu akumulatoru."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Skatīt visus paziņojumus"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletaips ir iespējots."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Zvana signāls — vibrācija."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lai izmantotu, atbloķējiet ekrānu"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ienesot jūsu kartes, radās problēma. Lūdzu, vēlāk mēģiniet vēlreiz."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Bloķēšanas ekrāna iestatījumi"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Kvadrātkoda skeneris"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Notiek atjaunināšana"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darba profils"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lidojuma režīms"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nākamais signāls (<xliff:g id="WHEN">%1$s</xliff:g>) netiks atskaņots."</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"hh:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Atvērt lietotni <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Lai pievienotu lietotni <xliff:g id="APPNAME">%1$s</xliff:g> kā saīsni, jābūt izpildītiem tālāk minētajiem nosacījumiem."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Lietotne ir iestatīta."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Makam ir pievienota vismaz viena karte."</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Ir instalēta kameras lietotne."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Lietotne ir iestatīta."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ir pieejama vismaz viena ierīce."</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index de4ae1e92036..e8d24cbe65d0 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Гласовна помош"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Скенер на QR-кодови"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Отклучено"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Уредот е заклучен"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Скенирање лице"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Испрати"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Не се препознава ликот"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Користи отпечаток"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"„Отклучувањето со лик“ е недостапно"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth е поврзан."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Процентот на батеријата е непознат."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Поврзано со <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Авионски режим."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN е вклучена."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Батерија <xliff:g id="NUMBER">%d</xliff:g> проценти."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Батерија <xliff:g id="PERCENTAGE">%1$d</xliff:g> отсто, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Полнење на батеријата, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> отсто."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Батерија <xliff:g id="PERCENTAGE">%d</xliff:g> отсто, , полнењето е паузирано за заштита на батеријата."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Батерија <xliff:g id="PERCENTAGE">%1$d</xliff:g> отсто, <xliff:g id="TIME">%2$s</xliff:g>, полнењето е паузирано за заштита на батеријата."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Видете ги сите известувања"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Овозможен е телепринтер."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Ѕвонче на вибрации."</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"Имаше проблем при преземањето на картичките. Обидете се повторно подоцна"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Поставки за заклучен екран"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"Скенер на QR-кодови"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Се ажурира"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Работен профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Авионски режим"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Нема да го слушнете следниот аларм <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -995,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Отворете ја <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"За да ја додадете апликацијата <xliff:g id="APPNAME">%1$s</xliff:g> како кратенка, треба да бидат исполнети следниве услови"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• апликацијата е поставена"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• најмалку една картичка е додадена во Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• инсталирана е апликација за камера"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• апликацијата е поставена"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• достапен е најмалку еден уред"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Откажи"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Префрли сега"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Отворете го телефонот за подобро селфи"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Да се префрли на предниот екран за подобро селфи?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Користете ја задната камера за поширока фотографија со повисока резолуција."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Екранов ќе се исклучи"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index e92ebeae265c..650aa77fa95a 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"വോയ്‌സ് സഹായം"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR കോഡ് സ്കാനർ"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"അൺലോക്ക് ചെയ്തു"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ഉപകരണം ലോക്ക് ചെയ്തു"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"മുഖം സ്കാൻ ചെയ്യുന്നു"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"അയയ്ക്കുക"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"മുഖം തിരിച്ചറിയാനാകുന്നില്ല"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"പകരം ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കൂ"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ഫെയ്‌സ് അൺലോക്ക് ലഭ്യമല്ല"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ബ്ലൂടൂത്ത് കണക്‌റ്റുചെയ്തു."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ബാറ്ററി ശതമാനം അജ്ഞാതമാണ്."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> എന്നതിലേക്ക് കണക്‌റ്റുചെയ്‌തു."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"ഫ്ലൈറ്റ് മോഡ്."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ഓണാണ്."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"ബാറ്ററി <xliff:g id="NUMBER">%d</xliff:g> ശതമാനം."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"ബാറ്ററി <xliff:g id="PERCENTAGE">%1$d</xliff:g> ശതമാനം, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"ബാറ്ററി ചാർജ് ചെയ്യുന്നു, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"ബാറ്ററി ചാർജ് <xliff:g id="PERCENTAGE">%d</xliff:g> ശതമാനം, ബാറ്ററി സംരക്ഷിക്കുന്നതിന്, ചാർജ് ചെയ്യൽ താൽക്കാലികമായി നിർത്തി."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"ബാറ്ററി ചാർജ് <xliff:g id="PERCENTAGE">%1$d</xliff:g> ശതമാനം, <xliff:g id="TIME">%2$s</xliff:g>, ബാറ്ററി സംരക്ഷിക്കുന്നതിന്, ചാർജ് ചെയ്യൽ താൽക്കാലികമായി നിർത്തി."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"എല്ലാ അറിയിപ്പുകളും കാണുക"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter പ്രവർത്തനക്ഷമമാണ്."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"റിംഗർ വൈബ്രേറ്റ് ചെയ്യുന്നു."</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ഉപയോഗിക്കാൻ അൺലോക്ക് ചെയ്യുക"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"നിങ്ങളുടെ കാർഡുകൾ ലഭ്യമാക്കുന്നതിൽ ഒരു പ്രശ്‌നമുണ്ടായി, പിന്നീട് വീണ്ടും ശ്രമിക്കുക"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ലോക്ക് സ്ക്രീൻ ക്രമീകരണം"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR കോഡ് സ്കാനർ"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ഫ്ലൈറ്റ് മോഡ്"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 005a04170ccc..b2a7df80bf33 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Дуут туслах"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR код сканнер"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Түгжээг тайлсан"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Төхөөрөмжийг түгжсэн"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Скан хийх нүүр царай"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Илгээх"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Царайг танихгүй байна"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Оронд нь хурууны хээ ашиглах"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Царайгаар түгжээ тайлах боломжгүй"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth холбогдсон."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарейн хувь тодорхойгүй байна."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>-тай холбогдсон."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Нислэгийн горим"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN асаалттай байна."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Батарей <xliff:g id="NUMBER">%d</xliff:g> хувьтай."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Батарей <xliff:g id="PERCENTAGE">%1$d</xliff:g> хувь, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Батарейг цэнэглэж байна, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Батарей <xliff:g id="PERCENTAGE">%d</xliff:g> хувь, батарейг хамгаалахын тулд цэнэглэхийг түр зогсоосон."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Батарей <xliff:g id="PERCENTAGE">%1$d</xliff:g> хувь, <xliff:g id="TIME">%2$s</xliff:g>, батарейг хамгаалахын тулд цэнэглэхийг түр зогсоосон."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Бүх мэдэгдлийг харах"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter идэвхтэй болов."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Хонхны чичиргээ."</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"Таны картыг авахад асуудал гарлаа. Дараа дахин оролдоно уу"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Түгжигдсэн дэлгэцийн тохиргоо"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"QR код сканнер"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Шинэчилж байна"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ажлын профайл"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Нислэгийн горим"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-т та дараагийн сэрүүлгээ сонсохгүй"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d EEE"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g>-г нээх"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> аппыг товчлолоор нэмэхийн тулд дараахыг баталгаажуулна уу"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Аппыг тохируулсан"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Дор хаяж нэг картыг Wallet-д нэмсэн"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Камер аппыг суулгах"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Аппыг тохируулсан"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Дор хаяж нэг төхөөрөмж боломжтой"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 2810cb731700..176f996dfe25 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"व्हॉइस सहाय्य"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"वॉलेट"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR कोड स्कॅनर"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"अनलॉक केले"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"डिव्हाइस लॉक केले"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"चेहरा स्कॅन करत आहे"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"पाठवा"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"चेहरा ओळखू शकत नाही"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"त्याऐवजी फिंगरप्रिंट वापरा"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"फेस अनलॉक उपलब्ध नाही"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ब्लूटूथ कनेक्‍ट केले."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"बॅटरीच्या चार्जिंगची टक्केवारी माहित नाही."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> शी कनेक्‍ट केले."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"विमान मोड."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN सुरू."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"बॅटरी <xliff:g id="NUMBER">%d</xliff:g> टक्के."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"बॅटरी <xliff:g id="PERCENTAGE">%1$d</xliff:g> टक्के आहे, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"बॅटरी चार्ज होत आहे, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> टक्के."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"बॅटरी <xliff:g id="PERCENTAGE">%d</xliff:g> टक्के आहे, बॅटरीच्या संंरक्षणासाठी चार्ज करणे थांबवले."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"बॅटरी <xliff:g id="PERCENTAGE">%1$d</xliff:g> टक्के आहे, <xliff:g id="TIME">%2$s</xliff:g>, बॅटरीच्या संंरक्षणासाठी चार्ज करणे थांबवले."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"सर्व सूचना पहा"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter सक्षम केले."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"रिंगर व्हायब्रेट."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"वापरण्यासाठी अनलॉक करा"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"तुमची कार्ड मिळवताना समस्या आली, कृपया नंतर पुन्हा प्रयत्न करा"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लॉक स्क्रीन सेटिंग्ज"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR कोड स्कॅनर"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"अपडेट करत आहे"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाईल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"विमान मोड"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"तुम्ही तुमचा <xliff:g id="WHEN">%1$s</xliff:g> वाजता होणारा पुढील अलार्म ऐकणार नाही"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> उघडा"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> हे अ‍ॅप शॉर्टकट म्हणून जोडण्यासाठी, पुढील गोष्टींची खात्री करा"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• अ‍ॅप सेट करणे"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Wallet मध्ये किमान एक कार्ड जोडणे"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• कॅमेरा अ‍ॅप इंस्टॉल करणे"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• अ‍ॅप सेट करणे"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• किमान एक डिव्हाइस उपलब्ध करणे"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 1d82ec09c484..049da6da9004 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Bantuan Suara"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Pengimbas Kod QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Tidak berkunci"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Peranti dikunci"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Mengimbas wajah"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Hantar"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Tak dapat mengecam wajah"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Gunakan cap jari"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Buka Kunci Wajah tidak tersedia"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth disambungkan."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Peratusan kuasa bateri tidak diketahui."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Disambungkan kepada <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Mod pesawat"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN dihidupkan."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Bateri <xliff:g id="NUMBER">%d</xliff:g> peratus."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Bateri <xliff:g id="PERCENTAGE">%1$d</xliff:g> peratus, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Bateri mengecas, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> peratus."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Bateri <xliff:g id="PERCENTAGE">%d</xliff:g> peratus, pengecasan dijeda demi perlindungan bateri."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Bateri <xliff:g id="PERCENTAGE">%1$d</xliff:g> peratus, <xliff:g id="TIME">%2$s</xliff:g>, pengecasan dijeda demi perlindungan bateri."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Lihat semua pemberitahuan"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Mesin Teletaip didayakan."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Pendering bergetar."</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"Terdapat masalah sewaktu mendapatkan kad anda. Sila cuba sebentar lagi"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Tetapan skrin kunci"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"Pengimbas kod QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mengemaskinikan"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod pesawat"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar penggera yang seterusnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Buka <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Untuk menambahkan apl <xliff:g id="APPNAME">%1$s</xliff:g> sebagai pintasan, pastikan"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Apl disediakan"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Sekurang-kurangnya satu kad telah ditambahkan pada Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Pasang apl kamera"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Apl disediakan"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Sekurang-kurangnya satu peranti tersedia"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index f79e32f8f713..fc2137b8f2e6 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"အသံ အကူအညီ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR ကုဒ်ဖတ်စနစ်"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"လော့ခ်ဖွင့်ထားသည်"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"စက်ပစ္စည်းကို လော့ခ်ချထားသည်"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"မျက်နှာ စကင်ဖတ်နေသည်"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ပို့ရန်"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"မျက်နှာကို မမှတ်မိပါ"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"လက်ဗွေကို အစားထိုးသုံးပါ"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း မရနိုင်ပါ"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ဘလူးတုသ်ချိတ်ဆက်ထားမှု"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ဘက်ထရီရာခိုင်နှုန်းကို မသိပါ။"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>သို့ ချိတ်ဆက်ထား"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"လေယာဉ်ပျံမုဒ်"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ကို ဖွင့်ထားသည်။"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"ဘက်ထရီ <xliff:g id="NUMBER">%d</xliff:g> ရာခိုင်နှုန်း။"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"ဘက်ထရီ <xliff:g id="PERCENTAGE">%1$d</xliff:g> ရာခိုင်နှုန်း။ <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"ဘက်ထရီအားသွင်းနေသည်၊ <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%။"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"ဘက်ထရီ <xliff:g id="PERCENTAGE">%d</xliff:g> ရာခိုင်နှုန်း။ ဘက်ထရီကာကွယ်ရန် အားသွင်းမှု ခဏရပ်ထားသည်။"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"ဘက်ထရီ <xliff:g id="PERCENTAGE">%1$d</xliff:g> ရာခိုင်နှုန်း။ <xliff:g id="TIME">%2$s</xliff:g>။ ဘက်ထရီကာကွယ်ရန် အားသွင်းမှု ခဏရပ်ထားသည်။"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"သတိပေးချက်များအားလုံးကို ကြည့်ရန်"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter ရရှိသည်။"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"တုန်ခါခြင်း ဖုန်းမြည်သံ"</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"သုံးရန် လော့ခ်ဖွင့်ပါ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"သင်၏ကတ်များ ရယူရာတွင် ပြဿနာရှိနေသည်၊ နောက်မှ ထပ်စမ်းကြည့်ပါ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"လော့ခ်မျက်နှာပြင် ဆက်တင်များ"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR ကုဒ် စကင်ဖတ်စနစ်"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"အလုပ် ပရိုဖိုင်"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"လေယာဉ်ပျံမုဒ်"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE၊ MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index bbd5dc9d0f92..adfccb7967b1 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Talehjelp"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-kodeskanner"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Ulåst"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheten er låst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skanning av ansikt"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Ansiktet gjenkjennes ikke"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Bruk fingeravtrykk"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Ansiktslås er utilgjengelig"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth er tilkoblet."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batteriprosenten er ukjent."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Koblet til <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Flymodus."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN på."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Batteri – <xliff:g id="NUMBER">%d</xliff:g> prosent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batterinivået er <xliff:g id="PERCENTAGE">%1$d</xliff:g> prosent, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batteriet lades – <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> prosent."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batterinivået er <xliff:g id="PERCENTAGE">%d</xliff:g> prosent. Lading er satt på pause for å beskytte batteriet."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batterinivået er <xliff:g id="PERCENTAGE">%1$d</xliff:g> prosent, <xliff:g id="TIME">%2$s</xliff:g>. Lading er satt på pause for å beskytte batteriet."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Se alle varslene"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter er aktivert."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibreringsmodus."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås opp for å bruke"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Det oppsto et problem med henting av kortene. Prøv på nytt senere"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Innstillinger for låseskjermen"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-kodeskanner"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Oppdaterer"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work-profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flymodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Du hører ikke neste innstilte alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d. MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Åpne <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"For å legge til <xliff:g id="APPNAME">%1$s</xliff:g>-appen som en snarvei må du sørge for at"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• appen er konfigurert"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• minst ett kort er lagt til i Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• en kameraapp er installert"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• appen er konfigurert"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• minst én enhet er tilgjengelig"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 3baf1de484aa..fee1ffdb2426 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"आवाज सहायता"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR कोड स्क्यानर"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"अनलक गरिएको छ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"यन्त्र लक गरिएको छ"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"अनुहार स्क्यान गर्दै"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"पठाउनुहोस्"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"अनुहार पहिचान गर्न सकिएन"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"बरु फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"फेस अनलक उपलब्ध छैन"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ब्लुटुथ जडान भयो।"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ब्याट्रीमा कति प्रतिशत चार्ज छ भन्ने कुराको जानाकरी छैन।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> मा जडित।"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"हवाइजहाज मोड।"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN सक्रिय छ।"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"ब्याट्री <xliff:g id="NUMBER">%d</xliff:g> प्रतिशत"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"ब्याट्री <xliff:g id="PERCENTAGE">%1$d</xliff:g> प्रतिशत, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"ब्याट्री चार्ज हुँदैछ, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> प्रतिशत भयो।"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"ब्याट्री <xliff:g id="PERCENTAGE">%d</xliff:g> प्रतिशत, ब्याट्री जोगाउन चार्ज गर्ने कार्य पज गरिएको छ।"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"ब्याट्री <xliff:g id="PERCENTAGE">%1$d</xliff:g> प्रतिशत, <xliff:g id="TIME">%2$s</xliff:g>, ब्याट्री जोगाउन चार्ज गर्ने कार्य पज गरिएको छ।"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"सबै सूचनाहरू हेर्नुहोस्"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"टेलि टाइपराइटर सक्षम गरियो।"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"बज्ने कम्पन हुन्छ।"</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"यो वालेट प्रयोग गर्न डिभाइस अनलक गर्नुहोस्"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"तपाईंका कार्डहरू प्राप्त गर्ने क्रममा समस्या भयो, कृपया पछि फेरि प्रयास गर्नुहोस्"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लक स्क्रिनसम्बन्धी सेटिङ"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR कोड स्क्यानर"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाइजहाज मोड"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 84d5b6d680d1..aa7f59e67f2f 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -117,7 +117,7 @@
<string name="accessibility_back" msgid="6530104400086152611">"Terug"</string>
<string name="accessibility_home" msgid="5430449841237966217">"Startscherm"</string>
<string name="accessibility_menu" msgid="2701163794470513040">"Menu"</string>
- <string name="accessibility_accessibility_button" msgid="4089042473497107709">"Toegankelijkheid"</string>
+ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"Toe­gankelijk­heid"</string>
<string name="accessibility_rotate_button" msgid="1238584767612362586">"Scherm draaien"</string>
<string name="accessibility_recent" msgid="901641734769533575">"Overzicht"</string>
<string name="accessibility_camera_button" msgid="2938898391716647247">"Camera"</string>
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Spraakassistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portemonnee"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-codescanner"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Ontgrendeld"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Apparaat vergrendeld"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Gezicht scannen"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Verzenden"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Gezicht niet herkend"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Vingerafdruk gebruiken"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Ontgrendelen via gezichtsherkenning niet beschikbaar"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-verbinding ingesteld."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batterijpercentage onbekend."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Verbonden met <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Vliegtuigmodus."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN staat aan."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Batterij: <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batterij: <xliff:g id="PERCENTAGE">%1$d</xliff:g> procent, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batterij wordt opgeladen, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%% procent."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batterij <xliff:g id="PERCENTAGE">%d</xliff:g> procent. Opladen is onderbroken om de batterij te beschermen."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batterij <xliff:g id="PERCENTAGE">%1$d</xliff:g> procent, <xliff:g id="TIME">%2$s</xliff:g>. Opladen is onderbroken om de batterij te beschermen."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Alle meldingen bekijken"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter staat aan."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Belsoftware trilt."</string>
@@ -471,7 +466,7 @@
<string name="stream_notification" msgid="7930294049046243939">"Melding"</string>
<string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
<string name="stream_dtmf" msgid="7322536356554673067">"Frequentie voor tweevoudige multitoon"</string>
- <string name="stream_accessibility" msgid="3873610336741987152">"Toegankelijkheid"</string>
+ <string name="stream_accessibility" msgid="3873610336741987152">"Toe­gankelijk­heid"</string>
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Bellen"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Trillen"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Geluid staat uit"</string>
@@ -501,10 +496,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontgrendelen om te gebruiken"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Er is een probleem opgetreden bij het ophalen van je kaarten. Probeer het later opnieuw."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Instellingen voor vergrendelscherm"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-codescanner"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Updaten"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
- <string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
+ <string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuig­modus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Je hoort je volgende wekker niet <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"om <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d mmm"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"u:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> openen"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Zorg voor het volgende om de <xliff:g id="APPNAME">%1$s</xliff:g>-app toe te voegen als snelkoppeling:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• De app is ingesteld"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Er is ten minste één kaart aan Wallet toegevoegd"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Er moet een camera-app zijn geïnstalleerd"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• De app is ingesteld"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Er is ten minste één apparaat beschikbaar"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 433170e24b56..1cda6deba426 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ଭଏସ୍‌ ସହାୟକ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"ୱାଲେଟ୍"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR କୋଡ ସ୍କାନର"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"ଅନଲକ କରାଯାଇଛି"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ଡିଭାଇସ୍ ଲକ୍ ହୋଇଯାଇଛି"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ଫେସ୍ ସ୍କାନିଙ୍ଗ କରାଯାଉଛି"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ପଠାନ୍ତୁ"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ଫେସ ଚିହ୍ନଟ ହୋଇପାରିବ ନାହିଁ"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ଫେସ ଅନଲକ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ବ୍ଲୁଟୂଥ୍‍‌ ସଂଯୋଗ କରାଯାଇଛି।"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ବ୍ୟାଟେରୀ ଶତକଡ଼ା ଅଜଣା ଅଟେ।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ସହ ସଂଯୁକ୍ତ"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍‌।"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ଅନ୍‍।"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"ବ୍ୟାଟେରୀ <xliff:g id="NUMBER">%d</xliff:g> ଶତକଡ଼ା।"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"ବେଟେରୀ <xliff:g id="PERCENTAGE">%1$d</xliff:g> ଶତକଡ଼ା, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"ବ୍ୟାଟେରୀ ଚାର୍ଜ ହେଉଛି, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ଶତକଡ଼ା।"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"ବେଟେରୀ <xliff:g id="PERCENTAGE">%d</xliff:g> ଶତକଡ଼ା, ବେଟେରୀର ସୁରକ୍ଷା ପାଇଁ ଚାର୍ଜିଂକୁ ବିରତ କରାଯାଇଛି।"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"ବେଟେରୀ <xliff:g id="PERCENTAGE">%1$d</xliff:g> ଶତକଡ଼ା, <xliff:g id="TIME">%2$s</xliff:g>, ବେଟେରୀର ସୁରକ୍ଷା ପାଇଁ ଚାର୍ଜିଂକୁ ବିରତ କରାଯାଇଛି।"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"ସମସ୍ତ ବିଜ୍ଞପ୍ତି ଦେଖନ୍ତୁ"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"ଟେଲି-ଟାଇପରାଇଟର୍ ସକ୍ଷମ ଅଛି।"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"ରିଙ୍ଗର୍‌ କମ୍ପନରେ ଅଛି।"</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"ଆପଣଙ୍କ କାର୍ଡଗୁଡ଼ିକ ପାଇବାରେ ଏକ ସମସ୍ୟା ହୋଇଥିଲା। ଦୟାକରି ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ସ୍କ୍ରିନ୍ ଲକ୍ ସେଟିଂସ୍"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"QR କୋଡ ସ୍କାନର"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ଅପଡେଟ ହେଉଛି"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‌"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>ବେଳେ ଆପଣ ନିଜର ପରବର୍ତ୍ତୀ ଆଲାର୍ମ ଶୁଣିପାରିବେ ନାହିଁ"</string>
@@ -530,7 +526,7 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"ସ୍ୱଚାଳିତ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"କୌଣସି ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ ନାହିଁ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"କୌଣସି ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ ନାହିଁ ଏବଂ ବାର୍ତ୍ତାଳାପ ବିଭାଗର ନିମ୍ନରେ ଦେଖାଯାଏ"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ"</string>
+ <string name="notification_channel_summary_default" msgid="3282930979307248890">"ଫୋନ ସେଟିଂସ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ ହୋଇପାରେ"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ। <xliff:g id="APP_NAME">%1$s</xliff:g>ରୁ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଡିଫଲ୍ଟ ଭାବରେ ବବଲ୍ ହୁଏ।"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ଏହି ବିଜ୍ଞପ୍ତି ପ୍ରାପ୍ତ ହେବା ସମୟରେ ସାଉଣ୍ଡ ହେବା ଉଚିତ ନା ଭାଇବ୍ରେସନ୍ ତାହା ସିଷ୍ଟମକୁ ସ୍ଥିର କରିବାକୁ ଦିଅନ୍ତୁ"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;ସ୍ଥିତି:&lt;/b&gt; ଡିଫଲ୍ଟକୁ ପ୍ରମୋଟ୍ କରାଯାଇଛି"</string>
@@ -665,7 +661,7 @@
<item msgid="8619482474544321778">"ଏହି ଆଇକନ୍‍ ଦେଖାନ୍ତୁ ନାହିଁ"</item>
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"କମ୍‍-ଅଗ୍ରାଧିକାର ବିଜ୍ଞପ୍ତି ଆଇକନ୍‍ ଦେଖାନ୍ତୁ"</string>
- <string name="other" msgid="429768510980739978">"ଅନ୍ୟାନ୍ୟ"</string>
+ <string name="other" msgid="429768510980739978">"ଅନ୍ୟ"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ଟାଇଲ୍ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ଶେଷରେ ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ଟାଇଲ୍ ମୁଭ୍ କରନ୍ତୁ"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ଖୋଲନ୍ତୁ"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"ଏକ ସର୍ଟକଟ ଭାବେ <xliff:g id="APPNAME">%1$s</xliff:g> ଆପ ଯୋଗ କରିବାକୁ, ଏହା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• ଆପ ସେଟ ଅପ କରାଯାଇଥିବା"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Walletରେ ଅତିକମରେ ଗୋଟିଏ କାର୍ଡ ଯୋଗ କରାଯାଇଥିବା"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• ଏକ କେମେରା ଆପ ଇନଷ୍ଟଲ କରିବା"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• ଆପ ସେଟ ଅପ କରାଯାଇଛି"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ଅତିକମରେ ଗୋଟିଏ ଡିଭାଇସ ଉପଲବ୍ଧ ଅଛି"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index ed4988f11dc7..15010ad2240d 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"ਅਵਾਜ਼ੀ ਸਹਾਇਕ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR ਕੋਡ ਸਕੈਨਰ"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"ਅਣਲਾਕ ਹੈ"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"ਡੀਵਾਈਸ ਲਾਕ ਹੈ"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ਚਿਹਰਾ ਸਕੈਨ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ਭੇਜੋ"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ਇਸਦੀ ਬਜਾਏ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ਫ਼ੇਸ ਅਣਲਾਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ਕਨੈਕਟ ਕੀਤੀ।"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ਬੈਟਰੀ ਪ੍ਰਤੀਸ਼ਤ ਅਗਿਆਤ ਹੈ।"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ।"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"ਏਅਰਪਲੇਨ ਮੋਡ।"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ਚਾਲੂ ਹੈ।"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"ਬੈਟਰੀ <xliff:g id="NUMBER">%d</xliff:g> ਪ੍ਰਤੀਸ਼ਤ ਹੈ।"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"ਬੈਟਰੀ <xliff:g id="PERCENTAGE">%1$d</xliff:g> ਫ਼ੀਸਦ, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"ਬੈਟਰੀ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ਪ੍ਰਤੀਸ਼ਤ ਹੋ ਗਈ।"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"ਬੈਟਰੀ <xliff:g id="PERCENTAGE">%d</xliff:g> ਫ਼ੀਸਦ, ਬੈਟਰੀ ਦੀ ਸੁਰੱਖਿਆ ਲਈ ਚਾਰਜਿੰਗ ਨੂੰ ਰੋਕਿਆ ਨੂੰ ਚਲਾਉਂਦੀ ਹੈ"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"ਬੈਟਰੀ <xliff:g id="PERCENTAGE">%1$d</xliff:g> ਫ਼ੀਸਦ, <xliff:g id="TIME">%2$s</xliff:g>, ਬੈਟਰੀ ਦੀ ਸੁਰੱਖਿਆ ਲਈ ਚਾਰਜਿੰਗ ਨੂੰ ਰੋਕਿਆ ਗਿਆ।"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਦੇਖੋ"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"ਟੈਲੀ ਟਾਈਪਰਾਈਟਰ ਸਮਰਥਿਤ।"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"ਰਿੰਗਰ ਥਰਥਰਾਹਟ।"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ਵਰਤਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ਤੁਹਾਡੇ ਕਾਰਡ ਪ੍ਰਾਪਤ ਕਰਨ ਵਿੱਚ ਕੋਈ ਸਮੱਸਿਆ ਆਈ, ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ਲਾਕ ਸਕ੍ਰੀਨ ਸੈਟਿੰਗਾਂ"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR ਕੋਡ ਸਕੈਨਰ"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"ਅੱਪਡੇਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ਤੁਸੀਂ <xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ ਆਪਣਾ ਅਗਲਾ ਅਲਾਰਮ ਨਹੀਂ ਸੁਣੋਗੇ"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> ਖੋਲ੍ਹੋ"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> ਐਪ ਨੂੰ ਸ਼ਾਰਟਕੱਟ ਵਜੋਂ ਸ਼ਾਮਲ ਕਰਨ ਲਈ, ਪੱਕਾ ਕਰੋ ਕਿ"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• ਐਪ ਦਾ ਸੈੱਟਅੱਪ ਹੋ ਗਿਆ ਹੈ"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• ਘੱਟੋ-ਘੱਟ ਇੱਕ ਕਾਰਡ ਨੂੰ Wallet ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• ਕੈਮਰਾ ਐਪ ਸਥਾਪਤ ਕਰੋ"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• ਐਪ ਦਾ ਸੈੱਟਅੱਪ ਹੋ ਗਿਆ ਹੈ"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• ਘੱਟੋ-ਘੱਟ ਇੱਕ ਡੀਵਾਈਸ ਉਪਲਬਧ ਹੈ"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 47b68b10be09..39f7c30c42e2 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asystent głosowy"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portfel"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Skaner kodów QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Odblokowano"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Urządzenie zablokowane"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skanowanie twarzy"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Wyślij"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Nie można rozpoznać twarzy"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Użyj odcisku palca"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Rozpoznawanie twarzy niedostępne"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth połączony."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Poziom naładowania baterii jest nieznany."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Połączono z <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Tryb samolotowy."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"Sieć VPN włączona."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Bateria: <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Bateria: <xliff:g id="PERCENTAGE">%1$d</xliff:g> procent, <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Ładuję baterię, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> procent."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Bateria <xliff:g id="PERCENTAGE">%d</xliff:g> procent. Ładowanie zostało wstrzymane, aby utrzymać baterię w dobrym stanie."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Bateria <xliff:g id="PERCENTAGE">%1$d</xliff:g> procent, <xliff:g id="TIME">%2$s</xliff:g>. Ładowanie zostało wstrzymane, aby utrzymać baterię w dobrym stanie."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Zobacz wszystkie powiadomienia"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Dalekopis (TTY) włączony."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Dzwonek z wibracjami."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odblokuj, aby użyć"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Podczas pobierania kart wystąpił problem. Spróbuj ponownie później."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ustawienia ekranu blokady"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Skaner kodów QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Aktualizuję"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil służbowy"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Tryb samolotowy"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nie usłyszysz swojego następnego alarmu <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otwórz: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Aby dodać aplikację <xliff:g id="APPNAME">%1$s</xliff:g> jako skrót, upewnij się, że spełnione zostały te warunki:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Aplikacja została skonfigurowana."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Do Portfela została dodana co najmniej 1 karta."</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Zainstalowano aplikację aparatu."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Aplikacja została skonfigurowana."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Dostępne jest co najmniej 1 urządzenie."</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 4ff823aee2d6..1630d08dce42 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistência de voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Carteira"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Leitor de código QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Desbloqueado"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Verificando rosto"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Rosto não reconhecido"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use a impressão digital"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"O Desbloqueio facial não está disponível"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Porcentagem da bateria desconhecida."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Modo avião."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ativada."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Bateria em <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Bateria em <xliff:g id="PERCENTAGE">%1$d</xliff:g> de carga, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Bateria carregando: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Bateria com <xliff:g id="PERCENTAGE">%d</xliff:g> de carga, o carregamento foi pausado para proteção da bateria."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Bateria com <xliff:g id="PERCENTAGE">%1$d</xliff:g> de carga, <xliff:g id="TIME">%2$s</xliff:g>, o carregamento foi pausado para proteção da bateria."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Ver todas as notificações"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTYpewriter ativado."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibração da campainha."</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Leitor de código QR"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index a50624e76c06..22bb507090e4 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -168,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Imposs. reconhecer rosto"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Usar impressão digital"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Desbloqueio facial indisponível"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ligado."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Percentagem da bateria desconhecida."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ligado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -180,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Modo de avião"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ativada."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Bateria a <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Bateria a <xliff:g id="PERCENTAGE">%1$d</xliff:g> por cento, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Bateria a carregar (<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%)."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Bateria a <xliff:g id="PERCENTAGE">%d</xliff:g> por cento, o carregamento foi pausado para proteção da bateria."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Bateria a <xliff:g id="PERCENTAGE">%1$d</xliff:g> por cento, <xliff:g id="TIME">%2$s</xliff:g>, o carregamento foi pausado para proteção da bateria."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Ver todas as notificações"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletipo ativado."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Campainha em vibração."</string>
@@ -501,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao obter os seus cartões. Tente mais tarde."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Definições do ecrã de bloqueio"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"Leitor de códigos QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"A atualizar"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avião"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Não vai ouvir o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -994,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Abrir <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Para adicionar a app <xliff:g id="APPNAME">%1$s</xliff:g> como um atalho, garanta"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• A app está configurada"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Foi adicionado, pelo menos, um cartão à Carteira"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Instale uma app de câmara"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• A app está configurada"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Está disponível, pelo menos, um dispositivo"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"Cancelar"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"Inverter agora"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"Desdobre o telemóvel para uma selfie melhor"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"Inverter para ecrã frontal para uma selfie melhor?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"Use a câmara traseira para uma foto mais ampla com uma resolução superior."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ Este ecrã vai ser desligado"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 4ff823aee2d6..1630d08dce42 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistência de voz"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Carteira"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Leitor de código QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Desbloqueado"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Verificando rosto"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Rosto não reconhecido"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Use a impressão digital"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"O Desbloqueio facial não está disponível"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth conectado."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Porcentagem da bateria desconhecida."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Modo avião."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ativada."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Bateria em <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Bateria em <xliff:g id="PERCENTAGE">%1$d</xliff:g> de carga, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Bateria carregando: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Bateria com <xliff:g id="PERCENTAGE">%d</xliff:g> de carga, o carregamento foi pausado para proteção da bateria."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Bateria com <xliff:g id="PERCENTAGE">%1$d</xliff:g> de carga, <xliff:g id="TIME">%2$s</xliff:g>, o carregamento foi pausado para proteção da bateria."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Ver todas as notificações"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTYpewriter ativado."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibração da campainha."</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Leitor de código QR"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index e281e88da858..944af8f49283 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistent vocal"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portofel"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Scanner de coduri QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Descuiat"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispozitiv blocat"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanarea chipului"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Trimite"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Chip nerecunoscut"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Folosește amprenta"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Deblocarea facială nu este disponibilă"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Conectat prin Bluetooth."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Procentajul bateriei este necunoscut."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectat la <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Mod Avion."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"Rețea VPN activată"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Baterie: <xliff:g id="NUMBER">%d</xliff:g> la sută."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Baterie la <xliff:g id="PERCENTAGE">%1$d</xliff:g> , <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Bateria se încarcă, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> la sută."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Baterie la <xliff:g id="PERCENTAGE">%d</xliff:g> ; încărcare întreruptă pentru protejarea bateriei."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Baterie la <xliff:g id="PERCENTAGE">%1$d</xliff:g> ; <xliff:g id="TIME">%2$s</xliff:g>, încărcare întreruptă pentru protejarea bateriei."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Vezi toate notificările"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter activat."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibrare sonerie."</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"A apărut o problemă la preluarea cardurilor. Încearcă din nou mai târziu"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setările ecranului de blocare"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"Scanner de coduri QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Se actualizează"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil de serviciu"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod Avion"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nu vei auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EE, z LLL"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Deschide <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Pentru a adăuga aplicația <xliff:g id="APPNAME">%1$s</xliff:g> drept comandă rapidă, asigură-te"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Aplicația este configurată"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Cel puțin un card a fost adăugat în Portofel"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Instalează o aplicație pentru camera foto"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Aplicația este configurată"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Este disponibil cel puțin un dispozitiv"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index b7f10f2943d2..20816c84839c 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Аудиоподсказки"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Кошелек"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Сканер QR-кодов"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Разблокировано"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Устройство заблокировано"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Сканирование лица"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Отправить"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Лицо не распознано."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Используйте отпечаток."</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Фейсконтроль недоступен"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth-соединение установлено."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Уровень заряда батареи в процентах неизвестен."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>: подключено."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Режим полета."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"Режим VPN включен."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Заряд батареи в процентах: <xliff:g id="NUMBER">%d</xliff:g>."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Заряд батареи: <xliff:g id="PERCENTAGE">%1$d</xliff:g>. Его хватит <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Зарядка батареи. Текущий заряд: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Зарядка приостановлена на уровне <xliff:g id="PERCENTAGE">%d</xliff:g>. Это поможет продлить срок службы батареи."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Заряд батареи: <xliff:g id="PERCENTAGE">%1$d</xliff:g>. Его хватит <xliff:g id="TIME">%2$s</xliff:g>. Зарядка приостановлена, чтобы продлить срок службы батареи."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Показать все уведомления"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Телетайп включен."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Вибровызов."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Разблокировать для использования"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Не удалось получить информацию о картах. Повторите попытку позже."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Настройки заблокированного экрана"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Сканер QR-кодов"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Обновление"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Рабочий профиль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим полета"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Следующий будильник: <xliff:g id="WHEN">%1$s</xliff:g>. Звук отключен."</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"d MMM EEEE"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Открыть \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Для добавления ярлыка приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\" должны выполняться следующие условия:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Приложение установлено."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• В Кошельке есть хотя бы одна карта."</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Установлено приложение камеры."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Приложение установлено."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Доступно хотя бы одно устройство."</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index f9a6b0b45ecb..2446ce8a1da9 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"හඬ සහාය"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR කේත ස්කෑනරය"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"අගුළු හරින ලදි"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"උපාංගය අගුලු දමා ඇත"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"මුහුණ ස්කෑන් කිරීම"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"යවන්න"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"මුහුණ හඳුනා ගත නොහැක"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ඒ වෙනුවට ඇඟිලි සලකුණ භාවිත කරන්න"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"මුහුණෙන් අගුළු ඇරීම නැත"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"බ්ලූටූත් සම්බන්ධිතයි."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"බැටරි ප්‍රතිශතය නොදනී."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> වෙත සම්බන්ධ කරන ලදි."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"අහස්යානා ආකාරය."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN ක්‍රියාත්මකයි."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"බැටරි ප්‍රතිශතය <xliff:g id="NUMBER">%d</xliff:g>"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"බැටරි ප්‍රතිශතය <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"බැටරිය ආරෝපණය කරමින්, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"බැටරිය සියයට <xliff:g id="PERCENTAGE">%d</xliff:g>, බැටරි ආරක්ෂණය සඳහා ආරෝපණය විරාම ගන්වා ඇත."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"බැටරිය සියයට <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>, බැටරි ආරක්ෂණය සඳහා ආරෝපණය විරාම ගන්වා ඇත."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"සියලු දැනුම්දීම් බලන්න"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter ක්‍රියාත්මකයි."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"හඬ නඟනය කම්පනය වේ."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"භාවිත කිරීමට අගුලු හරින්න"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ඔබගේ කාඩ්පත ලබා ගැනීමේ ගැටලුවක් විය, කරුණාකර පසුව නැවත උත්සාහ කරන්න"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"අගුලු තිර සැකසීම්"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR කේත ස්කෑනරය"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"යාවත්කාලීන කිරීම"</string>
<string name="status_bar_work" msgid="5238641949837091056">"කාර්යාල පැතිකඩ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ගුවන්යානා ප්‍රකාරය"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ඔබට ඔබේ ඊළඟ එලාමය <xliff:g id="WHEN">%1$s</xliff:g> නොඇසෙනු ඇත"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> විවෘත කරන්න"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"කෙටිමඟක් ලෙස <xliff:g id="APPNAME">%1$s</xliff:g> එක් කිරීමට, තහවුරු කර ගන්න"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• යෙදුම සකසා ඇත"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Wallet වෙත අවම වශයෙන් එක කාඩ්පතක් එක් කර ඇත"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• කැමරා යෙදුමක් ස්ථාපන කරන්න"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• යෙදුම සකසා ඇත"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• අවම වශයෙන් එක උපාංගයක් ලැබේ"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 51f0c7da28e6..39125800f064 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Hlasový asistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Peňaženka"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Skener QR kódov"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Odomknuté"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Zariadenie je uzamknuté"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skenovanie tváre"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Odoslať"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Tvár sa nedá rozpoznať"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Používať radšej odtlačok"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Odomknutie tvárou nie je k dispozícii"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth pripojené."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Percento batérie nie je známe."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Pripojené k zariadeniu <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Režim v lietadle."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN je zapnuté."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Batéria <xliff:g id="NUMBER">%d</xliff:g> percent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batéria má <xliff:g id="PERCENTAGE">%1$d</xliff:g> percent, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Nabíja sa batéria, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batéria má <xliff:g id="PERCENTAGE">%d</xliff:g> percent, z dôvodu jej ochrany bolo nabíjanie pozastavené."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batéria má <xliff:g id="PERCENTAGE">%1$d</xliff:g> percent, <xliff:g id="TIME">%2$s</xliff:g>, z dôvodu jej ochrany bolo nabíjanie pozastavené."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Zobraziť všetky upozornenia"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Rozhranie TeleTypewriter je povolené."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibračné zvonenie."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odomknúť a použiť"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pri načítavaní kariet sa vyskytol problém. Skúste to neskôr."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavenia uzamknutej obrazovky"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Skener QR kódov"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Aktualizuje sa"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovný profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim v lietadle"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Váš budík o <xliff:g id="WHEN">%1$s</xliff:g> sa nespustí"</string>
@@ -531,7 +526,7 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Žiadny zvuk ani vibrácie"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Žiadny zvuk ani vibrácie a zobrazuje sa nižšie v sekcii konverzácií"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Zvoní či vibruje podľa nastavení telefónu"</string>
+ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Môže zvoniť či vibrovať podľa nastavení telefónu"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Môže zvoniť alebo vibrovať podľa nastavení telefónu. Predvolene sa zobrazia konverzácie z bubliny <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nechajte systém určiť, či má toto upozornenie vydávať zvuk alebo vibrovať"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Stav:&lt;/b&gt; Preradené vyššie do kategórie Predvolené"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Otvoriť <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Ak chcete aplikáciu <xliff:g id="APPNAME">%1$s</xliff:g> pridať ako odkaz, uistite sa, že"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Aplikácia je nastavená"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Do Peňaženky bola pridaná minimálne jedna karta"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Nainštalujte si aplikáciu kamery"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Aplikácia je nastavená"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• K dispozícii je minimálne jedno zariadenie"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 9e2a4bb84e54..c084215d4ca6 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Glasovni pomočnik"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Google Denarnica"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Optični bralnik kod QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Odklenjeno"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Naprava je zaklenjena."</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Optično branje obraza"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošlji"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Obraz ni bil prepoznan."</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Uporabite prstni odtis."</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Odklepanje z obrazom ni na voljo."</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Povezava Bluetooth vzpostavljena."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Neznan odstotek napolnjenosti baterije."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezava vzpostavljena z: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Način za letalo."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"Omrežje VPN je vklopljeno."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Baterija <xliff:g id="NUMBER">%d</xliff:g> odstotkov."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Baterija je napolnjena na <xliff:g id="PERCENTAGE">%1$d</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Baterija se polni, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> odstotkov."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Baterija je napolnjena na <xliff:g id="PERCENTAGE">%d</xliff:g> – zaradi zaščite baterije je polnjenje začasno zaustavljeno."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Baterija je napolnjena na <xliff:g id="PERCENTAGE">%1$d</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> je zaradi zaščite baterije polnjenje začasno zaustavljeno."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Prikaži vsa obvestila"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter omogočen."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Zvonjenje z vibriranjem."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odklenite za uporabo"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pri pridobivanju kartic je prišlo do težave. Poskusite znova pozneje."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavitve zaklepanja zaslona"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Optični bralnik kod QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Posodabljanje"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil za Android Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način za letalo"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Naslednjega alarma ob <xliff:g id="WHEN">%1$s</xliff:g> ne boste slišali"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Odpri aplikacijo <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Če želite aplikacijo <xliff:g id="APPNAME">%1$s</xliff:g> dodati kot bližnjico, zagotovite naslednje:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Aplikacija mora biti nastavljena."</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Vsaj ena kartica mora biti dodana v Denarnico."</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Namestite fotografsko aplikacijo."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Aplikacija mora biti nastavljena."</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Na voljo mora biti vsaj ena naprava."</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 25475633c2f2..768037c281d3 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ndihma zanore"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portofoli"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Skaneri i kodeve QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Shkyçur"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Pajisja është e kyçur"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Po skanon fytyrën"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Dërgo"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Fytyra nuk mund të njihet"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Përdor më mirë gjurmën e gishtit"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"\"Shkyçja me fytyrë\" nuk ofrohet"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Pajisja është lidhur me \"bluetooth\"."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Përqindja e baterisë e panjohur."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Lidhur me <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"modaliteti i aeroplanit"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN-ja është aktive."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Bateria ka edhe <xliff:g id="NUMBER">%d</xliff:g> për qind."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Bateria ka edhe <xliff:g id="PERCENTAGE">%1$d</xliff:g> për qind, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Bateria po karikohet, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Bateria në <xliff:g id="PERCENTAGE">%d</xliff:g> për qind. Karikimi u vendos në pauzë për të mbrojtur baterinë."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Bateria në <xliff:g id="PERCENTAGE">%1$d</xliff:g> për qind. <xliff:g id="TIME">%2$s</xliff:g>. Karikimi u vendos në pauzë për të mbrojtur baterinë."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Shiko të gjitha njoftimet"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletajpi është i aktivizuar."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Zile me dridhje."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Shkyçe për ta përdorur"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pati një problem me marrjen e kartave të tua. Provo përsëri më vonë"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Cilësimet e ekranit të kyçjes"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Skaneri i kodeve QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Po përditësohet"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profili i punës"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modaliteti i aeroplanit"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nuk do ta dëgjosh alarmin e radhës në <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Hap \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Për të shtuar aplikacionin \"<xliff:g id="APPNAME">%1$s</xliff:g>\" si një shkurtore, sigurohu që"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Aplikacioni është konfiguruar"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Të paktën një kartë të jetë shtuar në \"Portofol\""</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Të instalosh një aplikacion të kamerës"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Aplikacioni është konfiguruar"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Ofrohet të paktën një pajisje"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index fb17123fc3b6..34e3a940be72 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Гласовна помоћ"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Новчаник"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Скенер QR кода"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Откључано"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Уређај је закључан"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Скенирање лица"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Пошаљи"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Лице није препознато"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Користите отисак прста"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Откључавање лицем није доступно"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth је прикључен."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Проценат напуњености батерије није познат."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Повезани сте са <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Режим рада у авиону."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN је укључен."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Батерија је на <xliff:g id="NUMBER">%d</xliff:g> посто."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Батерија је на <xliff:g id="PERCENTAGE">%1$d</xliff:g> посто, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Батерија се пуни, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> посто."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Батерија је на <xliff:g id="PERCENTAGE">%d</xliff:g> посто, пуњење је паузирано да би се заштитила батерија."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Батерија је на <xliff:g id="PERCENTAGE">%1$d</xliff:g> посто, <xliff:g id="TIME">%2$s</xliff:g>, пуњење је паузирано да би се заштитила батерија."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Погледајте сва обавештења"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter је омогућен."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Вибрација звона."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Откључај ради коришћења"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Дошло је до проблема при преузимању картица. Пробајте поново касније"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Подешавања закључаног екрана"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Скенер QR кода"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ажурира се"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Пословни профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим рада у авиону"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Нећете чути следећи аларм у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"ДДД, д. МММ"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"с:мин"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"ч:мин"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Отворите: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Да бисте додали апликацију <xliff:g id="APPNAME">%1$s</xliff:g> као пречицу, уверите се:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• да је апликација подешена"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• да је у Новчаник додата барем једна картица"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• да сте инсталирали апликацију за камеру"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• да је апликација подешена"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• да је доступан барем један уређај"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 6e74ea2bfeae..322ea0a682e8 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Röstassistent"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR-skanner"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Upplåst"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheten är låst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Registrerar ansikte"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Skicka"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Ansiktet kändes inte igen"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Använd fingeravtryck"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Ansiktslås är otillgängligt"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ansluten."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Okänd batterinivå."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ansluten till <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Flygplansläge"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN har aktiverats."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Batteri <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batterinivån är <xliff:g id="PERCENTAGE">%1$d</xliff:g> procent, <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batteriet laddas, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> procent."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batterinivån är <xliff:g id="PERCENTAGE">%d</xliff:g> procent. Laddningen har pausats för att skydda batteriet."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batterinivån är <xliff:g id="PERCENTAGE">%1$d</xliff:g> procent, <xliff:g id="TIME">%2$s</xliff:g>. Laddningen har pausats för att skydda batteriet."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Visa alla aviseringar"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter aktiverad."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibrerande ringsignal."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås upp för att använda"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Det gick inte att hämta dina kort. Försök igen senare."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Inställningar för låsskärm"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR-skanner"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Uppdaterar"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Jobbprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flygplansläge"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nästa alarm, kl. <xliff:g id="WHEN">%1$s</xliff:g>, kommer inte att höras"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h.mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk.mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Öppna <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Om du vill lägga till <xliff:g id="APPNAME">%1$s</xliff:g>-appen som en genväg ser du till att"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• appen har konfigurerats"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• minst ett kort har lagts till i Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• installera en kameraapp"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• appen har konfigurerats"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• minst en enhet är tillgänglig"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index e8a7caff02a3..5e822a0e2b1c 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Mapendekezo ya Sauti"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Pochi"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Kichanganuzi cha Msimbo wa QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Imefunguliwa"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Kifaa kimefungwa"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Inachanganua uso"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Tuma"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Imeshindwa kutambua uso"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Badala yake, tumia alama ya kidole"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Kipengele cha Kufungua kwa Uso hakipatikani"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth imeunganishwa."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Asilimia ya betri haijulikani."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Imeunganishwa kwenye <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Hali ya ndegeni."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN imewashwa."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Asilimia <xliff:g id="NUMBER">%d</xliff:g> ya betri"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Betri ina asilimia <xliff:g id="PERCENTAGE">%1$d</xliff:g>, hadi <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Betri inachaji, asilimia <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Betri ina asilimia <xliff:g id="PERCENTAGE">%d</xliff:g>, imesitisha kuchaji ili kulinda betri."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Betri ina asilimia <xliff:g id="PERCENTAGE">%1$d</xliff:g>, hadi <xliff:g id="TIME">%2$s</xliff:g>, imesitisha kuchaji ili kulinda betri."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Angalia arifa zote"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Kichapishaji cha Tele kimewezeshwa."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Mtetemo wa mlio"</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Fungua ili utumie"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Hitilafu imetokea wakati wa kuleta kadi zako, tafadhali jaribu tena baadaye"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mipangilio ya kufunga skrini"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Kichanganuzi cha msimbo wa QR"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"Wasifu wa kazini"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hali ya ndegeni"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"saa:dk"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:dk"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 868c003d99a5..3fc59e38ec6c 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -35,6 +35,11 @@
not appear immediately after user swipes to the side -->
<dimen name="qs_tiles_page_horizontal_margin">20dp</dimen>
+ <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
+ <dimen name="qs_media_rec_icon_top_margin">27dp</dimen>
+ <dimen name="qs_media_rec_album_size">152dp</dimen>
+ <dimen name="qs_media_rec_album_side_margin">16dp</dimen>
+
<dimen name="lockscreen_shade_max_over_scroll_amount">42dp</dimen>
<!-- Roughly the same distance as media on LS to media on QS. We will translate by this value
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 41e1f3f96f70..9f54b7ef36ef 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"குரல் உதவி"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR குறியீடு ஸ்கேனர்"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"அன்லாக் செய்யப்பட்டுள்ளது"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"சாதனம் பூட்டப்பட்டுள்ளது"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"முகத்தை ஸ்கேன் செய்கிறது"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"அனுப்பு"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"முகத்தை கண்டறிய இயலவில்லை"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"கைரேகையை உபயோகிக்கவும்"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"முகம் காட்டித் திறத்தல் அம்சம் கிடைக்கவில்லை"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"புளூடூத் இணைக்கப்பட்டது."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"பேட்டரி சதவீதம் தெரியவில்லை."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>க்கு இணைக்கப்பட்டது."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"விமானப் பயன்முறை."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN இயக்கத்தில் உள்ளது."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"பேட்டரி சக்தி <xliff:g id="NUMBER">%d</xliff:g> சதவிகிதம் உள்ளது."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"பேட்டரி <xliff:g id="PERCENTAGE">%1$d</xliff:g> உள்ளது (<xliff:g id="TIME">%2$s</xliff:g> வரை இயங்கும்)"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"பேட்டரி சார்ஜ் ஆகிறது, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> சதவீதம் உள்ளது."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"பேட்டரி <xliff:g id="PERCENTAGE">%d</xliff:g> உள்ளது, பேட்டரி பாதுகாப்பிற்காகச் சார்ஜிங் இடைநிறுத்தப்பட்டுள்ளது."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"பேட்டரி <xliff:g id="PERCENTAGE">%1$d</xliff:g> உள்ளது (<xliff:g id="TIME">%2$s</xliff:g> வரை இயங்கும்), பேட்டரி பாதுகாப்பிற்காகச் சார்ஜிங் இடைநிறுத்தப்பட்டுள்ளது."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"எல்லா அறிவிப்புகளையும் காட்டும்"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter இயக்கப்பட்டது."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"ரிங்கர் அதிர்வு."</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"பயன்படுத்துவதற்கு அன்லாக் செய்க"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"உங்கள் கார்டுகளின் விவரங்களைப் பெறுவதில் சிக்கல் ஏற்பட்டது, பிறகு முயலவும்"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"பூட்டுத் திரை அமைப்புகள்"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR குறியீடு ஸ்கேனர்"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"பணிக் கணக்கு"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"விமானப் பயன்முறை"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 9ad0277c6c55..fa438331c8d3 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"వాయిస్ అసిస్టెంట్"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR కోడ్ స్కానర్"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"అన్‌లాక్ చేయబడింది"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"పరికరం లాక్ చేయబడింది"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ముఖాన్ని స్కాన్ చేస్తోంది"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"పంపు"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ముఖం గుర్తించడం కుదరలేదు"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"బదులుగా వేలిముద్రను ఉపయోగించండి"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"ఫేస్ అన్‌లాక్ అందుబాటులో లేదు"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"బ్లూటూత్ కనెక్ట్ చేయబడింది."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"బ్యాటరీ శాతం తెలియదు."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"ఎయిర్‌ప్లేన్ మోడ్."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPNలో."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"బ్యాటరీ <xliff:g id="NUMBER">%d</xliff:g> శాతం."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"బ్యాటరీ <xliff:g id="PERCENTAGE">%1$d</xliff:g> శాతం, <xliff:g id="TIME">%2$s</xliff:g> ఉంటుంది"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"బ్యాటరీ ఛార్జ్ అవుతోంది, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> శాతం వద్ద ఉంది."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"బ్యాటరీ <xliff:g id="PERCENTAGE">%d</xliff:g> శాతం ఉంది, బ్యాటరీ ప్రొటెక్షన్ కోసం ఛార్జింగ్ పాజ్ చేయబడింది."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"బ్యాటరీ <xliff:g id="PERCENTAGE">%1$d</xliff:g> శాతం, <xliff:g id="TIME">%2$s</xliff:g> ఉంటుంది, బ్యాటరీ ప్రొటెక్షన్ కోసం ఛార్జింగ్ పాజ్ చేయబడింది."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"అన్ని నోటిఫికేషన్‌లను చూడండి"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"టెలిటైప్‌రైటర్ ప్రారంభించబడింది."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"రింగర్ వైబ్రేట్‌లో ఉంది."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ఉపయోగించడానికి అన్‌లాక్ చేయండి"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"మీ కార్డ్‌లను పొందడంలో సమస్య ఉంది, దయచేసి తర్వాత మళ్లీ ట్రై చేయండి"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"లాక్ స్క్రీన్ సెట్టింగ్‌లు"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR కోడ్ స్కానర్"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"అప్‌డేట్ చేస్తోంది"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ఆఫీస్ ప్రొఫైల్‌"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ఎయిర్‌ప్లేన్ మోడ్"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"మీరు <xliff:g id="WHEN">%1$s</xliff:g> సెట్ చేసిన మీ తర్వాత అలారం మీకు వినిపించదు"</string>
@@ -996,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g>‌ను తెరవండి"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> యాప్‌ను షార్ట్‌కట్‌గా జోడించడానికి, వీటిని నిర్ధారించుకోండి"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• యాప్ సెటప్ చేయబడి ఉందని"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Walletకు కనీసం ఒక కార్డ్ అయినా జోడించబడి ఉందని"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• కెమెరా యాప్ ఇన్‌స్టాల్ చేసి ఉందని"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• యాప్ సెటప్ చేయబడి ఉందని"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• కనీసం ఒక పరికరమైనా అందుబాటులో ఉందని"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"రద్దు చేయండి"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"ఇప్పుడే తిప్పండి"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"మెరుగైన సెల్ఫీ కోసం ఫోన్‌ను అన్‌ఫోల్డ్ చేయండి"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"మంచి సెల్ఫీ కోసం ముందు వైపు డిస్‌ప్లేకు తిప్పాలా?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"అధిక రిజల్యూషన్‌తో పెద్ద ఫోటో కోసం వెనుక వైపున ఉన్న కెమెరాను ఉపయోగించండి."</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ ఈ స్క్రీన్ ఆఫ్ అవుతుంది"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index f4116ad44319..17575100d330 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -168,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"ไม่รู้จักใบหน้า"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"ใช้ลายนิ้วมือแทน"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"การปลดล็อกด้วยใบหน้าไม่พร้อมใช้งาน"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"เชื่อมต่อบลูทูธแล้ว"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"ไม่ทราบเปอร์เซ็นต์แบตเตอรี่"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"เชื่อมต่อกับ <xliff:g id="BLUETOOTH">%s</xliff:g> แล้ว"</string>
@@ -180,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"โหมดบนเครื่องบิน"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN เปิดอยู่"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"แบตเตอรี่ <xliff:g id="NUMBER">%d</xliff:g> เปอร์เซ็นต์"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"แบตเตอรี่ <xliff:g id="PERCENTAGE">%1$d</xliff:g> เปอร์เซ็นต์ (<xliff:g id="TIME">%2$s</xliff:g>)"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"กำลังชาร์จแบตเตอรี่ <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> เปอร์เซ็นต์"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"แบตเตอรี่ <xliff:g id="PERCENTAGE">%d</xliff:g> เปอร์เซ็นต์ หยุดชาร์จชั่วคราวเพื่อยืดอายุแบตเตอรี่"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"แบตเตอรี่ <xliff:g id="PERCENTAGE">%1$d</xliff:g> เปอร์เซ็นต์ (<xliff:g id="TIME">%2$s</xliff:g>) หยุดชาร์จชั่วคราวเพื่อยืดอายุแบตเตอรี่"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"ดูการแจ้งเตือนทั้งหมด"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"เปิดใช้งาน TeleTypewriter อยู่"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"เสียงเรียกเข้าแบบสั่น"</string>
@@ -501,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"เกิดปัญหาในการดึงข้อมูลบัตรของคุณ โปรดลองอีกครั้งในภายหลัง"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"การตั้งค่าหน้าจอล็อก"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"เครื่องมือสแกนคิวอาร์โค้ด"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"กำลังอัปเดต"</string>
<string name="status_bar_work" msgid="5238641949837091056">"โปรไฟล์งาน"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"โหมดบนเครื่องบิน"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"คุณจะไม่ได้ยินเสียงปลุกครั้งถัดไปในเวลา <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -994,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"HH:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"เปิด <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"หากต้องการเพิ่มแอป <xliff:g id="APPNAME">%1$s</xliff:g> เป็นทางลัด โปรดตรวจสอบดังต่อไปนี้"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• แอปได้รับการตั้งค่าแล้ว"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• มีการเพิ่มบัตรลงใน Wallet อย่างน้อย 1 รายการ"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• ติดตั้งแอปกล้อง"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• แอปได้รับการตั้งค่าแล้ว"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• มีอุปกรณ์พร้อมใช้งานอย่างน้อย 1 รายการ"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ยกเลิก"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"พลิกเลย"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"กางโทรศัพท์เพื่อเซลฟีที่ดียิ่งขึ้น"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"พลิกเป็นหน้าจอด้านหน้าเพื่อภาพเซลฟีที่ดีขึ้นไหม"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"ใช้กล้องหลังเพื่อถ่ายภาพกว้างขึ้นด้วยความละเอียดสูงขึ้น"</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ หน้าจอนี้จะปิดไป"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 468c8a400de7..43768c6d0a87 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Voice Assist"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Scanner ng QR Code"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Naka-unlock"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Naka-lock ang device"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Sina-scan ang mukha"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Ipadala"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Hindi makilala ang mukha"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Gumamit ng fingerprint"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Hindi available ang Pag-unlock Gamit ang Mukha"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Nakakonekta ang Bluetooth."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Hindi alam ang porsyento ng baterya."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Nakakonekta sa <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Mode na eroplano."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"Naka-on ang VPN."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Baterya <xliff:g id="NUMBER">%d</xliff:g> (na) porsyento."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Nasa <xliff:g id="PERCENTAGE">%1$d</xliff:g> (na) porsyento ang baterya, <xliff:g id="TIME">%2$s</xliff:g>."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Nagcha-charge ang baterya, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> (na) porsyento."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Nasa <xliff:g id="PERCENTAGE">%d</xliff:g> (na) porsyento ang baterya, na-pause ang pag-charge para protektahan ang baterya."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Nasa <xliff:g id="PERCENTAGE">%1$d</xliff:g> (na) porsyento ang baterya, <xliff:g id="TIME">%2$s</xliff:g>, na-pause ang pag-charge para protektahan ang baterya."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Tingnan ang lahat ng notification"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Pinapagana ang TeleTypewriter."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Pag-vibrate ng ringer."</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"Nagkaproblema sa pagkuha ng iyong mga card, pakisubukan ulit sa ibang pagkakataon"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mga setting ng lock screen"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"Scanner ng QR code"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Ina-update"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profile sa trabaho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Hindi mo maririnig ang iyong susunod na alarm ng <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Buksan ang <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Para idagdag ang <xliff:g id="APPNAME">%1$s</xliff:g> app bilang shortcut, siguraduhing"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Na-set up ang app"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• May kahit isang card na idinagdag sa Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Mag-install ng camera app"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Na-set up ang app"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• May kahit isang device na available"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 7c126e5f1125..a140cf5fe09c 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Sesli Yardım"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Cüzdan"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR kodu tarayıcı"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Kilitli değil"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Cihaz kilitlendi"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Yüz taranıyor"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Gönder"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Yüz tanınamadı"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Bunun yerine parmak izi kullanın"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Yüz Tanıma Kilidi kullanılamıyor"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth bağlandı."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Pil yüzdesi bilinmiyor."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ile bağlı."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Uçak modu."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN açık."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Pil yüzdesi: <xliff:g id="NUMBER">%d</xliff:g>"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Pil yüzde <xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Pil şarj oluyor, yüzde <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Pil yüzde <xliff:g id="PERCENTAGE">%d</xliff:g> dolu. Şarj işlemi, pili korumak için duraklatıldı."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Pil yüzde <xliff:g id="PERCENTAGE">%1$d</xliff:g> dolu. Pilin <xliff:g id="TIME">%2$s</xliff:g> bitmemesi bekleniyor. Şarj işlemi, pili korumak için duraklatıldı."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Tüm bildirimleri göster"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter etkin."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Telefon zili titreşim."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Kullanmak için kilidi aç"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kartlarınız alınırken bir sorun oluştu. Lütfen daha sonra tekrar deneyin"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Kilit ekranı ayarları"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR kodu tarayıcı"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Güncelleniyor"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Uçak modu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> olarak ayarlanmış bir sonraki alarmınızı duymayacaksınız"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"d MMM, EEE"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:dd"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> uygulamasını aç"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> uygulamasını kısayol olarak ekleyebilmeniz için gerekenler"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Uygulama kurulmuş olmalıdır"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Cüzdan\'a en az bir kart eklenmelidir"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Kamera uygulaması yüklenmelidir"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Uygulama kurulmuş olmalıdır"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• En az bir cihaz mevcut olmalıdır"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 1c32bb1d7711..2c3f9fcc67cd 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Голосові підказки"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Гаманець"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Сканер QR-коду"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Розблоковано"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Пристрій заблоковано"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Сканування обличчя"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Надіслати"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Обличчя не розпізнано"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Скористайтеся відбитком"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Фейсконтроль недоступний"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth під’єднано."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Відсоток заряду акумулятора невідомий."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Підключено до <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Режим польоту."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"Мережу VPN увімкнено."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Заряд акумулятора у відсотках: <xliff:g id="NUMBER">%d</xliff:g>."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Заряд акумулятора: <xliff:g id="PERCENTAGE">%1$d</xliff:g>, вистачить <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Акумулятор заряджається, поточний заряд <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> відсотків."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Заряд акумулятора: <xliff:g id="PERCENTAGE">%d</xliff:g>%%. Для захисту акумулятора заряджання призупинено."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Заряд акумулятора: <xliff:g id="PERCENTAGE">%1$d</xliff:g>%%, вистачить <xliff:g id="TIME">%2$s</xliff:g>. Для захисту акумулятора заряджання призупинено."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Переглянути всі сповіщення"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Телетайп увімкнено."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Дзвінок на вібросигналі."</string>
@@ -501,7 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Розблокувати, щоб використовувати"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Не вдалось отримати ваші картки. Повторіть спробу пізніше."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Параметри блокування екрана"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Сканер QR-коду"</string>
+ <!-- no translation found for qr_code_scanner_updating_secondary_label (8344598017007876352) -->
<skip />
<string name="status_bar_work" msgid="5238641949837091056">"Робочий профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим польоту"</string>
@@ -996,4 +992,30 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <!-- no translation found for keyguard_affordance_enablement_dialog_action_template (8164857863036314664) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_message (2790910660524887941) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_1 (8439655049139819278) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_wallet_instruction_2 (4321089250629477835) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_qr_scanner_instruction (5355839079232119791) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_1 (8438311171750568633) -->
+ <skip />
+ <!-- no translation found for keyguard_affordance_enablement_dialog_home_instruction_2 (8308525385889021652) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index b78c50ec03e0..18de240d761f 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"صوتی معاون"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"والٹ"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"‏QR کوڈ اسکینر"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"غیر مقفل ہے"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"آلہ مقفل کر دیا گیا"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"اسکیننگ چہرہ"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"بھیجیں"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"چہرے کی پہچان نہیں ہو سکی"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"اس کے بجائے فنگر پرنٹ استعمال کریں"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"فیس اَنلاک غیر دستیاب ہے"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"بلوٹوتھ مربوط ہے۔"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"بیٹری کی فیصد نامعلوم ہے۔"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> سے منسلک ہیں۔"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"ہوائی جہاز وضع۔"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"‏VPN آن ہے۔"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"بیٹری <xliff:g id="NUMBER">%d</xliff:g> فیصد۔"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"بیٹری <xliff:g id="PERCENTAGE">%1$d</xliff:g> فیصد ہے، <xliff:g id="TIME">%2$s</xliff:g> تک چلے گی"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"بیٹری چارج ہو رہی ہے، اس وقت <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> فیصد ہے۔"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"بیٹری <xliff:g id="PERCENTAGE">%d</xliff:g> فیصد پے، بیٹری کے تحفظ کے لیے چارجنگ موقوف ہو گئی ہے۔"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"بیٹری <xliff:g id="PERCENTAGE">%1$d</xliff:g> فیصد پے، <xliff:g id="TIME">%2$s</xliff:g> تک چلے گی، بیٹری کے تحفظ کے لیے چارجنگ موقوف ہو گئی ہے۔"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"تمام اطلاعات دیکھیں"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"ٹیلی ٹائپ رائٹر فعال ہے۔"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"رنگر وائبریٹ۔"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"استعمال کرنے کے لیے غیر مقفل کریں"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"آپ کے کارڈز حاصل کرنے میں ایک مسئلہ درپیش تھا، براہ کرم بعد میں دوبارہ کوشش کریں"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"مقفل اسکرین کی ترتیبات"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"‏QR کوڈ اسکینر"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"اپ ڈیٹ ہو رہا ہے"</string>
<string name="status_bar_work" msgid="5238641949837091056">"دفتری پروفائل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ہوائی جہاز وضع"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"آپ کو <xliff:g id="WHEN">%1$s</xliff:g> بجے اپنا اگلا الارم سنائی نہیں دے گا"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> کھولیں"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> ایپ کو شارٹ کٹ کے طور پر شامل کرنے کے لیے درج ذیل کو یقینی بنائیں"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• ایپ سیٹ اپ ہو گئی ہے"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• والٹ میں کم از کم ایک کارڈ شامل کیا گیا ہے"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• کیمرا ایپ انسٹال کریں"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• ایپ سیٹ اپ ہو گئی ہے"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• کم از کم ایک آلہ دستیاب ہے"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 3b34ac5545dd..55f10c28294e 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Ovozli yordam"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR kod skaneri"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Qulfi ochilgan"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Qurilma qulflandi"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Yuzni skanerlash"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Yuborish"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Yuz aniqlanmadi"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Barmoq izi orqali urining"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Yuz bilan ochilmaydi."</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ulandi."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batareya quvvati foizi nomaʼlum."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ulangan: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Parvoz rejimi"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN yoniq."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Batareya <xliff:g id="NUMBER">%d</xliff:g> foiz."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batareya <xliff:g id="PERCENTAGE">%1$d</xliff:g> foiz, <xliff:g id="TIME">%2$s</xliff:g> yetadi."</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batareya quvvat olmoqda, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> foiz."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batareya <xliff:g id="PERCENTAGE">%d</xliff:g> foiz, batareya himoyasi uchun quvvatlash toʻxtatildi."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batareya <xliff:g id="PERCENTAGE">%1$d</xliff:g> foiz, <xliff:g id="TIME">%2$s</xliff:g> yetadi, batareya himoyasi uchun quvvatlash toʻxtatildi."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Barcha bildirishnomalarni ko‘rish"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter yoqildi."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibratsiyali qo‘ng‘iroq"</string>
@@ -502,6 +497,7 @@
<string name="wallet_error_generic" msgid="257704570182963611">"Bildirgilarni yuklashda xatolik yuz berdi, keyinroq qaytadan urining"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Qulflangan ekran sozlamalari"</string>
<string name="qr_code_scanner_title" msgid="1938155688725760702">"QR kod skaneri"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Yangilanmoqda"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ish profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Parvoz rejimi"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Keyingi signal (<xliff:g id="WHEN">%1$s</xliff:g>) chalinmaydi"</string>
@@ -995,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"s:dd"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Ochish: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"<xliff:g id="APPNAME">%1$s</xliff:g> ilovasini yorliq sifatida qoʻshish uchun"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Ilova sozlangan"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Kamida bitta kartochka Wallet xizmatiga qoʻshilgan"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Kamera ilovasini oʻrnating"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Ilova sozlangan"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Kamida bitta qurilma mavjud"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index ea86c1ee6044..a4d82accdcac 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Trợ lý thoại"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Ví"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Trình quét mã QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Đã mở khoá"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Đã khóa thiết bị"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Quét tìm khuôn mặt"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Gửi"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Không nhận ra khuôn mặt"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Hãy dùng vân tay"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Không dùng được tính năng Mở khoá bằng khuôn mặt"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Đã kết nối bluetooth."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Tỷ lệ phần trăm pin không xác định."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Đã kết nối với <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Chế độ trên máy bay."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN đang bật."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> phần trăm pin."</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> phần trăm pin, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Đang sạc pin, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"<xliff:g id="PERCENTAGE">%d</xliff:g> phần trăm pin, đã tạm dừng sạc để bảo vệ pin."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> phần trăm pin, <xliff:g id="TIME">%2$s</xliff:g>, đã tạm dừng sạc để bảo vệ pin."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Xem tất cả thông báo"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"Đã bật TeleTypewriter."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Chuông rung."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Mở khóa để sử dụng"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Đã xảy ra sự cố khi tải thẻ của bạn. Vui lòng thử lại sau"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Cài đặt màn hình khóa"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Trình quét mã QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Đang cập nhật"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Hồ sơ công việc"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Chế độ máy bay"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Bạn sẽ không nghe thấy báo thức tiếp theo lúc <xliff:g id="WHEN">%1$s</xliff:g> của mình"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Mở <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Để tạo lối tắt cho ứng dụng <xliff:g id="APPNAME">%1$s</xliff:g>, hãy đảm bảo"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• Ứng dụng được thiết lập"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Thêm ít nhất một thẻ vào Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Cài đặt một ứng dụng máy ảnh"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• Ứng dụng được thiết lập"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Có ít nhất một thiết bị đang hoạt động"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index ffe4f5f68f17..76c7b9a193fe 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"语音助理"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"钱包"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"二维码扫描器"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"已解锁"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"设备已锁定"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"正在扫描面孔"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"发送"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"人脸识别失败"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"改用指纹"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"无法使用人脸解锁功能"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"蓝牙已连接。"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"电池电量百分比未知。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已连接到<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"飞行模式。"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN 已开启。"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"电池电量为百分之 <xliff:g id="NUMBER">%d</xliff:g>。"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
- <string name="accessibility_battery_level_charging" msgid="8892191177774027364">"正在充电,已完成 <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%。"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"电池电量为百分之 <xliff:g id="PERCENTAGE">%1$d</xliff:g>,估计还可用 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="accessibility_battery_level_charging" msgid="8892191177774027364">"正在充电,已完成百分之 <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>。"</string>
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"电池电量为百分之 <xliff:g id="PERCENTAGE">%d</xliff:g>。为保护电池,系统已暂停充电。"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"电池电量为百分之 <xliff:g id="PERCENTAGE">%1$d</xliff:g>,估计还可用 <xliff:g id="TIME">%2$s</xliff:g>。为保护电池,系统已暂停充电。"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"查看所有通知"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"电传打字机已启用。"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"振铃器振动。"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解锁设备即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"获取您的卡片时出现问题,请稍后重试"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"锁屏设置"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"二维码扫描器"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"正在更新"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作资料"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飞行模式"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"您在<xliff:g id="WHEN">%1$s</xliff:g>将不会听到下次闹钟响铃"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"打开<xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"若要将<xliff:g id="APPNAME">%1$s</xliff:g>应用添加为快捷方式,请确保:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• 应用已设置完毕"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• 至少已将一张银行卡添加到钱包"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• 安装相机应用"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• 应用已设置完毕"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• 至少有一台设备可用"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 9a5cbd275176..a1e2194d34f9 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"語音助手"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"錢包"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR 碼掃瞄器"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"已解鎖"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"裝置已上鎖"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"掃瞄緊面孔"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"傳送"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"無法辨識面孔"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"請改用指紋"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"無法使用面孔解鎖"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"藍牙連線已建立。"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"電量百分比不明。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已連線至<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"飛航模式。"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"開咗 VPN。"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"電池電量為百分之 <xliff:g id="NUMBER">%d</xliff:g>。"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"目前電池電量為 <xliff:g id="PERCENTAGE">%1$d</xliff:g>,剩餘使用時間為 <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"正在充電:<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%。"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"目前電池電量為 <xliff:g id="PERCENTAGE">%d</xliff:g>。為保護電池,系統已暫停充電。"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"目前電池電量為 <xliff:g id="PERCENTAGE">%1$d</xliff:g>,剩餘使用時間為 <xliff:g id="TIME">%2$s</xliff:g>。為保護電池,系統已暫停充電。"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"睇所有通知"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter (TTY) 已啟用。"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"鈴聲震動。"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解鎖即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"擷取資訊卡時發生問題,請稍後再試。"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"上鎖畫面設定"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR 碼掃瞄器"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"正在更新"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作設定檔"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛行模式"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"您不會<xliff:g id="WHEN">%1$s</xliff:g>聽到鬧鐘"</string>
@@ -996,4 +991,17 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d EEE"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"開啟「<xliff:g id="APPNAME">%1$s</xliff:g>」"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"如要新增「<xliff:g id="APPNAME">%1$s</xliff:g>」應用程式為快速鍵,請確保:"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• 應用程式已完成設定"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• 已新增至少一張卡至「錢包」"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• 安裝相機應用程式"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• 應用程式已完成設定"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• 至少一部裝置可用"</string>
+ <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"取消"</string>
+ <string name="rear_display_bottom_sheet_confirm" msgid="4383356544661421206">"立即翻轉"</string>
+ <string name="rear_display_fold_bottom_sheet_title" msgid="6081542277622721548">"打開手機,即可拍攝更出色的自拍"</string>
+ <string name="rear_display_unfold_bottom_sheet_title" msgid="2137403802960396357">"要翻轉至前方螢幕拍攝更出色的自拍嗎?"</string>
+ <string name="rear_display_bottom_sheet_description" msgid="1852662982816810352">"使用後置鏡頭,拍攝更廣角、解像度更高的相片。"</string>
+ <string name="rear_display_bottom_sheet_warning" msgid="800995919558238930"><b>"✱ 此螢幕將關閉"</b></string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 9a31504399c5..6de9cba002c5 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"語音小幫手"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"錢包"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"QR code 掃描器"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"已解鎖"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"裝置已鎖定"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"掃描臉孔"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"傳送"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"無法辨識臉孔"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"請改用指紋"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"無法使用人臉解鎖功能"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"藍牙連線已建立。"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"電池電量不明。"</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已連線至<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"飛行模式。"</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN 已開啟。"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"電池電量為百分之 <xliff:g id="NUMBER">%d</xliff:g>。"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"目前電池電量為 <xliff:g id="PERCENTAGE">%1$d</xliff:g>,預估續航時間為 <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"充電中,已完成 <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%。"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"目前電池電量為 <xliff:g id="PERCENTAGE">%d</xliff:g>,系統為保護電池已暫停充電。"</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"目前電池電量為 <xliff:g id="PERCENTAGE">%1$d</xliff:g>,預估續航時間為 <xliff:g id="TIME">%2$s</xliff:g>,系統為保護電池已暫停充電。"</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"查看所有通知"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter (TTY) 已啟用。"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"鈴聲震動。"</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解鎖即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"擷取卡片時發生問題,請稍後再試"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"螢幕鎖定設定"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"QR code 掃描器"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"更新中"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作資料夾"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛航模式"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"你不會聽到下一個<xliff:g id="WHEN">%1$s</xliff:g> 的鬧鐘"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d EEE"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"開啟「<xliff:g id="APPNAME">%1$s</xliff:g>」"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"如要將「<xliff:g id="APPNAME">%1$s</xliff:g>」應用程式新增為捷徑,必須滿足以下條件"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• 完成應用程式設定"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• 錢包中至少要有一張卡片"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• 安裝相機應用程式"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• 完成應用程式設定"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• 至少要有一部可用裝置"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 36f94ae21d56..9b7a17387876 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -125,8 +125,7 @@
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Isisekeli sezwi"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"I-wallet"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Iskena sekhodi ye-QR"</string>
- <!-- no translation found for accessibility_unlock_button (3613812140816244310) -->
- <skip />
+ <string name="accessibility_unlock_button" msgid="3613812140816244310">"Ivuliwe"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Idivayisi ikhiyiwe"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Ukuskena ubuso"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Thumela"</string>
@@ -169,8 +168,7 @@
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Ayikwazi ukubona ubuso"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Kunalokho sebenzisa isigxivizo somunwe"</string>
- <!-- no translation found for keyguard_face_unlock_unavailable (1581949044193418736) -->
- <skip />
+ <string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"Ukuvula ngobuso akutholakali"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth ixhunyiwe"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Iphesenti lebhethri alaziwa."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Xhuma ku-<xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
@@ -181,13 +179,10 @@
<string name="accessibility_airplane_mode" msgid="1899529214045998505">"Imodi yendiza."</string>
<string name="accessibility_vpn_on" msgid="8037549696057288731">"I-VPN ivuliwe."</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"Iphesenti <xliff:g id="NUMBER">%d</xliff:g> lebhethri"</string>
- <!-- no translation found for accessibility_battery_level_with_estimate (6548654589315074529) -->
- <skip />
+ <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Iphesenti elingu-<xliff:g id="PERCENTAGE">%1$d</xliff:g> lebhethri, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Ibhethri liyashaja, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%"</string>
- <!-- no translation found for accessibility_battery_level_charging_paused (3560711496775146763) -->
- <skip />
- <!-- no translation found for accessibility_battery_level_charging_paused_with_estimate (2223541217743647858) -->
- <skip />
+ <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Iphesenti elingu-<xliff:g id="PERCENTAGE">%d</xliff:g>, ukushaja kumiswe okwesikhashana ukuvikela ibhethri."</string>
+ <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Iphesenti elingu-<xliff:g id="PERCENTAGE">%1$d</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>, ukushaja kumiswe okwesikhashana ukuvikela ibhethri."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"Bona zonke izaziso"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"i-TeleTypewriter inikwe amandla"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Ukudlidliza kweringa."</string>
@@ -501,8 +496,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Vula ukuze usebenzise"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kube khona inkinga yokuthola amakhadi akho, sicela uzame futhi ngemuva kwesikhathi"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Amasethingi okukhiya isikrini"</string>
- <!-- no translation found for qr_code_scanner_title (1938155688725760702) -->
- <skip />
+ <string name="qr_code_scanner_title" msgid="1938155688725760702">"Iskena sekhodi ye-QR"</string>
+ <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Iyabuyekeza"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Iphrofayela yomsebenzi"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Imodi yendiza"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ngeke uzwe i-alamu yakho elandelayo ngo-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -996,4 +991,23 @@
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
<string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
+ <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"Vula i-<xliff:g id="APPNAME">%1$s</xliff:g>"</string>
+ <string name="keyguard_affordance_enablement_dialog_message" msgid="2790910660524887941">"Ukwengeza i-app ye-<xliff:g id="APPNAME">%1$s</xliff:g> njengesinqamuleli, qinisekisa"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1" msgid="8439655049139819278">"• I-app isethiwe"</string>
+ <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2" msgid="4321089250629477835">"• Okungenani ikhadi elilodwa lengeziwe ku-Wallet"</string>
+ <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction" msgid="5355839079232119791">"• Faka i-app yekhamera"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_1" msgid="8438311171750568633">"• I-app isethiwe"</string>
+ <string name="keyguard_affordance_enablement_dialog_home_instruction_2" msgid="8308525385889021652">"• Okungenani idivayisi eyodwa iyatholakala"</string>
+ <!-- no translation found for rear_display_bottom_sheet_cancel (3461468855493357248) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_confirm (4383356544661421206) -->
+ <skip />
+ <!-- no translation found for rear_display_fold_bottom_sheet_title (6081542277622721548) -->
+ <skip />
+ <!-- no translation found for rear_display_unfold_bottom_sheet_title (2137403802960396357) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_description (1852662982816810352) -->
+ <skip />
+ <!-- no translation found for rear_display_bottom_sheet_warning (800995919558238930) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 4ce0852901ff..bc88bee41df8 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -133,6 +133,9 @@
<color name="biometric_dialog_accent">@color/material_dynamic_primary40</color>
<color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 -->
+ <!-- SFPS colors -->
+ <color name="sfps_chevron_fill">@color/material_dynamic_primary90</color>
+
<!-- UDFPS colors -->
<color name="udfps_enroll_icon">#699FF3</color>
<color name="udfps_moving_target_fill">#C2D7F7</color>
@@ -247,4 +250,8 @@
<color name="dream_overlay_clock_ambient_text_shadow_color">#4D000000</color>
<color name="dream_overlay_status_bar_key_text_shadow_color">#66000000</color>
<color name="dream_overlay_status_bar_ambient_text_shadow_color">#59000000</color>
+
+ <!-- Rear Display Education -->
+ <color name="rear_display_overlay_animation_background_color">#1E1B17</color>
+ <color name="rear_display_overlay_dialog_background_color">#1E1B17</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f223eb7edaf7..3bec72ecd2b3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -403,6 +403,8 @@
(quick_qs_offset_height (60dp) - ongoing_appops_chip_height (24dp) ) / 2 -->
<dimen name="notifications_top_padding_split_shade">18dp</dimen>
+ <dimen name="notifications_unseen_footer_icon_size">16dp</dimen>
+
<!-- Height of the status bar header bar when on Keyguard -->
<dimen name="status_bar_header_height_keyguard">40dp</dimen>
@@ -1051,6 +1053,7 @@
<dimen name="qs_media_session_collapsed_guideline">144dp</dimen>
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
+ <dimen name="qs_media_rec_icon_top_margin">16dp</dimen>
<dimen name="qs_media_rec_album_size">88dp</dimen>
<dimen name="qs_media_rec_album_side_margin">16dp</dimen>
<dimen name="qs_media_rec_album_bottom_margin">8dp</dimen>
@@ -1167,7 +1170,7 @@
<!-- Screen record dialog -->
<dimen name="screenrecord_option_padding">18dp</dimen>
- <dimen name="screenrecord_logo_size">26dp</dimen>
+ <dimen name="screenrecord_logo_size">30dp</dimen>
<dimen name="screenrecord_option_icon_size">24dp</dimen>
<!-- Screen record status bar icon -->
<dimen name="screenrecord_status_text_size">14sp</dimen>
@@ -1175,6 +1178,18 @@
<dimen name="screenrecord_status_icon_width">21dp</dimen>
<dimen name="screenrecord_status_icon_height">17.5dp</dimen>
<dimen name="screenrecord_status_icon_bg_radius">8dp</dimen>
+ <!-- Screen record spinner -->
+ <dimen name="screenrecord_spinner_height">72dp</dimen>
+ <dimen name="screenrecord_spinner_margin">24dp</dimen>
+ <dimen name="screenrecord_spinner_text_padding_start">20dp</dimen>
+ <dimen name="screenrecord_spinner_text_padding_end">80dp</dimen>
+ <dimen name="screenrecord_spinner_arrow_size">24dp</dimen>
+ <dimen name="screenrecord_spinner_background_radius">28dp</dimen>
+
+ <dimen name="screenrecord_title_margin_top">20dp</dimen>
+ <dimen name="screenrecord_warning_line_height">20dp</dimen>
+ <dimen name="screenrecord_options_padding_bottom">16dp</dimen>
+ <dimen name="screenrecord_buttons_margin_top">20dp</dimen>
<!-- Keyguard user switcher -->
<dimen name="kg_user_switcher_text_size">16sp</dimen>
@@ -1488,13 +1503,15 @@
<dimen name="dream_overlay_status_bar_extra_margin">8dp</dimen>
<!-- Dream overlay complications related dimensions -->
- <dimen name="dream_overlay_complication_clock_time_text_size">86sp</dimen>
+ <dimen name="dream_overlay_complication_clock_time_text_size">86dp</dimen>
+ <dimen name="dream_overlay_complication_clock_time_translation_y">28dp</dimen>
<dimen name="dream_overlay_complication_home_controls_padding">28dp</dimen>
<dimen name="dream_overlay_complication_clock_subtitle_text_size">24sp</dimen>
<dimen name="dream_overlay_complication_preview_text_size">36sp</dimen>
<dimen name="dream_overlay_complication_preview_icon_padding">28dp</dimen>
<dimen name="dream_overlay_complication_shadow_padding">2dp</dimen>
<dimen name="dream_overlay_complication_smartspace_padding">24dp</dimen>
+ <dimen name="dream_overlay_complication_smartspace_max_width">408dp</dimen>
<!-- The position of the end guide, which dream overlay complications can align their start with
if their end is aligned with the parent end. Represented as the percentage over from the
@@ -1592,4 +1609,10 @@
<dimen name="config_rounded_mask_size">0px</dimen>
<dimen name="config_rounded_mask_size_top">0px</dimen>
<dimen name="config_rounded_mask_size_bottom">0px</dimen>
+
+ <!-- Rear Display Education dimens -->
+ <dimen name="rear_display_animation_width">273dp</dimen>
+ <dimen name="rear_display_animation_height">200dp</dimen>
+ <dimen name="rear_display_title_top_padding">24dp</dimen>
+ <dimen name="rear_display_title_bottom_padding">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3e7b0f146b39..30788cf90e6a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -200,6 +200,8 @@
<!-- Informs the user that a screenshot is being saved. [CHAR LIMIT=50] -->
<string name="screenshot_saving_title">Saving screenshot\u2026</string>
+ <!-- Informs the user that a screenshot is being saved. [CHAR LIMIT=50] -->
+ <string name="screenshot_saving_work_profile_title">Saving screenshot to work profile\u2026</string>
<!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
<string name="screenshot_saved_title">Screenshot saved</string>
<!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
@@ -1047,6 +1049,12 @@
<!-- Text which is shown in the notification shade when there are no notifications. [CHAR LIMIT=30] -->
<string name="empty_shade_text">No notifications</string>
+ <!-- Text which is shown in the expanded notification shade when there are currently no notifications visible that the user hasn't already seen. [CHAR LIMIT=30] -->
+ <string name="no_unseen_notif_text">No new notifications</string>
+
+ <!-- Text which is shown in the locked notification shade when there are currently no notifications, but if the user were to unlock, notifications would appear. [CHAR LIMIT=40] -->
+ <string name="unlock_to_see_notif_text">Unlock to see older notifications</string>
+
<!-- Disclosure at the bottom of Quick Settings that indicates that parental controls are enabled. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_parental_controls">This device is managed by your parent</string>
@@ -2728,4 +2736,17 @@
shortcut button on the lock screen. [CHAR LIMIT=NONE].
-->
<string name="keyguard_affordance_enablement_dialog_home_instruction_2">&#8226; At least one device is available</string>
+
+ <!-- Text for education page of cancel button to hide the page. [CHAR_LIMIT=NONE] -->
+ <string name="rear_display_bottom_sheet_cancel">Cancel</string>
+ <!-- Text for the user to confirm they flipped the device around. [CHAR_LIMIT=NONE] -->
+ <string name="rear_display_bottom_sheet_confirm">Flip now</string>
+ <!-- Text for education page title to guide user to unfold phone. [CHAR_LIMIT=50] -->
+ <string name="rear_display_fold_bottom_sheet_title">Unfold phone for a better selfie</string>
+ <!-- Text for education page title to guide user to flip to the front display. [CHAR_LIMIT=50] -->
+ <string name="rear_display_unfold_bottom_sheet_title">Flip to front display for a better selfie?</string>
+ <!-- Text for education page description to suggest user to use rear selfie capture. [CHAR_LIMIT=NONE] -->
+ <string name="rear_display_bottom_sheet_description">Use the rear-facing camera for a wider photo with higher resolution.</string>
+ <!-- Text for education page description to warn user that the display will turn off if the button is clicked. [CHAR_LIMIT=NONE] -->
+ <string name="rear_display_bottom_sheet_warning"><b>&#x2731; This screen will turn off</b></string>
</resources>
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt
index 49cc48321d77..e032bb9b7e30 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ExternalViewScreenshotTestRule.kt
@@ -34,13 +34,19 @@ import platform.test.screenshot.*
/**
* A rule that allows to run a screenshot diff test on a view that is hosted in another activity.
*/
-class ExternalViewScreenshotTestRule(emulationSpec: DeviceEmulationSpec) : TestRule {
+class ExternalViewScreenshotTestRule(
+ emulationSpec: DeviceEmulationSpec,
+ assetPathRelativeToBuildRoot: String
+) : TestRule {
private val colorsRule = MaterialYouColorsRule()
private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
private val screenshotRule =
ScreenshotTestRule(
- SystemUIGoldenImagePathManager(getEmulatedDevicePathConfig(emulationSpec))
+ SystemUIGoldenImagePathManager(
+ getEmulatedDevicePathConfig(emulationSpec),
+ assetPathRelativeToBuildRoot
+ )
)
private val delegateRule =
RuleChain.outerRule(colorsRule).around(deviceEmulationRule).around(screenshotRule)
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
index fafc7744f439..72d8c5a09852 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
@@ -23,11 +23,11 @@ import platform.test.screenshot.PathConfig
/** A [GoldenImagePathManager] that should be used for all SystemUI screenshot tests. */
class SystemUIGoldenImagePathManager(
pathConfig: PathConfig,
- override val assetsPathRelativeToRepo: String = "tests/screenshot/assets"
+ assetsPathRelativeToBuildRoot: String
) :
GoldenImagePathManager(
appContext = InstrumentationRegistry.getInstrumentation().context,
- assetsPathRelativeToRepo = assetsPathRelativeToRepo,
+ assetsPathRelativeToBuildRoot = assetsPathRelativeToBuildRoot,
deviceLocalPath =
InstrumentationRegistry.getInstrumentation()
.targetContext
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
index 0b0595f4405f..738b37c9eea4 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
@@ -41,13 +41,17 @@ import platform.test.screenshot.matchers.BitmapMatcher
/** A rule for View screenshot diff unit tests. */
class ViewScreenshotTestRule(
emulationSpec: DeviceEmulationSpec,
- private val matcher: BitmapMatcher = UnitTestBitmapMatcher
+ private val matcher: BitmapMatcher = UnitTestBitmapMatcher,
+ assetsPathRelativeToBuildRoot: String
) : TestRule {
private val colorsRule = MaterialYouColorsRule()
private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
private val screenshotRule =
ScreenshotTestRule(
- SystemUIGoldenImagePathManager(getEmulatedDevicePathConfig(emulationSpec))
+ SystemUIGoldenImagePathManager(
+ getEmulatedDevicePathConfig(emulationSpec),
+ assetsPathRelativeToBuildRoot
+ )
)
private val activityRule = ActivityScenarioRule(ScreenshotActivity::class.java)
private val delegateRule =
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
index 023ef319352e..6bc8faa29adf 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
@@ -117,7 +117,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
@Override
public void onSampleCollected(float medianLuma) {
if (mSamplingEnabled) {
- updateMediaLuma(medianLuma);
+ updateMedianLuma(medianLuma);
}
}
};
@@ -260,7 +260,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
}
}
- private void updateMediaLuma(float medianLuma) {
+ private void updateMedianLuma(float medianLuma) {
mCurrentMedianLuma = medianLuma;
// If the difference between the new luma and the current luma is larger than threshold
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index f6c75a2d2752..c9ea79432360 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -39,6 +39,7 @@ public class PreviewPositionHelper {
private boolean mIsOrientationChanged;
private SplitBounds mSplitBounds;
private int mDesiredStagePosition;
+ private boolean mTaskbarInApp;
public Matrix getMatrix() {
return mMatrix;
@@ -57,6 +58,10 @@ public class PreviewPositionHelper {
mDesiredStagePosition = desiredStagePosition;
}
+ public void setTaskbarInApp(boolean taskbarInApp) {
+ mTaskbarInApp = taskbarInApp;
+ }
+
/**
* Updates the matrix based on the provided parameters
*/
@@ -83,8 +88,18 @@ public class PreviewPositionHelper {
? mSplitBounds.topTaskPercent
: (1 - (mSplitBounds.topTaskPercent + mSplitBounds.dividerHeightPercent));
// Scale portrait height to that of (actual screen - taskbar inset)
- fullscreenTaskHeight = (screenHeightPx - taskbarSize) * taskPercent;
- canvasScreenRatio = canvasHeight / fullscreenTaskHeight;
+ fullscreenTaskHeight = (screenHeightPx) * taskPercent;
+ if (mTaskbarInApp) {
+ canvasScreenRatio = canvasHeight / fullscreenTaskHeight;
+ } else {
+ if (mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ // Top app isn't cropped at all by taskbar
+ canvasScreenRatio = 0;
+ } else {
+ // Same as fullscreen ratio
+ canvasScreenRatio = (float) canvasWidth / screenWidthPx;
+ }
+ }
} else {
// For landscape, scale the width
taskPercent = mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
index 3748eba47be5..19d0a3d6bf32 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
@@ -71,7 +71,7 @@ class DoubleShadowIconDrawable(
mKeyShadowInfo.offsetY,
mKeyShadowInfo.alpha
)
- val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DARKEN)
+ val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DST_ATOP)
renderNode.setRenderEffect(blend)
return renderNode
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 93c807352521..1b0dacc327c1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -166,15 +166,14 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner
counterLauncher.cleanUp(finishTransaction);
counterWallpaper.cleanUp(finishTransaction);
// Release surface references now. This is apparently to free GPU memory
- // while doing quick operations (eg. during CTS).
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- info.getChanges().get(i).getLeash().release();
- }
+ // before GC would.
+ info.releaseAllSurfaces();
// Don't release here since launcher might still be using them. Instead
// let launcher release them (eg. via RemoteAnimationTargets)
leashMap.clear();
try {
finishCallback.onTransitionFinished(null /* wct */, finishTransaction);
+ finishTransaction.close();
} catch (RemoteException e) {
Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+ " finished callback", e);
@@ -203,10 +202,13 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner
synchronized (mFinishRunnables) {
finishRunnable = mFinishRunnables.remove(mergeTarget);
}
+ // Since we're not actually animating, release native memory now
+ t.close();
+ info.releaseAllSurfaces();
if (finishRunnable == null) return;
onAnimationCancelled(false /* isKeyguardOccluded */);
finishRunnable.run();
}
};
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index d4d3d2579b10..b7e2494ab839 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -126,15 +126,18 @@ public class RemoteTransitionCompat {
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishedCallback) {
- if (!mergeTarget.equals(mToken)) return;
- if (!mRecentsSession.merge(info, t, recents)) return;
- try {
- finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
- } catch (RemoteException e) {
- Log.e(TAG, "Error merging transition.", e);
+ if (mergeTarget.equals(mToken) && mRecentsSession.merge(info, t, recents)) {
+ try {
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error merging transition.", e);
+ }
+ // commit taskAppeared after merge transition finished.
+ mRecentsSession.commitTasksAppearedIfNeeded(recents);
+ } else {
+ t.close();
+ info.releaseAllSurfaces();
}
- // commit taskAppeared after merge transition finished.
- mRecentsSession.commitTasksAppearedIfNeeded(recents);
}
};
return new RemoteTransition(remote, appThread);
@@ -248,6 +251,8 @@ public class RemoteTransitionCompat {
}
// In this case, we are "returning" to an already running app, so just consume
// the merge and do nothing.
+ info.releaseAllSurfaces();
+ t.close();
return true;
}
final int layer = mInfo.getChanges().size() * 3;
@@ -264,6 +269,8 @@ public class RemoteTransitionCompat {
t.setLayer(targets[i].leash, layer);
}
t.apply();
+ // not using the incoming anim-only surfaces
+ info.releaseAnimSurfaces();
mAppearedTargets = targets;
return true;
}
@@ -380,9 +387,7 @@ public class RemoteTransitionCompat {
}
// Only release the non-local created surface references. The animator is responsible
// for releasing the leashes created by local.
- for (int i = 0; i < mInfo.getChanges().size(); ++i) {
- mInfo.getChanges().get(i).getLeash().release();
- }
+ mInfo.releaseAllSurfaces();
// Reset all members.
mWrapped = null;
mFinishCB = null;
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index 458d22efd206..a25b281f807c 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -25,7 +25,6 @@ import android.widget.Button;
import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.widget.LockPatternUtils;
-import com.android.settingslib.Utils;
/**
* This class implements a smart emergency button that updates itself based
@@ -91,17 +90,6 @@ public class EmergencyButton extends Button {
return super.onTouchEvent(event);
}
- /**
- * Reload colors from resources.
- **/
- public void reloadColors() {
- int color = Utils.getColorAttrDefaultColor(getContext(),
- com.android.internal.R.attr.textColorOnAccent);
- setTextColor(color);
- setBackground(getContext()
- .getDrawable(com.android.systemui.R.drawable.kg_emergency_button_background));
- }
-
@Override
public boolean performLongClick() {
return super.performLongClick();
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index c5190e828e35..ea808eb19b90 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -135,7 +135,7 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
}
mActivityTaskManager.stopSystemLockTaskMode();
- mShadeController.collapsePanel(false);
+ mShadeController.collapseShade(false);
if (mTelecomManager != null && mTelecomManager.isInCall()) {
mTelecomManager.showInCallScreen(false);
if (mEmergencyButtonCallback != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 3e32cf5521ff..860c8e3a9f77 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -20,7 +20,6 @@ import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
-import android.annotation.CallSuper;
import android.content.res.ColorStateList;
import android.os.AsyncTask;
import android.os.CountDownTimer;
@@ -117,13 +116,6 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
}
}
- @CallSuper
- @Override
- public void reloadColors() {
- super.reloadColors();
- mMessageAreaController.reloadColors();
- }
-
@Override
public boolean needsInput() {
return false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index faaba63938bf..2e9ad5868eba 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -16,7 +16,6 @@
package com.android.keyguard;
-import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -142,16 +141,6 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
public void showMessage(CharSequence message, ColorStateList colorState) {
}
- /**
- * Reload colors from resources.
- **/
- @CallSuper
- public void reloadColors() {
- if (mEmergencyButton != null) {
- mEmergencyButton.reloadColors();
- }
- }
-
public void startAppearAnimation() {
if (TextUtils.isEmpty(mMessageAreaController.getMessage())) {
mMessageAreaController.setMessage(getInitialMessageResId());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 819768544b0c..e6283b86283b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -26,7 +26,6 @@ data class KeyguardFingerprintListenModel(
val credentialAttempted: Boolean,
val deviceInteractive: Boolean,
val dreaming: Boolean,
- val encryptedOrLockdown: Boolean,
val fingerprintDisabled: Boolean,
val fingerprintLockedOut: Boolean,
val goingToSleep: Boolean,
@@ -37,6 +36,7 @@ data class KeyguardFingerprintListenModel(
val primaryUser: Boolean,
val shouldListenSfpsState: Boolean,
val shouldListenForFingerprintAssistant: Boolean,
+ val strongerAuthRequired: Boolean,
val switchingUser: Boolean,
val udfps: Boolean,
val userDoesNotHaveTrust: Boolean
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index c29f632b88d3..6a9216218d07 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -116,13 +116,6 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
return mView.getText();
}
- /**
- * Reload colors from resources.
- **/
- public void reloadColors() {
- mView.reloadColor();
- }
-
/** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */
public static class Factory {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 0025986c0e5c..195e8f92754d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -16,7 +16,6 @@
package com.android.keyguard;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.os.UserHandle;
import android.text.Editable;
@@ -39,7 +38,6 @@ import android.widget.TextView.OnEditorActionListener;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
@@ -95,18 +93,6 @@ public class KeyguardPasswordViewController
}
};
- @Override
- public void reloadColors() {
- super.reloadColors();
- int textColor = Utils.getColorAttr(mView.getContext(),
- android.R.attr.textColorPrimary).getDefaultColor();
- mPasswordEntry.setTextColor(textColor);
- mPasswordEntry.setHighlightColor(textColor);
- mPasswordEntry.setBackgroundTintList(ColorStateList.valueOf(textColor));
- mPasswordEntry.setForegroundTintList(ColorStateList.valueOf(textColor));
- mSwitchImeButton.setImageTintList(ColorStateList.valueOf(textColor));
- }
-
protected KeyguardPasswordViewController(KeyguardPasswordView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecurityMode securityMode,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index cdbfb2492e27..571d2740773d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -35,7 +35,6 @@ import com.android.internal.widget.LockPatternView.Cell;
import com.android.internal.widget.LockscreenCredential;
import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
@@ -272,16 +271,6 @@ public class KeyguardPatternViewController
}
@Override
- public void reloadColors() {
- super.reloadColors();
- mMessageAreaController.reloadColors();
- int textColor = Utils.getColorAttr(mLockPatternView.getContext(),
- android.R.attr.textColorSecondary).getDefaultColor();
- int errorColor = Utils.getColorError(mLockPatternView.getContext()).getDefaultColor();
- mLockPatternView.setColors(textColor, textColor, errorColor);
- }
-
- @Override
public void onPause() {
super.onPause();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 7876f071fdf5..f51ac325c9c1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -70,12 +70,6 @@ public class KeyguardPinViewController
}
@Override
- public void reloadColors() {
- super.reloadColors();
- mView.reloadColors();
- }
-
- @Override
public boolean startDisappearAnimation(Runnable finishRunnable) {
return mView.startDisappearAnimation(
mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 01be33e1e156..a72a484fb6f1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -363,16 +363,18 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
final boolean sfpsEnabled = getResources().getBoolean(
R.bool.config_show_sidefps_hint_on_bouncer);
final boolean fpsDetectionRunning = mUpdateMonitor.isFingerprintDetectionRunning();
- final boolean needsStrongAuth = mUpdateMonitor.userNeedsStrongAuth();
+ final boolean isUnlockingWithFpAllowed =
+ mUpdateMonitor.isUnlockingWithFingerprintAllowed();
- boolean toShow = mBouncerVisible && sfpsEnabled && fpsDetectionRunning && !needsStrongAuth;
+ boolean toShow = mBouncerVisible && sfpsEnabled && fpsDetectionRunning
+ && isUnlockingWithFpAllowed;
if (DEBUG) {
Log.d(TAG, "sideFpsToShow=" + toShow + ", "
+ "mBouncerVisible=" + mBouncerVisible + ", "
+ "configEnabled=" + sfpsEnabled + ", "
+ "fpsDetectionRunning=" + fpsDetectionRunning + ", "
- + "needsStrongAuth=" + needsStrongAuth);
+ + "isUnlockingWithFpAllowed=" + isUnlockingWithFpAllowed);
}
if (toShow) {
mSideFpsController.get().show(SideFpsUiRequestSource.PRIMARY_BOUNCER);
@@ -728,16 +730,20 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
private void reloadColors() {
- mSecurityViewFlipperController.reloadColors();
+ resetViewFlipper();
mView.reloadColors();
}
/** Handles density or font scale changes. */
private void onDensityOrFontScaleChanged() {
- mSecurityViewFlipperController.onDensityOrFontScaleChanged();
+ resetViewFlipper();
+ mView.onDensityOrFontScaleChanged();
+ }
+
+ private void resetViewFlipper() {
+ mSecurityViewFlipperController.clearViews();
mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
mKeyguardSecurityCallback);
- mView.onDensityOrFontScaleChanged();
}
static class Factory {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 25afe11ac536..a5c8c7881e3b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -74,17 +74,8 @@ public class KeyguardSecurityViewFlipperController
}
}
- /**
- * Reload colors of ui elements upon theme change.
- */
- public void reloadColors() {
- for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
- child.reloadColors();
- }
- }
-
/** Handles density or font scale changes. */
- public void onDensityOrFontScaleChanged() {
+ public void clearViews() {
mView.removeAllViews();
mChildren.clear();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 76f7d785071d..db4447399e13 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -133,12 +133,6 @@ public class KeyguardSimPinViewController
}
@Override
- public void reloadColors() {
- super.reloadColors();
- mView.reloadColors();
- }
-
- @Override
protected void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 5995e859c786..e9405eb79901 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -121,12 +121,6 @@ public class KeyguardSimPukViewController
}
@Override
- public void reloadColors() {
- super.reloadColors();
- mView.reloadColors();
- }
-
- @Override
protected void verifyPasswordAndUnlock() {
mStateMachine.next();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 331497e4bf85..90ed293c73c9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -27,6 +27,8 @@ import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_P
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED;
import static android.hardware.biometrics.BiometricConstants.LockoutMode;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
+import static android.hardware.biometrics.BiometricSourceType.FACE;
+import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
@@ -229,7 +231,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* Biometric authentication: Cancelling and waiting for the relevant biometric service to
* send us the confirmation that cancellation has happened.
*/
- private static final int BIOMETRIC_STATE_CANCELLING = 2;
+ @VisibleForTesting
+ protected static final int BIOMETRIC_STATE_CANCELLING = 2;
+
+ /**
+ * Biometric state: During cancelling we got another request to start listening, so when we
+ * receive the cancellation done signal, we should start listening again.
+ */
+ @VisibleForTesting
+ protected static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
/**
* Action indicating keyguard *can* start biometric authentiation.
@@ -244,12 +254,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
*/
private static final int BIOMETRIC_ACTION_UPDATE = 2;
- /**
- * Biometric state: During cancelling we got another request to start listening, so when we
- * receive the cancellation done signal, we should start listening again.
- */
- private static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
-
@VisibleForTesting
public static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1;
public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;
@@ -372,7 +376,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private KeyguardBypassController mKeyguardBypassController;
private List<SubscriptionInfo> mSubscriptionInfo;
- private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ @VisibleForTesting
+ protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
private boolean mIsDreaming;
private boolean mLogoutEnabled;
@@ -746,8 +751,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
*/
public void requestFaceAuthOnOccludingApp(boolean request) {
mOccludingAppRequestingFace = request;
- updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
- FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED);
+ int action = mOccludingAppRequestingFace ? BIOMETRIC_ACTION_UPDATE : BIOMETRIC_ACTION_STOP;
+ updateFaceListeningState(action, FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED);
}
/**
@@ -806,7 +811,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
new BiometricAuthenticated(true, isStrongBiometric));
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
- mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT);
+ mTrustManager.unlockedByBiometricForUser(userId, FINGERPRINT);
}
// Don't send cancel if authentication succeeds
mFingerprintCancelSignal = null;
@@ -816,7 +821,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT,
+ cb.onBiometricAuthenticated(userId, FINGERPRINT,
isStrongBiometric);
}
}
@@ -849,7 +854,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ cb.onBiometricAuthFailed(FINGERPRINT);
}
}
if (isUdfpsSupported()) {
@@ -874,7 +879,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAcquired(BiometricSourceType.FINGERPRINT, acquireInfo);
+ cb.onBiometricAcquired(FINGERPRINT, acquireInfo);
}
}
}
@@ -908,7 +913,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FINGERPRINT);
+ cb.onBiometricHelp(msgId, helpString, FINGERPRINT);
}
}
}
@@ -950,17 +955,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
- if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE
- || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED) {
- mLogger.logRetryAfterFpError(msgId, errString);
+ if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
+ mLogger.logRetryAfterFpErrorWithDelay(msgId, errString, HAL_ERROR_RETRY_TIMEOUT);
mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
}
+ if (msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED) {
+ mLogger.logRetryAfterFpErrorWithDelay(msgId, errString, 0);
+ updateFingerprintListeningState(BIOMETRIC_ACTION_START);
+ }
+
boolean lockedOutStateChanged = false;
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
lockedOutStateChanged = !mFingerprintLockedOutPermanent;
mFingerprintLockedOutPermanent = true;
- mLogger.d("Fingerprint locked out - requiring strong auth");
+ mLogger.d("Fingerprint permanently locked out - requiring stronger auth");
mLockPatternUtils.requireStrongAuth(
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, getCurrentUser());
}
@@ -969,6 +978,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
lockedOutStateChanged |= !mFingerprintLockedOut;
mFingerprintLockedOut = true;
+ mLogger.d("Fingerprint temporarily locked out - requiring stronger auth");
if (isUdfpsEnrolled()) {
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
@@ -979,12 +989,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT);
+ cb.onBiometricError(msgId, errString, FINGERPRINT);
}
}
if (lockedOutStateChanged) {
- notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
+ notifyLockedOutStateChanged(FINGERPRINT);
}
}
@@ -1012,7 +1022,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
if (changed) {
- notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
+ notifyLockedOutStateChanged(FINGERPRINT);
}
}
@@ -1035,7 +1045,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricRunningStateChanged(isFingerprintDetectionRunning(),
- BiometricSourceType.FINGERPRINT);
+ FINGERPRINT);
}
}
}
@@ -1048,7 +1058,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
new BiometricAuthenticated(true, isStrongBiometric));
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
- mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE);
+ mTrustManager.unlockedByBiometricForUser(userId, FACE);
}
// Don't send cancel if authentication succeeds
mFaceCancelSignal = null;
@@ -1059,7 +1069,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricAuthenticated(userId,
- BiometricSourceType.FACE,
+ FACE,
isStrongBiometric);
}
}
@@ -1081,7 +1091,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAuthFailed(BiometricSourceType.FACE);
+ cb.onBiometricAuthFailed(FACE);
}
}
handleFaceHelp(BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
@@ -1094,7 +1104,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAcquired(BiometricSourceType.FACE, acquireInfo);
+ cb.onBiometricAcquired(FACE, acquireInfo);
}
}
}
@@ -1129,7 +1139,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FACE);
+ cb.onBiometricHelp(msgId, helpString, FACE);
}
}
}
@@ -1197,12 +1207,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricError(msgId, errString,
- BiometricSourceType.FACE);
+ FACE);
}
}
if (lockedOutStateChanged) {
- notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ notifyLockedOutStateChanged(FACE);
}
}
@@ -1216,7 +1226,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
FACE_AUTH_TRIGGERED_FACE_LOCKOUT_RESET), getBiometricLockoutDelay());
if (changed) {
- notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ notifyLockedOutStateChanged(FACE);
}
}
@@ -1239,7 +1249,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricRunningStateChanged(isFaceDetectionRunning(),
- BiometricSourceType.FACE);
+ FACE);
}
}
}
@@ -1380,7 +1390,35 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
- return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric);
+ // StrongAuthTracker#isUnlockingWithBiometricAllowed includes
+ // STRONG_AUTH_REQUIRED_AFTER_LOCKOUT which is the same as mFingerprintLockedOutPermanent;
+ // however the strong auth tracker does not include the temporary lockout
+ // mFingerprintLockedOut.
+ return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric)
+ && !mFingerprintLockedOut;
+ }
+
+ /**
+ * Whether fingerprint is allowed ot be used for unlocking based on the strongAuthTracker
+ * and temporary lockout state (tracked by FingerprintManager via error codes).
+ */
+ public boolean isUnlockingWithFingerprintAllowed() {
+ return isUnlockingWithBiometricAllowed(FINGERPRINT);
+ }
+
+ /**
+ * Whether the given biometric is allowed based on strongAuth & lockout states.
+ */
+ public boolean isUnlockingWithBiometricAllowed(
+ @NonNull BiometricSourceType biometricSourceType) {
+ switch (biometricSourceType) {
+ case FINGERPRINT:
+ return isUnlockingWithBiometricAllowed(true);
+ case FACE:
+ return isUnlockingWithBiometricAllowed(false);
+ default:
+ return false;
+ }
}
public boolean isUserInLockdown(int userId) {
@@ -1402,11 +1440,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return isEncrypted || isLockDown;
}
- public boolean userNeedsStrongAuth() {
- return mStrongAuthTracker.getStrongAuthForUser(getCurrentUser())
- != LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
- }
-
private boolean containsFlag(int haystack, int needle) {
return (haystack & needle) != 0;
}
@@ -1576,12 +1609,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
};
- private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback
- = (sensorId, userId, isStrongBiometric) -> {
- // Trigger the fingerprint success path so the bouncer can be shown
- handleFingerprintAuthenticated(userId, isStrongBiometric);
- };
-
/**
* Propagates a pointer down event to keyguard.
*/
@@ -2651,27 +2678,25 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& (!mKeyguardGoingAway || !mDeviceInteractive)
&& mIsPrimaryUser
&& biometricEnabledForUser;
-
- final boolean shouldListenBouncerState = !(mFingerprintLockedOut
- && mPrimaryBouncerIsOrWillBeShowing && mCredentialAttempted);
-
- final boolean isEncryptedOrLockdownForUser = isEncryptedOrLockdown(user);
+ final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
+ final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled();
+ final boolean shouldListenBouncerState =
+ !strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing;
final boolean shouldListenUdfpsState = !isUdfps
|| (!userCanSkipBouncer
- && !isEncryptedOrLockdownForUser
+ && !strongerAuthRequired
&& userDoesNotHaveTrust);
boolean shouldListenSideFpsState = true;
- if (isSfpsSupported() && isSfpsEnrolled()) {
+ if (isSideFps) {
shouldListenSideFpsState =
mSfpsRequireScreenOnToAuthPrefEnabled ? isDeviceInteractive() : true;
}
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
- && shouldListenBouncerState && shouldListenUdfpsState && !isFingerprintLockedOut()
+ && shouldListenBouncerState && shouldListenUdfpsState
&& shouldListenSideFpsState;
-
maybeLogListenerModelData(
new KeyguardFingerprintListenModel(
System.currentTimeMillis(),
@@ -2683,7 +2708,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mCredentialAttempted,
mDeviceInteractive,
mIsDreaming,
- isEncryptedOrLockdownForUser,
fingerprintDisabledForUser,
mFingerprintLockedOut,
mGoingToSleep,
@@ -2694,6 +2718,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mIsPrimaryUser,
shouldListenSideFpsState,
shouldListenForFingerprintAssistant,
+ strongerAuthRequired,
mSwitchingUser,
isUdfps,
userDoesNotHaveTrust));
@@ -2721,10 +2746,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean isEncryptedOrTimedOut =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
-
- // TODO: always disallow when fp is already locked out?
- final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
-
+ final boolean fpLockedOut = isFingerprintLockedOut();
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
// There's no reason to ask the HAL for authentication when the user can dismiss the
@@ -2846,15 +2868,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// Waiting for restart via handleFingerprintError().
return;
}
- mLogger.v("startListeningForFingerprint()");
if (unlockPossible) {
mFingerprintCancelSignal = new CancellationSignal();
- if (isEncryptedOrLockdown(userId)) {
- mFpm.detectFingerprint(mFingerprintCancelSignal, mFingerprintDetectionCallback,
+ if (!isUnlockingWithFingerprintAllowed()) {
+ mLogger.v("startListeningForFingerprint - detect");
+ mFpm.detectFingerprint(
+ mFingerprintCancelSignal,
+ (sensorId, user, isStrongBiometric) -> {
+ mLogger.d("fingerprint detected");
+ // Trigger the fingerprint success path so the bouncer can be shown
+ handleFingerprintAuthenticated(user, isStrongBiometric);
+ },
userId);
} else {
+ mLogger.v("startListeningForFingerprint - authenticate");
mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
mFingerprintAuthenticationCallback, null /* handler */,
FingerprintManager.SENSOR_ID_ANY, userId, 0 /* flags */);
@@ -3071,11 +3100,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
+ // Immediately stop previous biometric listening states.
+ // Resetting lockout states updates the biometric listening states.
if (mFaceManager != null && !mFaceSensorProperties.isEmpty()) {
+ stopListeningForFace(FACE_AUTH_UPDATED_USER_SWITCHING);
handleFaceLockoutReset(mFaceManager.getLockoutModeForUser(
mFaceSensorProperties.get(0).sensorId, userId));
}
if (mFpm != null && !mFingerprintSensorProperties.isEmpty()) {
+ stopListeningForFingerprint();
handleFingerprintLockoutReset(mFpm.getLockoutModeForUser(
mFingerprintSensorProperties.get(0).sensorId, userId));
}
@@ -3482,7 +3515,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@AnyThread
public void setSwitchingUser(boolean switching) {
mSwitchingUser = switching;
- // Since this comes in on a binder thread, we need to post if first
+ // Since this comes in on a binder thread, we need to post it first
mHandler.post(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_USER_SWITCHING));
}
@@ -3574,8 +3607,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
Assert.isMainThread();
mUserFingerprintAuthenticated.clear();
mUserFaceAuthenticated.clear();
- mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT, unlockedUser);
- mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, unlockedUser);
+ mTrustManager.clearAllBiometricRecognized(FINGERPRINT, unlockedUser);
+ mTrustManager.clearAllBiometricRecognized(FACE, unlockedUser);
mLogger.d("clearBiometricRecognized");
for (int i = 0; i < mCallbacks.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 1f6441a47092..b66ae286171a 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -225,12 +225,13 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
{ "Retrying face after HW unavailable, attempt $int1" })
}
- fun logRetryAfterFpError(msgId: Int, errString: String?) {
+ fun logRetryAfterFpErrorWithDelay(msgId: Int, errString: String?, delay: Int) {
logBuffer.log(TAG, DEBUG, {
int1 = msgId
+ int2 = delay
str1 = "$errString"
}, {
- "Fingerprint retrying auth due to($int1) -> $str1"
+ "Fingerprint retrying auth after $int2 ms due to($int1) -> $str1"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index 929ebea37eef..becf5b39e9df 100644
--- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -39,10 +39,13 @@ import java.io.PrintWriter;
*/
public interface CoreStartable extends Dumpable {
- /** Main entry point for implementations. Called shortly after app startup. */
+ /** Main entry point for implementations. Called shortly after SysUI startup. */
void start();
- /** */
+ /** Called when the device configuration changes. This will not be called before
+ * {@link #start()}, but it could be called before {@link #onBootCompleted()}.
+ *
+ * @see android.app.Application#onConfigurationChanged(Configuration) */
default void onConfigurationChanged(Configuration newConfig) {
}
@@ -50,7 +53,11 @@ public interface CoreStartable extends Dumpable {
default void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
}
- /** Called when the device reports BOOT_COMPLETED. */
+ /** Called immediately after the system broadcasts
+ * {@link android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED} or during SysUI startup if the
+ * property {@code sys.boot_completed} is already set to 1. The latter typically occurs when
+ * starting a new SysUI instance, such as when starting SysUI for a secondary user.
+ * {@link #onBootCompleted()} will never be called before {@link #start()}. */
default void onBootCompleted() {
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 76a7cad15419..8ae63c4e2f54 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -17,24 +17,25 @@
package com.android.systemui;
import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.os.UserHandle;
-import android.util.Log;
+
+import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.settings.SecureSettings;
+import java.util.concurrent.Executor;
+
import javax.inject.Inject;
import dagger.assisted.Assisted;
@@ -44,31 +45,66 @@ import dagger.assisted.AssistedInject;
/**
* Manages notification when a guest session is resumed.
*/
-public class GuestResumeSessionReceiver extends BroadcastReceiver {
-
- private static final String TAG = GuestResumeSessionReceiver.class.getSimpleName();
+public class GuestResumeSessionReceiver {
@VisibleForTesting
public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
@VisibleForTesting
public AlertDialog mNewSessionDialog;
+ private final Executor mMainExecutor;
private final UserTracker mUserTracker;
private final SecureSettings mSecureSettings;
- private final BroadcastDispatcher mBroadcastDispatcher;
private final ResetSessionDialog.Factory mResetSessionDialogFactory;
private final GuestSessionNotification mGuestSessionNotification;
+ @VisibleForTesting
+ public final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ cancelDialog();
+
+ UserInfo currentUser = mUserTracker.getUserInfo();
+ if (!currentUser.isGuest()) {
+ return;
+ }
+
+ int guestLoginState = mSecureSettings.getIntForUser(
+ SETTING_GUEST_HAS_LOGGED_IN, 0, newUser);
+
+ if (guestLoginState == 0) {
+ // set 1 to indicate, 1st login
+ guestLoginState = 1;
+ mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState,
+ newUser);
+ } else if (guestLoginState == 1) {
+ // set 2 to indicate, 2nd or later login
+ guestLoginState = 2;
+ mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState,
+ newUser);
+ }
+
+ mGuestSessionNotification.createPersistentNotification(currentUser,
+ (guestLoginState <= 1));
+
+ if (guestLoginState > 1) {
+ mNewSessionDialog = mResetSessionDialogFactory.create(newUser);
+ mNewSessionDialog.show();
+ }
+ }
+ };
+
@Inject
public GuestResumeSessionReceiver(
+ @Main Executor mainExecutor,
UserTracker userTracker,
SecureSettings secureSettings,
- BroadcastDispatcher broadcastDispatcher,
GuestSessionNotification guestSessionNotification,
ResetSessionDialog.Factory resetSessionDialogFactory) {
+ mMainExecutor = mainExecutor;
mUserTracker = userTracker;
mSecureSettings = secureSettings;
- mBroadcastDispatcher = broadcastDispatcher;
mGuestSessionNotification = guestSessionNotification;
mResetSessionDialogFactory = resetSessionDialogFactory;
}
@@ -77,49 +113,7 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
* Register this receiver with the {@link BroadcastDispatcher}
*/
public void register() {
- IntentFilter f = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
-
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- cancelDialog();
-
- int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- if (userId == UserHandle.USER_NULL) {
- Log.e(TAG, intent + " sent to " + TAG + " without EXTRA_USER_HANDLE");
- return;
- }
-
- UserInfo currentUser = mUserTracker.getUserInfo();
- if (!currentUser.isGuest()) {
- return;
- }
-
- int guestLoginState = mSecureSettings.getIntForUser(
- SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
-
- if (guestLoginState == 0) {
- // set 1 to indicate, 1st login
- guestLoginState = 1;
- mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId);
- } else if (guestLoginState == 1) {
- // set 2 to indicate, 2nd or later login
- guestLoginState = 2;
- mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId);
- }
-
- mGuestSessionNotification.createPersistentNotification(currentUser,
- (guestLoginState <= 1));
-
- if (guestLoginState > 1) {
- mNewSessionDialog = mResetSessionDialogFactory.create(userId);
- mNewSessionDialog.show();
- }
- }
+ mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
}
private void cancelDialog() {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 7e3b1389792c..02a6d7be7143 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -26,10 +26,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import android.annotation.IdRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -45,7 +42,6 @@ import android.hardware.graphics.common.DisplayDecorationSupport;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.Trace;
-import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.util.DisplayUtils;
import android.util.Log;
@@ -68,7 +64,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.settingslib.Utils;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.decor.CutoutDecorProviderFactory;
@@ -128,7 +123,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
private DisplayManager mDisplayManager;
@VisibleForTesting
protected boolean mIsRegistered;
- private final BroadcastDispatcher mBroadcastDispatcher;
private final Context mContext;
private final Executor mMainExecutor;
private final TunerService mTunerService;
@@ -302,7 +296,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
public ScreenDecorations(Context context,
@Main Executor mainExecutor,
SecureSettings secureSettings,
- BroadcastDispatcher broadcastDispatcher,
TunerService tunerService,
UserTracker userTracker,
PrivacyDotViewController dotViewController,
@@ -312,7 +305,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
mContext = context;
mMainExecutor = mainExecutor;
mSecureSettings = secureSettings;
- mBroadcastDispatcher = broadcastDispatcher;
mTunerService = tunerService;
mUserTracker = userTracker;
mDotViewController = dotViewController;
@@ -598,10 +590,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
mColorInversionSetting.onChange(false);
updateColorInversion(mColorInversionSetting.getValue());
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(mUserSwitchIntentReceiver, filter,
- mExecutor, UserHandle.ALL);
+ mUserTracker.addCallback(mUserChangedCallback, mExecutor);
mIsRegistered = true;
} else {
mMainExecutor.execute(() -> mTunerService.removeTunable(this));
@@ -610,7 +599,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
mColorInversionSetting.setListening(false);
}
- mBroadcastDispatcher.unregisterReceiver(mUserSwitchIntentReceiver);
+ mUserTracker.removeCallback(mUserChangedCallback);
mIsRegistered = false;
}
}
@@ -897,18 +886,18 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
}
}
- private final BroadcastReceiver mUserSwitchIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- int newUserId = mUserTracker.getUserId();
- if (DEBUG) {
- Log.d(TAG, "UserSwitched newUserId=" + newUserId);
- }
- // update color inversion setting to the new user
- mColorInversionSetting.setUserId(newUserId);
- updateColorInversion(mColorInversionSetting.getValue());
- }
- };
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ if (DEBUG) {
+ Log.d(TAG, "UserSwitched newUserId=" + newUser);
+ }
+ // update color inversion setting to the new user
+ mColorInversionSetting.setUserId(newUser);
+ updateColorInversion(mColorInversionSetting.getValue());
+ }
+ };
private void updateColorInversion(int colorsInvertedValue) {
mTintColor = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 998288a0a2c0..dab73e9bf289 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -52,6 +52,7 @@ import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.recents.Recents;
+import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -183,15 +184,18 @@ public class SystemActions implements CoreStartable {
private final AccessibilityManager mA11yManager;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final NotificationShadeWindowController mNotificationShadeController;
+ private final ShadeController mShadeController;
private final StatusBarWindowCallback mNotificationShadeCallback;
private boolean mDismissNotificationShadeActionRegistered;
@Inject
public SystemActions(Context context,
NotificationShadeWindowController notificationShadeController,
+ ShadeController shadeController,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Optional<Recents> recentsOptional) {
mContext = context;
+ mShadeController = shadeController;
mRecentsOptional = recentsOptional;
mReceiver = new SystemActionsBroadcastReceiver();
mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
@@ -529,9 +533,7 @@ public class SystemActions implements CoreStartable {
}
private void handleAccessibilityDismissNotificationShade() {
- mCentralSurfacesOptionalLazy.get().ifPresent(
- centralSurfaces -> centralSurfaces.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_NONE, false /* force */));
+ mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE);
}
private void handleDpadUp() {
diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
index 5616a00592f2..621b99d6804a 100644
--- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
@@ -29,13 +29,15 @@ import android.os.UserHandle
import android.util.Log
import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper
import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper
+import com.android.systemui.keyguard.domain.backup.KeyguardQuickAffordanceBackupHelper
import com.android.systemui.people.widget.PeopleBackupHelper
/**
* Helper for backing up elements in SystemUI
*
- * This helper is invoked by BackupManager whenever a backup or restore is required in SystemUI.
- * The helper can be used to back up any element that is stored in [Context.getFilesDir].
+ * This helper is invoked by BackupManager whenever a backup or restore is required in SystemUI. The
+ * helper can be used to back up any element that is stored in [Context.getFilesDir] or
+ * [Context.getSharedPreferences].
*
* After restoring is done, a [ACTION_RESTORE_FINISHED] intent will be send to SystemUI user 0,
* indicating that restoring is finished for a given user.
@@ -47,9 +49,11 @@ open class BackupHelper : BackupAgentHelper() {
internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME
private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite"
private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences"
+ private const val KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY =
+ "systemui.keyguard.quickaffordance.shared_preferences"
val controlsDataLock = Any()
const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED"
- private const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
+ const val PERMISSION_SELF = "com.android.systemui.permission.SELF"
}
override fun onCreate(userHandle: UserHandle, operationType: Int) {
@@ -67,17 +71,27 @@ open class BackupHelper : BackupAgentHelper() {
}
val keys = PeopleBackupHelper.getFilesToBackup()
- addHelper(PEOPLE_TILES_BACKUP_KEY, PeopleBackupHelper(
- this, userHandle, keys.toTypedArray()))
+ addHelper(
+ PEOPLE_TILES_BACKUP_KEY,
+ PeopleBackupHelper(this, userHandle, keys.toTypedArray())
+ )
+ addHelper(
+ KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY,
+ KeyguardQuickAffordanceBackupHelper(
+ context = this,
+ userId = userHandle.identifier,
+ ),
+ )
}
override fun onRestoreFinished() {
super.onRestoreFinished()
- val intent = Intent(ACTION_RESTORE_FINISHED).apply {
- `package` = packageName
- putExtra(Intent.EXTRA_USER_ID, userId)
- flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY
- }
+ val intent =
+ Intent(ACTION_RESTORE_FINISHED).apply {
+ `package` = packageName
+ putExtra(Intent.EXTRA_USER_ID, userId)
+ flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ }
sendBroadcastAsUser(intent, UserHandle.SYSTEM, PERMISSION_SELF)
}
@@ -90,7 +104,9 @@ open class BackupHelper : BackupAgentHelper() {
* @property lock a lock to hold while backing up and restoring the files.
* @property context the context of the [BackupAgent]
* @property fileNamesAndPostProcess a map from the filenames to back up and the post processing
+ * ```
* actions to take
+ * ```
*/
private class NoOverwriteFileBackupHelper(
val lock: Any,
@@ -115,23 +131,23 @@ open class BackupHelper : BackupAgentHelper() {
data: BackupDataOutput?,
newState: ParcelFileDescriptor?
) {
- synchronized(lock) {
- super.performBackup(oldState, data, newState)
- }
+ synchronized(lock) { super.performBackup(oldState, data, newState) }
}
}
}
+
private fun getPPControlsFile(context: Context): () -> Unit {
return {
val filesDir = context.filesDir
val file = Environment.buildPath(filesDir, BackupHelper.CONTROLS)
if (file.exists()) {
- val dest = Environment.buildPath(filesDir,
- AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME)
+ val dest =
+ Environment.buildPath(filesDir, AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME)
file.copyTo(dest)
val jobScheduler = context.getSystemService(JobScheduler::class.java)
jobScheduler?.schedule(
- AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context))
+ AuxiliaryPersistenceWrapper.DeletionJobService.getJobForContext(context)
+ )
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index b2a2a679b383..b962cc43eddf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -107,6 +107,8 @@ open class AuthBiometricFingerprintIconController(
if (shouldAnimateForTransition(lastState, newState)) {
iconView.playAnimation()
iconViewOverlay.playAnimation()
+ } else if (lastState == STATE_IDLE && newState == STATE_AUTHENTICATING_ANIMATING_IN) {
+ iconView.playAnimation()
}
LottieColorUtils.applyDynamicColors(context, iconView)
LottieColorUtils.applyDynamicColors(context, iconViewOverlay)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index eb974dd909be..092339a74e3f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -598,6 +598,10 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
getFingerprintSensorLocationInNaturalOrientation(),
mCachedDisplayInfo);
}
+
+ for (final Callback cb : mCallbacks) {
+ cb.onFingerprintLocationChanged();
+ }
}
/**
@@ -619,6 +623,10 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
mCachedDisplayInfo
);
}
+
+ for (final Callback cb : mCallbacks) {
+ cb.onFaceSensorLocationChanged();
+ }
}
/**
@@ -1265,8 +1273,24 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
default void onBiometricPromptDismissed() {}
/**
- * The location in pixels can change due to resolution changes.
+ * Called when the location of the fingerprint sensor changes. The location in pixels can
+ * change due to resolution changes.
+ */
+ default void onFingerprintLocationChanged() {}
+
+ /**
+ * Called when the location of the under display fingerprint sensor changes. The location in
+ * pixels can change due to resolution changes.
+ *
+ * On devices with UDFPS, this is always called alongside
+ * {@link #onFingerprintLocationChanged}.
*/
default void onUdfpsLocationChanged() {}
+
+ /**
+ * Called when the location of the face unlock sensor (typically the front facing camera)
+ * changes. The location in pixels can change due to resolution changes.
+ */
+ default void onFaceSensorLocationChanged() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 4363b88bf21e..d68fcd0a5922 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -29,6 +29,8 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CircleReveal
@@ -71,7 +73,8 @@ class AuthRippleController @Inject constructor(
private val biometricUnlockController: BiometricUnlockController,
private val udfpsControllerProvider: Provider<UdfpsController>,
private val statusBarStateController: StatusBarStateController,
- rippleView: AuthRippleView?
+ private val featureFlags: FeatureFlags,
+ rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView), KeyguardStateController.Callback,
WakefulnessLifecycle.Observer {
@@ -116,9 +119,9 @@ class AuthRippleController @Inject constructor(
notificationShadeWindowController.setForcePluginOpen(false, this)
}
- fun showUnlockRipple(biometricSourceType: BiometricSourceType?) {
+ fun showUnlockRipple(biometricSourceType: BiometricSourceType) {
if (!keyguardStateController.isShowing ||
- keyguardUpdateMonitor.userNeedsStrongAuth()) {
+ !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(biometricSourceType)) {
return
}
@@ -159,12 +162,17 @@ class AuthRippleController @Inject constructor(
private fun showUnlockedRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- val lightRevealScrim = centralSurfaces.lightRevealScrim
- if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
- circleReveal?.let {
- lightRevealScrim?.revealAmount = 0f
- lightRevealScrim?.revealEffect = it
- startLightRevealScrimOnKeyguardFadingAway = true
+
+ // This code path is not used if the KeyguardTransitionRepository is managing the light
+ // reveal scrim.
+ if (!featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ val lightRevealScrim = centralSurfaces.lightRevealScrim
+ if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
+ circleReveal?.let {
+ lightRevealScrim?.revealAmount = 0f
+ lightRevealScrim?.revealEffect = it
+ startLightRevealScrimOnKeyguardFadingAway = true
+ }
}
}
@@ -177,6 +185,10 @@ class AuthRippleController @Inject constructor(
}
override fun onKeyguardFadingAwayChanged() {
+ if (featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ return
+ }
+
if (keyguardStateController.isKeyguardFadingAway) {
val lightRevealScrim = centralSurfaces.lightRevealScrim
if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
@@ -246,7 +258,7 @@ class AuthRippleController @Inject constructor(
object : KeyguardUpdateMonitorCallback() {
override fun onBiometricAuthenticated(
userId: Int,
- biometricSourceType: BiometricSourceType?,
+ biometricSourceType: BiometricSourceType,
isStrongBiometric: Boolean
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
@@ -255,14 +267,14 @@ class AuthRippleController @Inject constructor(
showUnlockRipple(biometricSourceType)
}
- override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
+ override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
mView.retractDwellRipple()
}
}
override fun onBiometricAcquired(
- biometricSourceType: BiometricSourceType?,
+ biometricSourceType: BiometricSourceType,
acquireInfo: Int
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 1c3dd451a1d3..e09e65e1b479 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -370,11 +370,15 @@ private fun WindowInsets.hasBigNavigationBar(): Boolean =
private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
fun update() {
val c = context.getColor(R.color.biometric_dialog_accent)
+ val chevronFill = context.getColor(R.color.sfps_chevron_fill)
for (key in listOf(".blue600", ".blue400")) {
addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP)
}
}
+ addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
+ PorterDuffColorFilter(chevronFill, PorterDuff.Mode.SRC_ATOP)
+ }
}
if (composition != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 45595c80d36d..5a81bd3e01b6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -61,6 +61,11 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.biometrics.dagger.BiometricsBackground;
+import com.android.systemui.biometrics.udfps.InteractionEvent;
+import com.android.systemui.biometrics.udfps.NormalizedTouchData;
+import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
+import com.android.systemui.biometrics.udfps.TouchProcessor;
+import com.android.systemui.biometrics.udfps.TouchProcessorResult;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
@@ -142,6 +147,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
@NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+ @Nullable private final TouchProcessor mTouchProcessor;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -165,7 +171,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
// The current request from FingerprintService. Null if no current request.
@Nullable UdfpsControllerOverlay mOverlay;
- @Nullable private UdfpsEllipseDetection mUdfpsEllipseDetection;
// The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
// to turn off high brightness mode. To get around this limitation, the state of the AOD
@@ -322,10 +327,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (!mOverlayParams.equals(overlayParams)) {
mOverlayParams = overlayParams;
- if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
- mUdfpsEllipseDetection.updateOverlayParams(overlayParams);
- }
-
final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateBouncer();
// When the bounds change it's always necessary to re-create the overlay's window with
@@ -434,8 +435,99 @@ public class UdfpsController implements DozeReceiver, Dumpable {
return portraitTouch;
}
+ private void tryDismissingKeyguard() {
+ if (!mOnFingerDown) {
+ playStartHaptic();
+ }
+ mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */);
+ mAttemptedToDismissKeyguard = true;
+ }
+
@VisibleForTesting
boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
+ if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
+ return newOnTouch(requestId, event, fromUdfpsView);
+ } else {
+ return oldOnTouch(requestId, event, fromUdfpsView);
+ }
+ }
+
+ private boolean newOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
+ if (!fromUdfpsView) {
+ Log.e(TAG, "ignoring the touch injected from outside of UdfpsView");
+ return false;
+ }
+ if (mOverlay == null) {
+ Log.w(TAG, "ignoring onTouch with null overlay");
+ return false;
+ }
+ if (!mOverlay.matchesRequestId(requestId)) {
+ Log.w(TAG, "ignoring stale touch event: " + requestId + " current: "
+ + mOverlay.getRequestId());
+ return false;
+ }
+
+ final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId,
+ mOverlayParams);
+ if (result instanceof TouchProcessorResult.Failure) {
+ Log.w(TAG, ((TouchProcessorResult.Failure) result).getReason());
+ return false;
+ }
+
+ final TouchProcessorResult.ProcessedTouch processedTouch =
+ (TouchProcessorResult.ProcessedTouch) result;
+ final NormalizedTouchData data = processedTouch.getTouchData();
+
+ mActivePointerId = processedTouch.getPointerOnSensorId();
+ switch (processedTouch.getEvent()) {
+ case DOWN:
+ if (shouldTryToDismissKeyguard()) {
+ tryDismissingKeyguard();
+ }
+ onFingerDown(requestId,
+ data.getPointerId(),
+ data.getX(),
+ data.getY(),
+ data.getMinor(),
+ data.getMajor(),
+ data.getOrientation(),
+ data.getTime(),
+ data.getGestureStart(),
+ mStatusBarStateController.isDozing());
+ break;
+
+ case UP:
+ case CANCEL:
+ if (InteractionEvent.CANCEL.equals(processedTouch.getEvent())) {
+ Log.w(TAG, "This is a CANCEL event that's reported as an UP event!");
+ }
+ mAttemptedToDismissKeyguard = false;
+ onFingerUp(requestId,
+ mOverlay.getOverlayView(),
+ data.getPointerId(),
+ data.getX(),
+ data.getY(),
+ data.getMinor(),
+ data.getMajor(),
+ data.getOrientation(),
+ data.getTime(),
+ data.getGestureStart(),
+ mStatusBarStateController.isDozing());
+ mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
+ break;
+
+
+ default:
+ break;
+ }
+
+ // We should only consume touches that are within the sensor. By returning "false" for
+ // touches outside of the sensor, we let other UI components consume these events and act on
+ // them appropriately.
+ return processedTouch.getTouchData().isWithinSensor(mOverlayParams.getNativeSensorBounds());
+ }
+
+ private boolean oldOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
if (mOverlay == null) {
Log.w(TAG, "ignoring onTouch with null overlay");
return false;
@@ -465,23 +557,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mVelocityTracker.clear();
}
- boolean withinSensorArea;
- if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
- // Ellipse detection
- withinSensorArea = mUdfpsEllipseDetection.isGoodEllipseOverlap(event);
- } else {
- // Centroid with expanded overlay
- withinSensorArea =
- isWithinSensorArea(udfpsView, event.getRawX(),
- event.getRawY(), fromUdfpsView);
- }
- } else {
- // Centroid with sensor sized view
- withinSensorArea =
+ final boolean withinSensorArea =
isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView);
- }
-
if (withinSensorArea) {
Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0);
Log.v(TAG, "onTouch | action down");
@@ -495,11 +572,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
if ((withinSensorArea || fromUdfpsView) && shouldTryToDismissKeyguard()) {
Log.v(TAG, "onTouch | dismiss keyguard ACTION_DOWN");
- if (!mOnFingerDown) {
- playStartHaptic();
- }
- mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */);
- mAttemptedToDismissKeyguard = true;
+ tryDismissingKeyguard();
}
Trace.endSection();
@@ -512,33 +585,13 @@ public class UdfpsController implements DozeReceiver, Dumpable {
? event.getPointerId(0)
: event.findPointerIndex(mActivePointerId);
if (idx == event.getActionIndex()) {
- boolean actionMoveWithinSensorArea;
- if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
- // Ellipse detection
- actionMoveWithinSensorArea =
- mUdfpsEllipseDetection.isGoodEllipseOverlap(event);
- } else {
- // Centroid with expanded overlay
- actionMoveWithinSensorArea =
- isWithinSensorArea(udfpsView, event.getRawX(idx),
- event.getRawY(idx), fromUdfpsView);
- }
- } else {
- // Centroid with sensor sized view
- actionMoveWithinSensorArea =
- isWithinSensorArea(udfpsView, event.getX(idx),
- event.getY(idx), fromUdfpsView);
- }
-
+ final boolean actionMoveWithinSensorArea =
+ isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx),
+ fromUdfpsView);
if ((fromUdfpsView || actionMoveWithinSensorArea)
&& shouldTryToDismissKeyguard()) {
Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE");
- if (!mOnFingerDown) {
- playStartHaptic();
- }
- mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */);
- mAttemptedToDismissKeyguard = true;
+ tryDismissingKeyguard();
break;
}
// Map the touch to portrait mode if the device is in landscape mode.
@@ -663,7 +716,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
@NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider,
@NonNull @BiometricsBackground Executor biometricsExecutor,
- @NonNull PrimaryBouncerInteractor primaryBouncerInteractor) {
+ @NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
+ @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -704,6 +758,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mBiometricExecutor = biometricsExecutor;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
+ mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
+ ? singlePointerTouchProcessor : null;
+
mDumpManager.registerDumpable(TAG, this);
mOrientationListener = new BiometricDisplayListener(
@@ -728,10 +785,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
udfpsHapticsSimulator.setUdfpsController(this);
udfpsShell.setUdfpsOverlayController(mUdfpsOverlayController);
-
- if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
- mUdfpsEllipseDetection = new UdfpsEllipseDetection(mOverlayParams);
- }
}
/**
@@ -913,7 +966,36 @@ public class UdfpsController implements DozeReceiver, Dumpable {
return mOnFingerDown;
}
- private void onFingerDown(long requestId, int x, int y, float minor, float major) {
+ private void onFingerDown(
+ long requestId,
+ int x,
+ int y,
+ float minor,
+ float major) {
+ onFingerDown(
+ requestId,
+ MotionEvent.INVALID_POINTER_ID /* pointerId */,
+ x,
+ y,
+ minor,
+ major,
+ 0f /* orientation */,
+ 0L /* time */,
+ 0L /* gestureStart */,
+ false /* isAod */);
+ }
+
+ private void onFingerDown(
+ long requestId,
+ int pointerId,
+ float x,
+ float y,
+ float minor,
+ float major,
+ float orientation,
+ long time,
+ long gestureStart,
+ boolean isAod) {
mExecution.assertIsMainThread();
if (mOverlay == null) {
@@ -942,7 +1024,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mOnFingerDown = true;
if (mAlternateTouchProvider != null) {
mBiometricExecutor.execute(() -> {
- mAlternateTouchProvider.onPointerDown(requestId, x, y, minor, major);
+ mAlternateTouchProvider.onPointerDown(requestId, (int) x, (int) y, minor, major);
});
mFgExecutor.execute(() -> {
if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
@@ -950,7 +1032,13 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
});
} else {
- mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major);
+ if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
+ mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
+ minor, major, orientation, time, gestureStart, isAod);
+ } else {
+ mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, (int) x,
+ (int) y, minor, major);
+ }
}
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
final UdfpsView view = mOverlay.getOverlayView();
@@ -974,6 +1062,32 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
private void onFingerUp(long requestId, @NonNull UdfpsView view) {
+ onFingerUp(
+ requestId,
+ view,
+ MotionEvent.INVALID_POINTER_ID /* pointerId */,
+ 0f /* x */,
+ 0f /* y */,
+ 0f /* minor */,
+ 0f /* major */,
+ 0f /* orientation */,
+ 0L /* time */,
+ 0L /* gestureStart */,
+ false /* isAod */);
+ }
+
+ private void onFingerUp(
+ long requestId,
+ @NonNull UdfpsView view,
+ int pointerId,
+ float x,
+ float y,
+ float minor,
+ float major,
+ float orientation,
+ long time,
+ long gestureStart,
+ boolean isAod) {
mExecution.assertIsMainThread();
mActivePointerId = -1;
mAcquiredReceived = false;
@@ -988,7 +1102,12 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
});
} else {
- mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
+ if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
+ mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x,
+ y, minor, major, orientation, time, gestureStart, isAod);
+ } else {
+ mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
+ }
}
for (Callback cb : mCallbacks) {
cb.onFingerUp();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt
deleted file mode 100644
index 8ae4775467df..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.graphics.Point
-import android.graphics.Rect
-import android.util.RotationUtils
-import android.view.MotionEvent
-import kotlin.math.cos
-import kotlin.math.pow
-import kotlin.math.sin
-
-private const val TAG = "UdfpsEllipseDetection"
-
-private const val NEEDED_POINTS = 2
-
-class UdfpsEllipseDetection(overlayParams: UdfpsOverlayParams) {
- var sensorRect = Rect()
- var points: Array<Point> = emptyArray()
-
- init {
- sensorRect = Rect(overlayParams.sensorBounds)
-
- points = calculateSensorPoints(sensorRect)
- }
-
- fun updateOverlayParams(params: UdfpsOverlayParams) {
- sensorRect = Rect(params.sensorBounds)
-
- val rot = params.rotation
- RotationUtils.rotateBounds(
- sensorRect,
- params.naturalDisplayWidth,
- params.naturalDisplayHeight,
- rot
- )
-
- points = calculateSensorPoints(sensorRect)
- }
-
- fun isGoodEllipseOverlap(event: MotionEvent): Boolean {
- return points.count { checkPoint(event, it) } >= NEEDED_POINTS
- }
-
- private fun checkPoint(event: MotionEvent, point: Point): Boolean {
- // Calculate if sensor point is within ellipse
- // Formula: ((cos(o)(xE - xS) + sin(o)(yE - yS))^2 / a^2) + ((sin(o)(xE - xS) + cos(o)(yE -
- // yS))^2 / b^2) <= 1
- val a: Float = cos(event.orientation) * (point.x - event.rawX)
- val b: Float = sin(event.orientation) * (point.y - event.rawY)
- val c: Float = sin(event.orientation) * (point.x - event.rawX)
- val d: Float = cos(event.orientation) * (point.y - event.rawY)
- val result =
- (a + b).pow(2) / (event.touchMinor / 2).pow(2) +
- (c - d).pow(2) / (event.touchMajor / 2).pow(2)
-
- return result <= 1
- }
-}
-
-fun calculateSensorPoints(sensorRect: Rect): Array<Point> {
- val sensorX = sensorRect.centerX()
- val sensorY = sensorRect.centerY()
- val cornerOffset: Int = sensorRect.width() / 4
- val sideOffset: Int = sensorRect.width() / 3
-
- return arrayOf(
- Point(sensorX - cornerOffset, sensorY - cornerOffset),
- Point(sensorX, sensorY - sideOffset),
- Point(sensorX + cornerOffset, sensorY - cornerOffset),
- Point(sensorX - sideOffset, sensorY),
- Point(sensorX, sensorY),
- Point(sensorX + sideOffset, sensorY),
- Point(sensorX - cornerOffset, sensorY + cornerOffset),
- Point(sensorX, sensorY + sideOffset),
- Point(sensorX + cornerOffset, sensorY + cornerOffset)
- )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
index 98d4c22d927d..7f3846ca4e40 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
@@ -7,17 +7,23 @@ import android.view.Surface.Rotation
/**
* Collection of parameters that define an under-display fingerprint sensor (UDFPS) overlay.
*
- * @property sensorBounds coordinates of the bounding box around the sensor, in natural orientation,
- * in pixels, for the current resolution.
- * @property naturalDisplayWidth width of the physical display, in natural orientation, in pixels,
- * for the current resolution.
- * @property naturalDisplayHeight height of the physical display, in natural orientation, in pixels,
- * for the current resolution.
- * @property scaleFactor ratio of a dimension in the current resolution to the corresponding
- * dimension in the native resolution.
- * @property rotation current rotation of the display.
+ * [sensorBounds] coordinates of the bounding box around the sensor in natural orientation, in
+ * pixels, for the current resolution.
+ *
+ * [overlayBounds] coordinates of the UI overlay in natural orientation, in pixels, for the current
+ * resolution.
+ *
+ * [naturalDisplayWidth] width of the physical display in natural orientation, in pixels, for the
+ * current resolution.
+ *
+ * [naturalDisplayHeight] height of the physical display in natural orientation, in pixels, for the
+ * current resolution.
+ *
+ * [scaleFactor] ratio of a dimension in the current resolution to the corresponding dimension in
+ * the native resolution.
+ *
+ * [rotation] current rotation of the display.
*/
-
data class UdfpsOverlayParams(
val sensorBounds: Rect = Rect(),
val overlayBounds: Rect = Rect(),
@@ -26,19 +32,23 @@ data class UdfpsOverlayParams(
val scaleFactor: Float = 1f,
@Rotation val rotation: Int = Surface.ROTATION_0
) {
+
+ /** Same as [sensorBounds], but in native resolution. */
+ val nativeSensorBounds = Rect(sensorBounds).apply { scale(1f / scaleFactor) }
+
/** See [android.view.DisplayInfo.logicalWidth] */
- val logicalDisplayWidth
- get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+ val logicalDisplayWidth =
+ if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
naturalDisplayHeight
} else {
naturalDisplayWidth
}
/** See [android.view.DisplayInfo.logicalHeight] */
- val logicalDisplayHeight
- get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+ val logicalDisplayHeight =
+ if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
naturalDisplayWidth
} else {
naturalDisplayHeight
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
new file mode 100644
index 000000000000..001fed76c124
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.dagger
+
+import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector
+import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
+import com.android.systemui.biometrics.udfps.OverlapDetector
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import dagger.Module
+import dagger.Provides
+
+/** Dagger module for all things UDFPS. TODO(b/260558624): Move to BiometricsModule. */
+@Module
+interface UdfpsModule {
+ companion object {
+
+ @Provides
+ @SysUISingleton
+ fun providesOverlapDetector(featureFlags: FeatureFlags): OverlapDetector {
+ return if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
+ EllipseOverlapDetector()
+ } else {
+ BoundingBoxOverlapDetector()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
new file mode 100644
index 000000000000..79a0acb8bbc1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import com.android.systemui.dagger.SysUISingleton
+
+/** Returns whether the touch coordinates are within the sensor's bounding box. */
+@SysUISingleton
+class BoundingBoxOverlapDetector : OverlapDetector {
+ override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean =
+ touchData.isWithinSensor(nativeSensorBounds)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt
new file mode 100644
index 000000000000..857224290752
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.biometrics.udfps
+
+import android.graphics.Point
+import android.graphics.Rect
+import com.android.systemui.dagger.SysUISingleton
+import kotlin.math.cos
+import kotlin.math.pow
+import kotlin.math.sin
+
+/**
+ * Approximates the touch as an ellipse and determines whether the ellipse has a sufficient overlap
+ * with the sensor.
+ */
+@SysUISingleton
+class EllipseOverlapDetector(private val neededPoints: Int = 2) : OverlapDetector {
+
+ override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
+ val points = calculateSensorPoints(nativeSensorBounds)
+ return points.count { checkPoint(it, touchData) } >= neededPoints
+ }
+
+ private fun checkPoint(point: Point, touchData: NormalizedTouchData): Boolean {
+ // Calculate if sensor point is within ellipse
+ // Formula: ((cos(o)(xE - xS) + sin(o)(yE - yS))^2 / a^2) + ((sin(o)(xE - xS) + cos(o)(yE -
+ // yS))^2 / b^2) <= 1
+ val a: Float = cos(touchData.orientation) * (point.x - touchData.x)
+ val b: Float = sin(touchData.orientation) * (point.y - touchData.y)
+ val c: Float = sin(touchData.orientation) * (point.x - touchData.x)
+ val d: Float = cos(touchData.orientation) * (point.y - touchData.y)
+ val result =
+ (a + b).pow(2) / (touchData.minor / 2).pow(2) +
+ (c - d).pow(2) / (touchData.major / 2).pow(2)
+
+ return result <= 1
+ }
+
+ private fun calculateSensorPoints(sensorBounds: Rect): List<Point> {
+ val sensorX = sensorBounds.centerX()
+ val sensorY = sensorBounds.centerY()
+ val cornerOffset: Int = sensorBounds.width() / 4
+ val sideOffset: Int = sensorBounds.width() / 3
+
+ return listOf(
+ Point(sensorX - cornerOffset, sensorY - cornerOffset),
+ Point(sensorX, sensorY - sideOffset),
+ Point(sensorX + cornerOffset, sensorY - cornerOffset),
+ Point(sensorX - sideOffset, sensorY),
+ Point(sensorX, sensorY),
+ Point(sensorX + sideOffset, sensorY),
+ Point(sensorX - cornerOffset, sensorY + cornerOffset),
+ Point(sensorX, sensorY + sideOffset),
+ Point(sensorX + cornerOffset, sensorY + cornerOffset)
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt
new file mode 100644
index 000000000000..6e47dadc4545
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.biometrics.udfps
+
+import android.view.MotionEvent
+
+/** Interaction event between a finger and the under-display fingerprint sensor (UDFPS). */
+enum class InteractionEvent {
+ /**
+ * A finger entered the sensor area. This can originate from either [MotionEvent.ACTION_DOWN] or
+ * [MotionEvent.ACTION_MOVE].
+ */
+ DOWN,
+
+ /**
+ * A finger left the sensor area. This can originate from either [MotionEvent.ACTION_UP] or
+ * [MotionEvent.ACTION_MOVE].
+ */
+ UP,
+
+ /**
+ * The touch reporting has stopped. This corresponds to [MotionEvent.ACTION_CANCEL]. This should
+ * not be confused with [UP]. If there was a finger on the sensor, it may or may not still be on
+ * the sensor.
+ */
+ CANCEL,
+
+ /**
+ * The interaction hasn't changed since the previous event. The can originate from any of
+ * [MotionEvent.ACTION_DOWN], [MotionEvent.ACTION_MOVE], or [MotionEvent.ACTION_UP] if one of
+ * these is true:
+ * - There was previously a finger on the sensor, and that finger is still on the sensor.
+ * - There was previously no finger on the sensor, and there still isn't.
+ */
+ UNCHANGED,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
new file mode 100644
index 000000000000..62bedc627b07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import android.view.MotionEvent
+
+/** Touch data in natural orientation and native resolution. */
+data class NormalizedTouchData(
+
+ /**
+ * Value obtained from [MotionEvent.getPointerId], or [MotionEvent.INVALID_POINTER_ID] if the ID
+ * is not available.
+ */
+ val pointerId: Int,
+
+ /** [MotionEvent.getRawX] mapped to natural orientation and native resolution. */
+ val x: Float,
+
+ /** [MotionEvent.getRawY] mapped to natural orientation and native resolution. */
+ val y: Float,
+
+ /** [MotionEvent.getTouchMinor] mapped to natural orientation and native resolution. */
+ val minor: Float,
+
+ /** [MotionEvent.getTouchMajor] mapped to natural orientation and native resolution. */
+ val major: Float,
+
+ /** [MotionEvent.getOrientation] mapped to natural orientation. */
+ val orientation: Float,
+
+ /** [MotionEvent.getEventTime]. */
+ val time: Long,
+
+ /** [MotionEvent.getDownTime]. */
+ val gestureStart: Long,
+) {
+
+ /**
+ * [nativeSensorBounds] contains the location and dimensions of the sensor area in native
+ * resolution and natural orientation.
+ *
+ * Returns whether the coordinates of the given pointer are within the sensor's bounding box.
+ */
+ fun isWithinSensor(nativeSensorBounds: Rect): Boolean {
+ return nativeSensorBounds.left <= x &&
+ nativeSensorBounds.right >= x &&
+ nativeSensorBounds.top <= y &&
+ nativeSensorBounds.bottom >= y
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt
new file mode 100644
index 000000000000..0fec8ffbaa0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+
+/** Determines whether the touch has a sufficient overlap with the sensor. */
+interface OverlapDetector {
+ fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
new file mode 100644
index 000000000000..338bf66d197e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
@@ -0,0 +1,164 @@
+/*
+ * 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.biometrics.udfps
+
+import android.graphics.PointF
+import android.util.RotationUtils
+import android.view.MotionEvent
+import android.view.MotionEvent.INVALID_POINTER_ID
+import android.view.Surface
+import com.android.systemui.biometrics.UdfpsOverlayParams
+import com.android.systemui.biometrics.udfps.TouchProcessorResult.Failure
+import com.android.systemui.biometrics.udfps.TouchProcessorResult.ProcessedTouch
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * TODO(b/259140693): Consider using an object pool of TouchProcessorResult to avoid allocations.
+ */
+@SysUISingleton
+class SinglePointerTouchProcessor @Inject constructor(val overlapDetector: OverlapDetector) :
+ TouchProcessor {
+
+ override fun processTouch(
+ event: MotionEvent,
+ previousPointerOnSensorId: Int,
+ overlayParams: UdfpsOverlayParams,
+ ): TouchProcessorResult {
+
+ fun preprocess(): PreprocessedTouch {
+ // TODO(b/253085297): Add multitouch support. pointerIndex can be > 0 for ACTION_MOVE.
+ val pointerIndex = 0
+ val touchData = event.normalize(pointerIndex, overlayParams)
+ val isGoodOverlap =
+ overlapDetector.isGoodOverlap(touchData, overlayParams.nativeSensorBounds)
+ return PreprocessedTouch(touchData, previousPointerOnSensorId, isGoodOverlap)
+ }
+
+ return when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> processActionDown(preprocess())
+ MotionEvent.ACTION_MOVE -> processActionMove(preprocess())
+ MotionEvent.ACTION_UP -> processActionUp(preprocess())
+ MotionEvent.ACTION_CANCEL ->
+ processActionCancel(event.normalize(pointerIndex = 0, overlayParams))
+ else ->
+ Failure("Unsupported MotionEvent." + MotionEvent.actionToString(event.actionMasked))
+ }
+ }
+}
+
+private data class PreprocessedTouch(
+ val data: NormalizedTouchData,
+ val previousPointerOnSensorId: Int,
+ val isGoodOverlap: Boolean,
+)
+
+private fun processActionDown(touch: PreprocessedTouch): TouchProcessorResult {
+ return if (touch.isGoodOverlap) {
+ ProcessedTouch(InteractionEvent.DOWN, pointerOnSensorId = touch.data.pointerId, touch.data)
+ } else {
+ val event =
+ if (touch.data.pointerId == touch.previousPointerOnSensorId) {
+ InteractionEvent.UP
+ } else {
+ InteractionEvent.UNCHANGED
+ }
+ ProcessedTouch(event, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+ }
+}
+
+private fun processActionMove(touch: PreprocessedTouch): TouchProcessorResult {
+ val hadPointerOnSensor = touch.previousPointerOnSensorId != INVALID_POINTER_ID
+ val interactionEvent =
+ when {
+ touch.isGoodOverlap && !hadPointerOnSensor -> InteractionEvent.DOWN
+ !touch.isGoodOverlap && hadPointerOnSensor -> InteractionEvent.UP
+ else -> InteractionEvent.UNCHANGED
+ }
+ val pointerOnSensorId =
+ when (interactionEvent) {
+ InteractionEvent.UNCHANGED -> touch.previousPointerOnSensorId
+ InteractionEvent.DOWN -> touch.data.pointerId
+ else -> INVALID_POINTER_ID
+ }
+ return ProcessedTouch(interactionEvent, pointerOnSensorId, touch.data)
+}
+
+private fun processActionUp(touch: PreprocessedTouch): TouchProcessorResult {
+ return if (touch.isGoodOverlap) {
+ ProcessedTouch(InteractionEvent.UP, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+ } else {
+ val event =
+ if (touch.previousPointerOnSensorId != INVALID_POINTER_ID) {
+ InteractionEvent.UP
+ } else {
+ InteractionEvent.UNCHANGED
+ }
+ ProcessedTouch(event, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+ }
+}
+
+private fun processActionCancel(data: NormalizedTouchData): TouchProcessorResult {
+ return ProcessedTouch(InteractionEvent.CANCEL, pointerOnSensorId = INVALID_POINTER_ID, data)
+}
+
+/**
+ * Returns the touch information from the given [MotionEvent] with the relevant fields mapped to
+ * natural orientation and native resolution.
+ */
+private fun MotionEvent.normalize(
+ pointerIndex: Int,
+ overlayParams: UdfpsOverlayParams
+): NormalizedTouchData {
+ val naturalTouch: PointF = rotateToNaturalOrientation(pointerIndex, overlayParams)
+ val nativeX = naturalTouch.x / overlayParams.scaleFactor
+ val nativeY = naturalTouch.y / overlayParams.scaleFactor
+ val nativeMinor: Float = getTouchMinor(pointerIndex) / overlayParams.scaleFactor
+ val nativeMajor: Float = getTouchMajor(pointerIndex) / overlayParams.scaleFactor
+ return NormalizedTouchData(
+ pointerId = getPointerId(pointerIndex),
+ x = nativeX,
+ y = nativeY,
+ minor = nativeMinor,
+ major = nativeMajor,
+ // TODO(b/259311354): touch orientation should be reported relative to Surface.ROTATION_O.
+ orientation = getOrientation(pointerIndex),
+ time = eventTime,
+ gestureStart = downTime,
+ )
+}
+
+/**
+ * Returns the [MotionEvent.getRawX] and [MotionEvent.getRawY] of the given pointer as if the device
+ * is in the [Surface.ROTATION_0] orientation.
+ */
+private fun MotionEvent.rotateToNaturalOrientation(
+ pointerIndex: Int,
+ overlayParams: UdfpsOverlayParams
+): PointF {
+ val touchPoint = PointF(getRawX(pointerIndex), getRawY(pointerIndex))
+ val rot = overlayParams.rotation
+ if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+ RotationUtils.rotatePointF(
+ touchPoint,
+ RotationUtils.deltaRotation(rot, Surface.ROTATION_0),
+ overlayParams.logicalDisplayWidth.toFloat(),
+ overlayParams.logicalDisplayHeight.toFloat()
+ )
+ }
+ return touchPoint
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt
new file mode 100644
index 000000000000..ffcebf9cff75
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.biometrics.udfps
+
+import android.view.MotionEvent
+import com.android.systemui.biometrics.UdfpsOverlayParams
+
+/**
+ * Determines whether a finger entered or left the area of the under-display fingerprint sensor
+ * (UDFPS). Maps the touch information from a [MotionEvent] to the orientation and scale independent
+ * [NormalizedTouchData].
+ */
+interface TouchProcessor {
+
+ /**
+ * [event] touch event to be processed.
+ *
+ * [previousPointerOnSensorId] pointerId for the finger that was on the sensor prior to this
+ * event. See [MotionEvent.getPointerId]. If there was no finger on the sensor, this should be
+ * set to [MotionEvent.INVALID_POINTER_ID].
+ *
+ * [overlayParams] contains the location and dimensions of the sensor area, as well as the scale
+ * factor and orientation of the overlay. See [UdfpsOverlayParams].
+ *
+ * Returns [TouchProcessorResult.ProcessedTouch] on success, and [TouchProcessorResult.Failure]
+ * on failure.
+ */
+ fun processTouch(
+ event: MotionEvent,
+ previousPointerOnSensorId: Int,
+ overlayParams: UdfpsOverlayParams,
+ ): TouchProcessorResult
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt
new file mode 100644
index 000000000000..be75bb0d3821
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.view.MotionEvent
+
+/** Contains all the possible returns types for [TouchProcessor.processTouch] */
+sealed class TouchProcessorResult {
+
+ /**
+ * [event] whether a finger entered or left the sensor area. See [InteractionEvent].
+ *
+ * [pointerOnSensorId] pointerId fof the finger that's currently on the sensor. See
+ * [MotionEvent.getPointerId]. If there is no finger on the sensor, the value is set to
+ * [MotionEvent.INVALID_POINTER_ID].
+ *
+ * [touchData] relevant data from the MotionEvent, mapped to natural orientation and native
+ * resolution. See [NormalizedTouchData].
+ */
+ data class ProcessedTouch(
+ val event: InteractionEvent,
+ val pointerOnSensorId: Int,
+ val touchData: NormalizedTouchData
+ ) : TouchProcessorResult()
+
+ /** [reason] the reason for the failure. */
+ data class Failure(val reason: String = "") : TouchProcessorResult()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 537cbc5a267d..a0a892de0085 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -64,8 +64,9 @@ private const val TAG = "BroadcastDispatcher"
* from SystemUI. That way the number of calls to [BroadcastReceiver.onReceive] can be reduced for
* a given broadcast.
*
- * Use only for IntentFilters with actions and optionally categories. It does not support,
- * permissions, schemes, data types, data authorities or priority different than 0.
+ * Use only for IntentFilters with actions and optionally categories. It does not support schemes,
+ * data types, data authorities or priority different than 0.
+ *
* Cannot be used for getting sticky broadcasts (either as return of registering or as re-delivery).
* Broadcast handling may be asynchronous *without* calling goAsync(), as it's running within sysui
* and doesn't need to worry about being killed.
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
index 4dfcd6398a4d..66e5d7c4a3bc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
@@ -30,6 +30,7 @@ import android.os.UserHandle
import android.service.controls.ControlsProviderService
import androidx.annotation.WorkerThread
import com.android.settingslib.applications.DefaultAppInfo
+import com.android.systemui.R
import java.util.Objects
class ControlsServiceInfo(
@@ -59,7 +60,8 @@ class ControlsServiceInfo(
* instead of using the controls rendered by SystemUI.
*
* The activity must be in the same package, exported, enabled and protected by the
- * [Manifest.permission.BIND_CONTROLS] permission.
+ * [Manifest.permission.BIND_CONTROLS] permission. Additionally, only packages declared in
+ * [R.array.config_controlsPreferredPackages] can declare activities for use as a panel.
*/
var panelActivity: ComponentName? = null
private set
@@ -70,6 +72,9 @@ class ControlsServiceInfo(
fun resolvePanelActivity() {
if (resolved) return
resolved = true
+ val validPackages = context.resources
+ .getStringArray(R.array.config_controlsPreferredPackages)
+ if (componentName.packageName !in validPackages) return
panelActivity = _panelActivity?.let {
val resolveInfos = mPm.queryIntentActivitiesAsUser(
Intent().setComponent(it),
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt
new file mode 100644
index 000000000000..3d10ab906f2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.controls
+
+import kotlinx.coroutines.flow.StateFlow
+
+/** Repository for Device controls related settings. */
+interface ControlsSettingsRepository {
+ /** Whether device controls activity can be shown above lockscreen for this user. */
+ val canShowControlsInLockscreen: StateFlow<Boolean>
+
+ /** Whether trivial controls can be actioned from the lockscreen for this user. */
+ val allowActionOnTrivialControlsInLockscreen: StateFlow<Boolean>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt
new file mode 100644
index 000000000000..9dc422a09674
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.controls
+
+import android.provider.Settings
+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.qs.SettingObserver
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.settings.SecureSettings
+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.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * This implementation uses an `@Application` [CoroutineScope] to provide hot flows for the values
+ * of the tracked settings.
+ */
+@SysUISingleton
+class ControlsSettingsRepositoryImpl
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val userRepository: UserRepository,
+ private val secureSettings: SecureSettings
+) : ControlsSettingsRepository {
+
+ override val canShowControlsInLockscreen =
+ makeFlowForSetting(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS)
+
+ override val allowActionOnTrivialControlsInLockscreen =
+ makeFlowForSetting(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS)
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private fun makeFlowForSetting(setting: String): StateFlow<Boolean> {
+ return userRepository.selectedUserInfo
+ .distinctUntilChanged()
+ .flatMapLatest { userInfo ->
+ conflatedCallbackFlow {
+ val observer =
+ object : SettingObserver(secureSettings, null, setting, userInfo.id) {
+ override fun handleValueChanged(
+ value: Int,
+ observedChange: Boolean
+ ) {
+ trySend(value == 1)
+ }
+ }
+ observer.isListening = true
+ trySend(observer.value == 1)
+ awaitClose { observer.isListening = false }
+ }
+ .flowOn(backgroundDispatcher)
+ .distinctUntilChanged()
+ }
+ .stateIn(
+ scope,
+ started = SharingStarted.Eagerly,
+ // When the observer starts listening, the flow will emit the current value
+ // so the initialValue here is irrelevant.
+ initialValue = false,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index bdfe1fbeb4b5..80c5f661f9a3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -33,7 +33,6 @@ import android.util.Log
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.backup.BackupHelper
-import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.management.ControlsListingController
@@ -60,11 +59,10 @@ class ControlsControllerImpl @Inject constructor (
private val uiController: ControlsUiController,
private val bindingController: ControlsBindingController,
private val listingController: ControlsListingController,
- private val broadcastDispatcher: BroadcastDispatcher,
private val userFileManager: UserFileManager,
+ private val userTracker: UserTracker,
optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
dumpManager: DumpManager,
- userTracker: UserTracker
) : Dumpable, ControlsController {
companion object {
@@ -121,18 +119,15 @@ class ControlsControllerImpl @Inject constructor (
userChanging = false
}
- private val userSwitchReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action == Intent.ACTION_USER_SWITCHED) {
- userChanging = true
- val newUser =
- UserHandle.of(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, sendingUserId))
- if (currentUser == newUser) {
- userChanging = false
- return
- }
- setValuesForUser(newUser)
+ private val userTrackerCallback = object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ userChanging = true
+ val newUserHandle = UserHandle.of(newUser)
+ if (currentUser == newUserHandle) {
+ userChanging = false
+ return
}
+ setValuesForUser(newUserHandle)
}
}
@@ -234,12 +229,7 @@ class ControlsControllerImpl @Inject constructor (
dumpManager.registerDumpable(javaClass.name, this)
resetFavorites()
userChanging = false
- broadcastDispatcher.registerReceiver(
- userSwitchReceiver,
- IntentFilter(Intent.ACTION_USER_SWITCHED),
- executor,
- UserHandle.ALL
- )
+ userTracker.addCallback(userTrackerCallback, executor)
context.registerReceiver(
restoreFinishedReceiver,
IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
@@ -251,7 +241,7 @@ class ControlsControllerImpl @Inject constructor (
}
fun destroy() {
- broadcastDispatcher.unregisterReceiver(userSwitchReceiver)
+ userTracker.removeCallback(userTrackerCallback)
context.unregisterReceiver(restoreFinishedReceiver)
listingController.removeCallback(listingCallback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
index 9e4a364562e5..77d0496e43db 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
@@ -16,13 +16,10 @@
package com.android.systemui.controls.dagger
-import android.content.ContentResolver
import android.content.Context
-import android.database.ContentObserver
-import android.os.UserHandle
-import android.provider.Settings
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+import com.android.systemui.controls.ControlsSettingsRepository
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.ControlsTileResourceConfiguration
import com.android.systemui.controls.controller.ControlsTileResourceConfigurationImpl
@@ -31,12 +28,10 @@ import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.settings.SecureSettings
import dagger.Lazy
+import kotlinx.coroutines.flow.StateFlow
import java.util.Optional
import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
/**
* Pseudo-component to inject into classes outside `com.android.systemui.controls`.
@@ -46,47 +41,26 @@ import kotlinx.coroutines.flow.asStateFlow
*/
@SysUISingleton
class ControlsComponent @Inject constructor(
- @ControlsFeatureEnabled private val featureEnabled: Boolean,
- private val context: Context,
- private val lazyControlsController: Lazy<ControlsController>,
- private val lazyControlsUiController: Lazy<ControlsUiController>,
- private val lazyControlsListingController: Lazy<ControlsListingController>,
- private val lockPatternUtils: LockPatternUtils,
- private val keyguardStateController: KeyguardStateController,
- private val userTracker: UserTracker,
- private val secureSettings: SecureSettings,
- private val optionalControlsTileResourceConfiguration:
- Optional<ControlsTileResourceConfiguration>
+ @ControlsFeatureEnabled private val featureEnabled: Boolean,
+ private val context: Context,
+ private val lazyControlsController: Lazy<ControlsController>,
+ private val lazyControlsUiController: Lazy<ControlsUiController>,
+ private val lazyControlsListingController: Lazy<ControlsListingController>,
+ private val lockPatternUtils: LockPatternUtils,
+ private val keyguardStateController: KeyguardStateController,
+ private val userTracker: UserTracker,
+ controlsSettingsRepository: ControlsSettingsRepository,
+ optionalControlsTileResourceConfiguration: Optional<ControlsTileResourceConfiguration>
) {
- private val contentResolver: ContentResolver
- get() = context.contentResolver
- private val _canShowWhileLockedSetting = MutableStateFlow(false)
- val canShowWhileLockedSetting = _canShowWhileLockedSetting.asStateFlow()
+ val canShowWhileLockedSetting: StateFlow<Boolean> =
+ controlsSettingsRepository.canShowControlsInLockscreen
private val controlsTileResourceConfiguration: ControlsTileResourceConfiguration =
optionalControlsTileResourceConfiguration.orElse(
ControlsTileResourceConfigurationImpl()
)
- val showWhileLockedObserver = object : ContentObserver(null) {
- override fun onChange(selfChange: Boolean) {
- updateShowWhileLocked()
- }
- }
-
- init {
- if (featureEnabled) {
- secureSettings.registerContentObserverForUser(
- Settings.Secure.getUriFor(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS),
- false, /* notifyForDescendants */
- showWhileLockedObserver,
- UserHandle.USER_ALL
- )
- updateShowWhileLocked()
- }
- }
-
fun getControlsController(): Optional<ControlsController> {
return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty()
}
@@ -127,11 +101,6 @@ class ControlsComponent @Inject constructor(
return Visibility.AVAILABLE
}
- private fun updateShowWhileLocked() {
- _canShowWhileLockedSetting.value = secureSettings.getIntForUser(
- Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
- }
-
enum class Visibility {
AVAILABLE, AVAILABLE_AFTER_UNLOCK, UNAVAILABLE
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index 6f58abdeed56..9ae605e30a83 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -20,6 +20,8 @@ import android.app.Activity
import android.content.pm.PackageManager
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.ControlsMetricsLoggerImpl
+import com.android.systemui.controls.ControlsSettingsRepository
+import com.android.systemui.controls.ControlsSettingsRepositoryImpl
import com.android.systemui.controls.controller.ControlsBindingController
import com.android.systemui.controls.controller.ControlsBindingControllerImpl
import com.android.systemui.controls.controller.ControlsController
@@ -83,6 +85,11 @@ abstract class ControlsModule {
abstract fun provideUiController(controller: ControlsUiControllerImpl): ControlsUiController
@Binds
+ abstract fun provideSettingsManager(
+ manager: ControlsSettingsRepositoryImpl
+ ): ControlsSettingsRepository
+
+ @Binds
abstract fun provideMetricsLogger(logger: ControlsMetricsLoggerImpl): ControlsMetricsLogger
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index b8a00133c728..8472ca0731d7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -24,9 +24,6 @@ import android.app.PendingIntent
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
import android.os.UserHandle
import android.os.VibrationEffect
import android.provider.Settings.Secure
@@ -40,6 +37,7 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.controls.ControlsMetricsLogger
+import com.android.systemui.controls.ControlsSettingsRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -68,17 +66,17 @@ class ControlActionCoordinatorImpl @Inject constructor(
private val vibrator: VibratorHelper,
private val secureSettings: SecureSettings,
private val userContextProvider: UserContextProvider,
- @Main mainHandler: Handler
+ private val controlsSettingsRepository: ControlsSettingsRepository,
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private var pendingAction: Action? = null
private var actionsInProgress = mutableSetOf<String>()
private val isLocked: Boolean
get() = !keyguardStateController.isUnlocked()
- private var mAllowTrivialControls: Boolean = secureSettings.getIntForUser(
- Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
- private var mShowDeviceControlsInLockscreen: Boolean = secureSettings.getIntForUser(
- Secure.LOCKSCREEN_SHOW_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
+ private val allowTrivialControls: Boolean
+ get() = controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen.value
+ private val showDeviceControlsInLockscreen: Boolean
+ get() = controlsSettingsRepository.canShowControlsInLockscreen.value
override lateinit var activityContext: Context
companion object {
@@ -86,38 +84,6 @@ class ControlActionCoordinatorImpl @Inject constructor(
private const val MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG = 2
}
- init {
- val lockScreenShowControlsUri =
- secureSettings.getUriFor(Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS)
- val showControlsUri =
- secureSettings.getUriFor(Secure.LOCKSCREEN_SHOW_CONTROLS)
- val controlsContentObserver = object : ContentObserver(mainHandler) {
- override fun onChange(selfChange: Boolean, uri: Uri?) {
- super.onChange(selfChange, uri)
- when (uri) {
- lockScreenShowControlsUri -> {
- mAllowTrivialControls = secureSettings.getIntForUser(
- Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
- 0, UserHandle.USER_CURRENT) != 0
- }
- showControlsUri -> {
- mShowDeviceControlsInLockscreen = secureSettings
- .getIntForUser(Secure.LOCKSCREEN_SHOW_CONTROLS,
- 0, UserHandle.USER_CURRENT) != 0
- }
- }
- }
- }
- secureSettings.registerContentObserverForUser(
- lockScreenShowControlsUri,
- false /* notifyForDescendants */, controlsContentObserver, UserHandle.USER_ALL
- )
- secureSettings.registerContentObserverForUser(
- showControlsUri,
- false /* notifyForDescendants */, controlsContentObserver, UserHandle.USER_ALL
- )
- }
-
override fun closeDialogs() {
dialog?.dismiss()
dialog = null
@@ -224,7 +190,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
@AnyThread
@VisibleForTesting
fun bouncerOrRun(action: Action) {
- val authRequired = action.authIsRequired || !mAllowTrivialControls
+ val authRequired = action.authIsRequired || !allowTrivialControls
if (keyguardStateController.isShowing() && authRequired) {
if (isLocked) {
@@ -282,7 +248,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
val attempts = prefs.getInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, 0)
if (attempts >= MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG ||
- (mShowDeviceControlsInLockscreen && mAllowTrivialControls)) {
+ (showDeviceControlsInLockscreen && allowTrivialControls)) {
return
}
val builder = AlertDialog
@@ -304,7 +270,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
true
}
- if (mShowDeviceControlsInLockscreen) {
+ if (showDeviceControlsInLockscreen) {
dialog = builder
.setTitle(R.string.controls_settings_trivial_controls_dialog_title)
.setMessage(R.string.controls_settings_trivial_controls_dialog_message)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index d60a22204b3d..3d8e4cb71aca 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -19,7 +19,6 @@ package com.android.systemui.dagger;
import android.content.BroadcastReceiver;
import com.android.systemui.GuestResetOrExitSessionReceiver;
-import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -106,15 +105,6 @@ public abstract class DefaultBroadcastReceiverBinder {
*/
@Binds
@IntoMap
- @ClassKey(GuestResumeSessionReceiver.class)
- public abstract BroadcastReceiver bindGuestResumeSessionReceiver(
- GuestResumeSessionReceiver broadcastReceiver);
-
- /**
- *
- */
- @Binds
- @IntoMap
@ClassKey(GuestResetOrExitSessionReceiver.class)
public abstract BroadcastReceiver bindGuestResetOrExitSessionReceiver(
GuestResetOrExitSessionReceiver broadcastReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 721c0ba4f865..0fbe0acb5642 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -36,6 +36,7 @@ import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderCoordinator
import com.android.systemui.power.PowerUI
+import com.android.systemui.reardisplay.RearDisplayDialogController
import com.android.systemui.recents.Recents
import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
@@ -243,4 +244,11 @@ abstract class SystemUICoreStartableModule {
@IntoMap
@ClassKey(ChipbarCoordinator::class)
abstract fun bindChipbarController(sysui: ChipbarCoordinator): CoreStartable
+
+
+ /** Inject into RearDisplayDialogController) */
+ @Binds
+ @IntoMap
+ @ClassKey(RearDisplayDialogController::class)
+ abstract fun bindRearDisplayDialogController(sysui: RearDisplayDialogController): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 95919c6b2c0d..b8e66735c740 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -33,6 +33,7 @@ import com.android.systemui.assist.AssistModule;
import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
import com.android.systemui.biometrics.dagger.BiometricsModule;
+import com.android.systemui.biometrics.dagger.UdfpsModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
import com.android.systemui.controls.dagger.ControlsModule;
@@ -156,6 +157,7 @@ import dagger.Provides;
TelephonyRepositoryModule.class,
TemporaryDisplayModule.class,
TunerModule.class,
+ UdfpsModule.class,
UserModule.class,
UtilModule.class,
NoteTaskModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 0b69b80689e0..5daf1ceaf592 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -29,12 +29,13 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.text.format.Formatter;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.view.Display;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
@@ -100,6 +101,7 @@ public class DozeTriggers implements DozeMachine.Part {
private final BroadcastDispatcher mBroadcastDispatcher;
private final AuthController mAuthController;
private final KeyguardStateController mKeyguardStateController;
+ private final UserTracker mUserTracker;
private final UiEventLogger mUiEventLogger;
private long mNotificationPulseTime;
@@ -110,6 +112,14 @@ public class DozeTriggers implements DozeMachine.Part {
private boolean mWantTouchScreenSensors;
private boolean mWantSensors;
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ mDozeSensors.onUserSwitched();
+ }
+ };
+
@VisibleForTesting
public enum DozingUpdateUiEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "Dozing updated due to notification.")
@@ -210,6 +220,7 @@ public class DozeTriggers implements DozeMachine.Part {
mAuthController = authController;
mUiEventLogger = uiEventLogger;
mKeyguardStateController = keyguardStateController;
+ mUserTracker = userTracker;
}
@Override
@@ -234,7 +245,7 @@ public class DozeTriggers implements DozeMachine.Part {
return;
}
mNotificationPulseTime = SystemClock.elapsedRealtime();
- if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
+ if (!mConfig.pulseOnNotificationEnabled(mUserTracker.getUserId())) {
runIfNotNull(onPulseSuppressedListener);
mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
return;
@@ -490,12 +501,14 @@ public class DozeTriggers implements DozeMachine.Part {
mBroadcastReceiver.register(mBroadcastDispatcher);
mDockManager.addListener(mDockEventListener);
mDozeHost.addCallback(mHostCallback);
+ mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
}
private void unregisterCallbacks() {
mBroadcastReceiver.unregister(mBroadcastDispatcher);
mDozeHost.removeCallback(mHostCallback);
mDockManager.removeListener(mDockEventListener);
+ mUserTracker.removeCallback(mUserChangedCallback);
}
private void stopListeningToAllTriggers() {
@@ -620,9 +633,6 @@ public class DozeTriggers implements DozeMachine.Part {
requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
null /* onPulseSuppressedListener */);
}
- if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- mDozeSensors.onUserSwitched();
- }
}
public void register(BroadcastDispatcher broadcastDispatcher) {
@@ -630,7 +640,6 @@ public class DozeTriggers implements DozeMachine.Part {
return;
}
IntentFilter filter = new IntentFilter(PULSE_ACTION);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
broadcastDispatcher.registerReceiver(this, filter);
mRegistered = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index 48159aed524e..46ce7a90f5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -192,9 +192,7 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll
break;
}
- // Add margin if specified by the complication. Otherwise add default margin
- // between complications.
- if (mLayoutParams.isMarginSpecified() || !isRoot) {
+ if (!isRoot) {
final int margin = mLayoutParams.getMargin(mDefaultMargin);
switch(direction) {
case ComplicationLayoutParams.DIRECTION_DOWN:
@@ -213,6 +211,19 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll
}
});
+ if (mLayoutParams.constraintSpecified()) {
+ switch (direction) {
+ case ComplicationLayoutParams.DIRECTION_START:
+ case ComplicationLayoutParams.DIRECTION_END:
+ params.matchConstraintMaxWidth = mLayoutParams.getConstraint();
+ break;
+ case ComplicationLayoutParams.DIRECTION_UP:
+ case ComplicationLayoutParams.DIRECTION_DOWN:
+ params.matchConstraintMaxHeight = mLayoutParams.getConstraint();
+ break;
+ }
+ }
+
mView.setLayoutParams(params);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
index 4fae68d57ee8..1755cb92da70 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
@@ -52,6 +52,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
private static final int LAST_POSITION = POSITION_END;
private static final int MARGIN_UNSPECIFIED = 0xFFFFFFFF;
+ private static final int CONSTRAINT_UNSPECIFIED = 0xFFFFFFFF;
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "DIRECTION_" }, value = {
@@ -81,6 +82,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
private final int mMargin;
+ private final int mConstraint;
+
private final boolean mSnapToGuide;
// Do not allow specifying opposite positions
@@ -110,7 +113,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
*/
public ComplicationLayoutParams(int width, int height, @Position int position,
@Direction int direction, int weight) {
- this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, false);
+ this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, CONSTRAINT_UNSPECIFIED,
+ false);
}
/**
@@ -127,7 +131,27 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
*/
public ComplicationLayoutParams(int width, int height, @Position int position,
@Direction int direction, int weight, int margin) {
- this(width, height, position, direction, weight, margin, false);
+ this(width, height, position, direction, weight, margin, CONSTRAINT_UNSPECIFIED, false);
+ }
+
+ /**
+ * Constructs a {@link ComplicationLayoutParams}.
+ * @param width The width {@link android.view.View.MeasureSpec} for the view.
+ * @param height The height {@link android.view.View.MeasureSpec} for the view.
+ * @param position The place within the parent container where the view should be positioned.
+ * @param direction The direction the view should be laid out from either the parent container
+ * or preceding view.
+ * @param weight The weight that should be considered for this view when compared to other
+ * views. This has an impact on the placement of the view but not the rendering of
+ * the view.
+ * @param margin The margin to apply between complications.
+ * @param constraint The max width or height the complication is allowed to spread, depending on
+ * its direction. For horizontal directions, this would be applied on width,
+ * and for vertical directions, height.
+ */
+ public ComplicationLayoutParams(int width, int height, @Position int position,
+ @Direction int direction, int weight, int margin, int constraint) {
+ this(width, height, position, direction, weight, margin, constraint, false);
}
/**
@@ -148,7 +172,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
*/
public ComplicationLayoutParams(int width, int height, @Position int position,
@Direction int direction, int weight, boolean snapToGuide) {
- this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, snapToGuide);
+ this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, CONSTRAINT_UNSPECIFIED,
+ snapToGuide);
}
/**
@@ -162,6 +187,9 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
* views. This has an impact on the placement of the view but not the rendering of
* the view.
* @param margin The margin to apply between complications.
+ * @param constraint The max width or height the complication is allowed to spread, depending on
+ * its direction. For horizontal directions, this would be applied on width,
+ * and for vertical directions, height.
* @param snapToGuide When set to {@code true}, the dimension perpendicular to the direction
* will be automatically set to align with a predetermined guide for that
* side. For example, if the complication is aligned to the top end and
@@ -169,7 +197,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
* from the end of the parent to the guide.
*/
public ComplicationLayoutParams(int width, int height, @Position int position,
- @Direction int direction, int weight, int margin, boolean snapToGuide) {
+ @Direction int direction, int weight, int margin, int constraint, boolean snapToGuide) {
super(width, height);
if (!validatePosition(position)) {
@@ -187,6 +215,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
mMargin = margin;
+ mConstraint = constraint;
+
mSnapToGuide = snapToGuide;
}
@@ -199,6 +229,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
mDirection = source.mDirection;
mWeight = source.mWeight;
mMargin = source.mMargin;
+ mConstraint = source.mConstraint;
mSnapToGuide = source.mSnapToGuide;
}
@@ -261,13 +292,6 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
}
/**
- * Returns whether margin has been specified by the complication.
- */
- public boolean isMarginSpecified() {
- return mMargin != MARGIN_UNSPECIFIED;
- }
-
- /**
* Returns the margin to apply between complications, or the given default if no margin is
* specified.
*/
@@ -276,6 +300,21 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
}
/**
+ * Returns whether the horizontal or vertical constraint has been specified.
+ */
+ public boolean constraintSpecified() {
+ return mConstraint != CONSTRAINT_UNSPECIFIED;
+ }
+
+ /**
+ * Returns the horizontal or vertical constraint of the complication, depending its direction.
+ * For horizontal directions, this is the max width, and for vertical directions, max height.
+ */
+ public int getConstraint() {
+ return mConstraint;
+ }
+
+ /**
* Returns whether the complication's dimension perpendicular to direction should be
* automatically set.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
index c01cf43eae74..ee0051220787 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
@@ -32,6 +32,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.CoreStartable;
+import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.controls.ControlsServiceInfo;
import com.android.systemui.controls.dagger.ControlsComponent;
@@ -151,7 +152,7 @@ public class DreamHomeControlsComplication implements Complication {
@Inject
DreamHomeControlsChipViewHolder(
DreamHomeControlsChipViewController dreamHomeControlsChipViewController,
- @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view,
+ @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) View view,
@Named(DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams
) {
mView = view;
@@ -174,7 +175,7 @@ public class DreamHomeControlsComplication implements Complication {
/**
* Controls behavior of the dream complication.
*/
- static class DreamHomeControlsChipViewController extends ViewController<ImageView> {
+ static class DreamHomeControlsChipViewController extends ViewController<View> {
private static final String TAG = "DreamHomeControlsCtrl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -203,7 +204,7 @@ public class DreamHomeControlsComplication implements Complication {
@Inject
DreamHomeControlsChipViewController(
- @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view,
+ @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) View view,
ActivityStarter activityStarter,
Context context,
ControlsComponent controlsComponent,
@@ -218,9 +219,10 @@ public class DreamHomeControlsComplication implements Complication {
@Override
protected void onViewAttached() {
- mView.setImageResource(mControlsComponent.getTileImageId());
- mView.setContentDescription(mContext.getString(mControlsComponent.getTileTitleId()));
- mView.setOnClickListener(this::onClickHomeControls);
+ final ImageView chip = mView.findViewById(R.id.home_controls_chip);
+ chip.setImageResource(mControlsComponent.getTileImageId());
+ chip.setContentDescription(mContext.getString(mControlsComponent.getTileTitleId()));
+ chip.setOnClickListener(this::onClickHomeControls);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java
index 7d9f1059f3b8..5290e44aa7fb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java
@@ -45,11 +45,12 @@ public interface DreamClockTimeComplicationModule {
@Provides
@Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW)
static View provideComplicationView(LayoutInflater layoutInflater) {
- final TextClock view = Preconditions.checkNotNull((TextClock)
+ final View view = Preconditions.checkNotNull(
layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time,
null, false),
"R.layout.dream_overlay_complication_clock_time did not properly inflated");
- view.setFontVariationSettings(TAG_WEIGHT + WEIGHT);
+ ((TextClock) view.findViewById(R.id.time_view)).setFontVariationSettings(
+ TAG_WEIGHT + WEIGHT);
return view;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java
index cf05d2d9cda0..a7aa97f74e31 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java
@@ -19,7 +19,7 @@ package com.android.systemui.dreams.complication.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import android.view.LayoutInflater;
-import android.widget.ImageView;
+import android.view.View;
import com.android.systemui.R;
import com.android.systemui.dreams.complication.DreamHomeControlsComplication;
@@ -74,8 +74,8 @@ public interface DreamHomeControlsComplicationComponent {
@Provides
@DreamHomeControlsComplicationScope
@Named(DREAM_HOME_CONTROLS_CHIP_VIEW)
- static ImageView provideHomeControlsChipView(LayoutInflater layoutInflater) {
- return (ImageView) layoutInflater.inflate(R.layout.dream_overlay_home_controls_chip,
+ static View provideHomeControlsChipView(LayoutInflater layoutInflater) {
+ return layoutInflater.inflate(R.layout.dream_overlay_home_controls_chip,
null, false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index a514c47f1fc1..9b954f5f0c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -47,10 +47,10 @@ public interface RegisteredComplicationsModule {
String DREAM_MEDIA_ENTRY_LAYOUT_PARAMS = "media_entry_layout_params";
int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT = 1;
- int DREAM_SMARTSPACE_COMPLICATION_WEIGHT = 0;
+ int DREAM_SMARTSPACE_COMPLICATION_WEIGHT = 2;
int DREAM_MEDIA_COMPLICATION_WEIGHT = 0;
- int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 2;
- int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 1;
+ int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 4;
+ int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 3;
/**
* Provides layout parameters for the clock time complication.
@@ -72,17 +72,14 @@ public interface RegisteredComplicationsModule {
*/
@Provides
@Named(DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS)
- static ComplicationLayoutParams provideHomeControlsChipLayoutParams(@Main Resources res) {
+ static ComplicationLayoutParams provideHomeControlsChipLayoutParams() {
return new ComplicationLayoutParams(
- res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
- res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_UP,
- DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT,
- // Add margin to the bottom of home controls to horizontally align with smartspace.
- res.getDimensionPixelSize(
- R.dimen.dream_overlay_complication_home_controls_padding));
+ ComplicationLayoutParams.DIRECTION_END,
+ DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
}
/**
@@ -106,12 +103,14 @@ public interface RegisteredComplicationsModule {
@Provides
@Named(DREAM_SMARTSPACE_LAYOUT_PARAMS)
static ComplicationLayoutParams provideSmartspaceLayoutParams(@Main Resources res) {
- return new ComplicationLayoutParams(0,
+ return new ComplicationLayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
ComplicationLayoutParams.DIRECTION_END,
DREAM_SMARTSPACE_COMPLICATION_WEIGHT,
- res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding));
+ res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding),
+ res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_max_width));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index a70b791691ed..7c05356dd5fc 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -164,6 +164,13 @@ object Flags {
// TODO(b/256513609): Tracking Bug
@JvmField val ACTIVE_UNLOCK_CHIPBAR = releasedFlag(217, "active_unlock_chipbar")
+ /**
+ * Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the
+ * new KeyguardTransitionRepository.
+ */
+ @JvmField
+ val LIGHT_REVEAL_MIGRATION = unreleasedFlag(218, "light_reveal_migration", teamfood = true)
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -207,15 +214,6 @@ object Flags {
unreleasedFlag(508, "qs_secondary_data_sub_info", teamfood = true)
// 600- status bar
- // TODO(b/254512623): Tracking Bug
- @Deprecated("Replaced by mobile and wifi specific flags.")
- val NEW_STATUS_BAR_PIPELINE_BACKEND =
- unreleasedFlag(604, "new_status_bar_pipeline_backend", teamfood = false)
-
- // TODO(b/254512660): Tracking Bug
- @Deprecated("Replaced by mobile and wifi specific flags.")
- val NEW_STATUS_BAR_PIPELINE_FRONTEND =
- unreleasedFlag(605, "new_status_bar_pipeline_frontend", teamfood = false)
// TODO(b/256614753): Tracking Bug
val NEW_STATUS_BAR_MOBILE_ICONS = unreleasedFlag(606, "new_status_bar_mobile_icons")
@@ -233,6 +231,10 @@ object Flags {
// TODO(b/256623670): Tracking Bug
@JvmField val BATTERY_SHIELD_ICON = unreleasedFlag(610, "battery_shield_icon")
+ // TODO(b/260881289): Tracking Bug
+ val NEW_STATUS_BAR_ICONS_DEBUG_COLORING =
+ unreleasedFlag(611, "new_status_bar_icons_debug_coloring")
+
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag(700, "ongoing_call_status_bar_chip")
@@ -348,6 +350,12 @@ object Flags {
// TODO(b/256873975): Tracking Bug
@JvmField @Keep val WM_BUBBLE_BAR = unreleasedFlag(1111, "wm_bubble_bar")
+ // TODO(b/260271148): Tracking bug
+ @Keep
+ @JvmField
+ val WM_DESKTOP_WINDOWING_2 =
+ sysPropBooleanFlag(1112, "persist.wm.debug.desktop_mode_2", default = false)
+
// 1200 - predictive back
@Keep
@JvmField
@@ -368,6 +376,11 @@ object Flags {
@JvmField
val NEW_BACK_AFFORDANCE = unreleasedFlag(1203, "new_back_affordance", teamfood = false)
+ // TODO(b/255854141): Tracking Bug
+ @JvmField
+ val WM_ENABLE_PREDICTIVE_BACK_SYSUI =
+ unreleasedFlag(1204, "persist.wm.debug.predictive_back_sysui_enable", teamfood = true)
+
// 1300 - screenshots
// TODO(b/254512719): Tracking Bug
@JvmField val SCREENSHOT_REQUEST_PROCESSOR = releasedFlag(1300, "screenshot_request_processor")
@@ -392,17 +405,15 @@ object Flags {
// 1700 - clipboard
@JvmField val CLIPBOARD_OVERLAY_REFACTOR = releasedFlag(1700, "clipboard_overlay_refactor")
- @JvmField
- val CLIPBOARD_REMOTE_BEHAVIOR =
- unreleasedFlag(1701, "clipboard_remote_behavior", teamfood = true)
+ @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior")
// 1800 - shade container
@JvmField
val LEAVE_SHADE_OPEN_FOR_BUGREPORT =
unreleasedFlag(1800, "leave_shade_open_for_bugreport", teamfood = true)
- // 1900 - note task
- @JvmField val NOTE_TASKS = sysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
+ // 1900
+ @JvmField val NOTE_TASKS = unreleasedFlag(1900, "keycode_flag")
// 2000 - device controls
@Keep @JvmField val USE_APP_PANELS = unreleasedFlag(2000, "use_app_panels", teamfood = true)
@@ -419,6 +430,12 @@ object Flags {
// 2300 - stylus
@JvmField val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used")
+ // 2400 - performance tools and debugging info
+ // TODO(b/238923086): Tracking Bug
+ @JvmField
+ val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
+ unreleasedFlag(2400, "warn_on_blocking_binder_transactions")
+
// TODO(b259590361): Tracking bug
val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
index 29febb6dd0d9..4ae37c51f278 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
@@ -29,7 +29,7 @@ import android.util.Log
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
-import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
import javax.inject.Inject
import kotlinx.coroutines.runBlocking
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 8846bbd4754d..c332a0d66294 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -249,6 +249,7 @@ public class KeyguardService extends Service {
synchronized (mFinishCallbacks) {
if (mFinishCallbacks.remove(transition) == null) return;
}
+ info.releaseAllSurfaces();
Slog.d(TAG, "Finish IRemoteAnimationRunner.");
finishCallback.onTransitionFinished(null /* wct */, null /* t */);
}
@@ -264,6 +265,8 @@ public class KeyguardService extends Service {
synchronized (mFinishCallbacks) {
origFinishCB = mFinishCallbacks.remove(transition);
}
+ info.releaseAllSurfaces();
+ t.close();
if (origFinishCB == null) {
// already finished (or not started yet), so do nothing.
return;
@@ -459,12 +462,15 @@ public class KeyguardService extends Service {
t.apply();
mBinder.setOccluded(true /* isOccluded */, true /* animate */);
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ info.releaseAllSurfaces();
}
@Override
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishCallback) {
+ t.close();
+ info.releaseAllSurfaces();
}
};
@@ -476,12 +482,15 @@ public class KeyguardService extends Service {
t.apply();
mBinder.setOccluded(false /* isOccluded */, true /* animate */);
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ info.releaseAllSurfaces();
}
@Override
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishCallback) {
+ t.close();
+ info.releaseAllSurfaces();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8403fe650687..6ed555056cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -869,7 +869,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void onLaunchAnimationEnd(boolean launchIsFullScreen) {
if (launchIsFullScreen) {
- mCentralSurfaces.instantCollapseNotificationPanel();
+ mShadeController.get().instantCollapseShade();
}
mOccludeAnimationPlaying = false;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index f5220b8fae92..73dbeab3030b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -25,6 +25,7 @@ package com.android.systemui.keyguard.data.quickaffordance
object BuiltInKeyguardQuickAffordanceKeys {
// Please keep alphabetical order of const names to simplify future maintenance.
const val CAMERA = "camera"
+ const val FLASHLIGHT = "flashlight"
const val HOME_CONTROLS = "home"
const val QR_CODE_SCANNER = "qr_code_scanner"
const val QUICK_ACCESS_WALLET = "wallet"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index 3c09aab60443..dbc376e62950 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -26,14 +26,17 @@ import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import dagger.Lazy
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
-import javax.inject.Inject
@SysUISingleton
-class CameraQuickAffordanceConfig @Inject constructor(
- @Application private val context: Context,
- private val cameraGestureHelper: CameraGestureHelper,
+class CameraQuickAffordanceConfig
+@Inject
+constructor(
+ @Application private val context: Context,
+ private val cameraGestureHelper: Lazy<CameraGestureHelper>,
) : KeyguardQuickAffordanceConfig {
override val key: String
@@ -46,17 +49,23 @@ class CameraQuickAffordanceConfig @Inject constructor(
get() = com.android.internal.R.drawable.perm_group_camera
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
- get() = flowOf(
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- icon = Icon.Resource(
+ get() =
+ flowOf(
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ icon =
+ Icon.Resource(
com.android.internal.R.drawable.perm_group_camera,
ContentDescription.Resource(R.string.accessibility_camera_button)
- )
+ )
+ )
)
- )
- override fun onTriggered(expandable: Expandable?): KeyguardQuickAffordanceConfig.OnTriggeredResult {
- cameraGestureHelper.launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
+ override fun onTriggered(
+ expandable: Expandable?
+ ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+ cameraGestureHelper
+ .get()
+ .launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
new file mode 100644
index 000000000000..62fe80a82908
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.Context
+import com.android.systemui.R
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.statusbar.policy.FlashlightController
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class FlashlightQuickAffordanceConfig
+@Inject
+constructor(
+ @Application private val context: Context,
+ private val flashlightController: FlashlightController,
+) : KeyguardQuickAffordanceConfig {
+
+ private sealed class FlashlightState {
+
+ abstract fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState
+
+ object On : FlashlightState() {
+ override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ Icon.Resource(
+ R.drawable.qs_flashlight_icon_on,
+ ContentDescription.Resource(R.string.quick_settings_flashlight_label)
+ ),
+ ActivationState.Active
+ )
+ }
+
+ object OffAvailable : FlashlightState() {
+ override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ Icon.Resource(
+ R.drawable.qs_flashlight_icon_off,
+ ContentDescription.Resource(R.string.quick_settings_flashlight_label)
+ ),
+ ActivationState.Inactive
+ )
+ }
+
+ object Unavailable : FlashlightState() {
+ override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
+ KeyguardQuickAffordanceConfig.LockScreenState.Hidden
+ }
+ }
+
+ override val key: String
+ get() = BuiltInKeyguardQuickAffordanceKeys.FLASHLIGHT
+
+ override val pickerName: String
+ get() = context.getString(R.string.quick_settings_flashlight_label)
+
+ override val pickerIconResourceId: Int
+ get() = R.drawable.ic_flashlight_off
+
+ override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
+ conflatedCallbackFlow {
+ val flashlightCallback =
+ object : FlashlightController.FlashlightListener {
+ override fun onFlashlightChanged(enabled: Boolean) {
+ trySendWithFailureLogging(
+ if (enabled) {
+ FlashlightState.On.toLockScreenState()
+ } else {
+ FlashlightState.OffAvailable.toLockScreenState()
+ },
+ TAG
+ )
+ }
+
+ override fun onFlashlightError() {
+ trySendWithFailureLogging(
+ FlashlightState.OffAvailable.toLockScreenState(),
+ TAG
+ )
+ }
+
+ override fun onFlashlightAvailabilityChanged(available: Boolean) {
+ trySendWithFailureLogging(
+ if (!available) {
+ FlashlightState.Unavailable.toLockScreenState()
+ } else {
+ if (flashlightController.isEnabled) {
+ FlashlightState.On.toLockScreenState()
+ } else {
+ FlashlightState.OffAvailable.toLockScreenState()
+ }
+ },
+ TAG
+ )
+ }
+ }
+
+ flashlightController.addCallback(flashlightCallback)
+
+ awaitClose { flashlightController.removeCallback(flashlightCallback) }
+ }
+
+ override fun onTriggered(
+ expandable: Expandable?
+ ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+ flashlightController.setFlashlight(
+ flashlightController.isAvailable && !flashlightController.isEnabled
+ )
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ }
+
+ override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
+ if (flashlightController.isAvailable) {
+ KeyguardQuickAffordanceConfig.PickerScreenState.Default
+ } else {
+ KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
+ }
+
+ companion object {
+ private const val TAG = "FlashlightQuickAffordanceConfig"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index f7225a249eda..072cfb140e62 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -17,25 +17,35 @@
package com.android.systemui.keyguard.data.quickaffordance
+import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.ElementsIntoSet
@Module
-object KeyguardDataQuickAffordanceModule {
- @Provides
- @ElementsIntoSet
- fun quickAffordanceConfigs(
- home: HomeControlsKeyguardQuickAffordanceConfig,
- quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
- qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
- camera: CameraQuickAffordanceConfig,
- ): Set<KeyguardQuickAffordanceConfig> {
- return setOf(
- camera,
- home,
- quickAccessWallet,
- qrCodeScanner,
- )
+interface KeyguardDataQuickAffordanceModule {
+ @Binds
+ fun providerClientFactory(
+ impl: KeyguardQuickAffordanceProviderClientFactoryImpl,
+ ): KeyguardQuickAffordanceProviderClientFactory
+
+ companion object {
+ @Provides
+ @ElementsIntoSet
+ fun quickAffordanceConfigs(
+ flashlight: FlashlightQuickAffordanceConfig,
+ home: HomeControlsKeyguardQuickAffordanceConfig,
+ quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
+ qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
+ camera: CameraQuickAffordanceConfig,
+ ): Set<KeyguardQuickAffordanceConfig> {
+ return setOf(
+ camera,
+ flashlight,
+ home,
+ quickAccessWallet,
+ qrCodeScanner,
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 4477310dca41..98b1a731b82c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -21,7 +21,7 @@ import android.content.Intent
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
-import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
import kotlinx.coroutines.flow.Flow
/** Defines interface that can act as data source for a single quick affordance model. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt
index 766096f1fa2b..72747f68bbbd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt
@@ -67,7 +67,7 @@ constructor(
@Application private val scope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val secureSettings: SecureSettings,
- private val selectionsManager: KeyguardQuickAffordanceSelectionManager,
+ private val selectionsManager: KeyguardQuickAffordanceLocalUserSelectionManager,
) {
companion object {
private val BINDINGS =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
new file mode 100644
index 000000000000..006678546de8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -0,0 +1,184 @@
+/*
+ * 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.keyguard.data.quickaffordance
+
+import android.content.Context
+import android.content.IntentFilter
+import android.content.SharedPreferences
+import com.android.systemui.R
+import com.android.systemui.backup.BackupHelper
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.onStart
+
+/**
+ * Manages and provides access to the current "selections" of keyguard quick affordances, answering
+ * the question "which affordances should the keyguard show?" for the user associated with the
+ * System UI process.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class KeyguardQuickAffordanceLocalUserSelectionManager
+@Inject
+constructor(
+ @Application context: Context,
+ private val userFileManager: UserFileManager,
+ private val userTracker: UserTracker,
+ broadcastDispatcher: BroadcastDispatcher,
+) : KeyguardQuickAffordanceSelectionManager {
+
+ private var sharedPrefs: SharedPreferences = instantiateSharedPrefs()
+
+ private val userId: Flow<Int> = conflatedCallbackFlow {
+ val callback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ trySendWithFailureLogging(newUser, TAG)
+ }
+ }
+
+ userTracker.addCallback(callback) { it.run() }
+ trySendWithFailureLogging(userTracker.userId, TAG)
+
+ awaitClose { userTracker.removeCallback(callback) }
+ }
+
+ private val defaults: Map<String, List<String>> by lazy {
+ context.resources
+ .getStringArray(R.array.config_keyguardQuickAffordanceDefaults)
+ .associate { item ->
+ val splitUp = item.split(SLOT_AFFORDANCES_DELIMITER)
+ check(splitUp.size == 2)
+ val slotId = splitUp[0]
+ val affordanceIds = splitUp[1].split(AFFORDANCE_DELIMITER)
+ slotId to affordanceIds
+ }
+ }
+
+ /**
+ * Emits an event each time a Backup & Restore restoration job is completed. Does not emit an
+ * initial value.
+ */
+ private val backupRestorationEvents: Flow<Unit> =
+ broadcastDispatcher.broadcastFlow(
+ filter = IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
+ flags = Context.RECEIVER_NOT_EXPORTED,
+ permission = BackupHelper.PERMISSION_SELF,
+ )
+
+ override val selections: Flow<Map<String, List<String>>> =
+ combine(
+ userId,
+ backupRestorationEvents.onStart {
+ // We emit an initial event to make sure that the combine emits at least once,
+ // even if we never get a Backup & Restore restoration event (which is the most
+ // common case anyway as restoration really only happens on initial device
+ // setup).
+ emit(Unit)
+ }
+ ) { _, _ -> }
+ .flatMapLatest {
+ conflatedCallbackFlow {
+ // We want to instantiate a new SharedPreferences instance each time either the
+ // user ID changes or we have a backup & restore restoration event. The reason
+ // is that our sharedPrefs instance needs to be replaced with a new one as it
+ // depends on the user ID and when the B&R job completes, the backing file is
+ // replaced but the existing instance still has a stale in-memory cache.
+ sharedPrefs = instantiateSharedPrefs()
+
+ val listener =
+ SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
+ trySend(getSelections())
+ }
+
+ sharedPrefs.registerOnSharedPreferenceChangeListener(listener)
+ send(getSelections())
+
+ awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) }
+ }
+ }
+
+ override fun getSelections(): Map<String, List<String>> {
+ val slotKeys = sharedPrefs.all.keys.filter { it.startsWith(KEY_PREFIX_SLOT) }
+ val result =
+ slotKeys
+ .associate { key ->
+ val slotId = key.substring(KEY_PREFIX_SLOT.length)
+ val value = sharedPrefs.getString(key, null)
+ val affordanceIds =
+ if (!value.isNullOrEmpty()) {
+ value.split(AFFORDANCE_DELIMITER)
+ } else {
+ emptyList()
+ }
+ slotId to affordanceIds
+ }
+ .toMutableMap()
+
+ // If the result map is missing keys, it means that the system has never set anything for
+ // those slots. This is where we need examine our defaults and see if there should be a
+ // default value for the affordances in the slot IDs that are missing from the result.
+ //
+ // Once the user makes any selection for a slot, even when they select "None", this class
+ // will persist a key for that slot ID. In the case of "None", it will have a value of the
+ // empty string. This is why this system works.
+ defaults.forEach { (slotId, affordanceIds) ->
+ if (!result.containsKey(slotId)) {
+ result[slotId] = affordanceIds
+ }
+ }
+
+ return result
+ }
+
+ override fun setSelections(
+ slotId: String,
+ affordanceIds: List<String>,
+ ) {
+ val key = "$KEY_PREFIX_SLOT$slotId"
+ val value = affordanceIds.joinToString(AFFORDANCE_DELIMITER)
+ sharedPrefs.edit().putString(key, value).apply()
+ }
+
+ private fun instantiateSharedPrefs(): SharedPreferences {
+ return userFileManager.getSharedPreferences(
+ FILE_NAME,
+ Context.MODE_PRIVATE,
+ userTracker.userId,
+ )
+ }
+
+ companion object {
+ private const val TAG = "KeyguardQuickAffordancePrimaryUserSelectionManager"
+ const val FILE_NAME = "quick_affordance_selections"
+ private const val KEY_PREFIX_SLOT = "slot_"
+ private const val SLOT_AFFORDANCES_DELIMITER = ":"
+ private const val AFFORDANCE_DELIMITER = ","
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
new file mode 100644
index 000000000000..727a81391dc2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClientImpl
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+
+interface KeyguardQuickAffordanceProviderClientFactory {
+ fun create(): KeyguardQuickAffordanceProviderClient
+}
+
+class KeyguardQuickAffordanceProviderClientFactoryImpl
+@Inject
+constructor(
+ private val userTracker: UserTracker,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : KeyguardQuickAffordanceProviderClientFactory {
+ override fun create(): KeyguardQuickAffordanceProviderClient {
+ return KeyguardQuickAffordanceProviderClientImpl(
+ context = userTracker.userContext,
+ backgroundDispatcher = backgroundDispatcher,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
new file mode 100644
index 000000000000..8ffef25d3aae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.Context
+import android.os.UserHandle
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+import javax.inject.Inject
+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.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/**
+ * Manages and provides access to the current "selections" of keyguard quick affordances, answering
+ * the question "which affordances should the keyguard show?" for users associated with other System
+ * UI processes.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class KeyguardQuickAffordanceRemoteUserSelectionManager
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val userTracker: UserTracker,
+ private val clientFactory: KeyguardQuickAffordanceProviderClientFactory,
+ private val userHandle: UserHandle,
+) : KeyguardQuickAffordanceSelectionManager {
+
+ private val userId: Flow<Int> = conflatedCallbackFlow {
+ val callback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ trySendWithFailureLogging(newUser, TAG)
+ }
+ }
+
+ userTracker.addCallback(callback) { it.run() }
+ trySendWithFailureLogging(userTracker.userId, TAG)
+
+ awaitClose { userTracker.removeCallback(callback) }
+ }
+
+ private val clientOrNull: StateFlow<KeyguardQuickAffordanceProviderClient?> =
+ userId
+ .distinctUntilChanged()
+ .map { selectedUserId ->
+ if (userHandle.isSystem && userHandle.identifier != selectedUserId) {
+ clientFactory.create()
+ } else {
+ null
+ }
+ }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = null,
+ )
+
+ private val _selections: StateFlow<Map<String, List<String>>> =
+ clientOrNull
+ .flatMapLatest { client ->
+ client?.observeSelections()?.map { selections ->
+ buildMap<String, List<String>> {
+ selections.forEach { selection ->
+ val slotId = selection.slotId
+ val affordanceIds = (get(slotId) ?: emptyList()).toMutableList()
+ affordanceIds.add(selection.affordanceId)
+ put(slotId, affordanceIds)
+ }
+ }
+ }
+ ?: emptyFlow()
+ }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = emptyMap(),
+ )
+
+ override val selections: Flow<Map<String, List<String>>> = _selections
+
+ override fun getSelections(): Map<String, List<String>> {
+ return _selections.value
+ }
+
+ override fun setSelections(slotId: String, affordanceIds: List<String>) {
+ clientOrNull.value?.let { client ->
+ scope.launch {
+ client.deleteAllSelections(slotId = slotId)
+ affordanceIds.forEach { affordanceId ->
+ client.insertSelection(slotId = slotId, affordanceId = affordanceId)
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "KeyguardQuickAffordanceMultiUserSelectionManager"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt
index b29cf45cc709..21fffede5f97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt
@@ -17,119 +17,22 @@
package com.android.systemui.keyguard.data.quickaffordance
-import android.content.Context
-import android.content.SharedPreferences
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.R
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.settings.UserFileManager
-import com.android.systemui.settings.UserTracker
-import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flatMapLatest
/**
- * Manages and provides access to the current "selections" of keyguard quick affordances, answering
- * the question "which affordances should the keyguard show?".
+ * Defines interface for classes that manage and provide access to the current "selections" of
+ * keyguard quick affordances, answering the question "which affordances should the keyguard show?".
*/
-@SysUISingleton
-class KeyguardQuickAffordanceSelectionManager
-@Inject
-constructor(
- @Application context: Context,
- private val userFileManager: UserFileManager,
- private val userTracker: UserTracker,
-) {
-
- private val sharedPrefs: SharedPreferences
- get() =
- userFileManager.getSharedPreferences(
- FILE_NAME,
- Context.MODE_PRIVATE,
- userTracker.userId,
- )
-
- private val userId: Flow<Int> = conflatedCallbackFlow {
- val callback =
- object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- trySendWithFailureLogging(newUser, TAG)
- }
- }
-
- userTracker.addCallback(callback) { it.run() }
- trySendWithFailureLogging(userTracker.userId, TAG)
-
- awaitClose { userTracker.removeCallback(callback) }
- }
- private val defaults: Map<String, List<String>> by lazy {
- context.resources
- .getStringArray(R.array.config_keyguardQuickAffordanceDefaults)
- .associate { item ->
- val splitUp = item.split(SLOT_AFFORDANCES_DELIMITER)
- check(splitUp.size == 2)
- val slotId = splitUp[0]
- val affordanceIds = splitUp[1].split(AFFORDANCE_DELIMITER)
- slotId to affordanceIds
- }
- }
+interface KeyguardQuickAffordanceSelectionManager {
/** IDs of affordances to show, indexed by slot ID, and sorted in descending priority order. */
- val selections: Flow<Map<String, List<String>>> =
- userId.flatMapLatest {
- conflatedCallbackFlow {
- val listener =
- SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
- trySend(getSelections())
- }
-
- sharedPrefs.registerOnSharedPreferenceChangeListener(listener)
- send(getSelections())
-
- awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) }
- }
- }
+ val selections: Flow<Map<String, List<String>>>
/**
* Returns a snapshot of the IDs of affordances to show, indexed by slot ID, and sorted in
* descending priority order.
*/
- fun getSelections(): Map<String, List<String>> {
- val slotKeys = sharedPrefs.all.keys.filter { it.startsWith(KEY_PREFIX_SLOT) }
- val result =
- slotKeys
- .associate { key ->
- val slotId = key.substring(KEY_PREFIX_SLOT.length)
- val value = sharedPrefs.getString(key, null)
- val affordanceIds =
- if (!value.isNullOrEmpty()) {
- value.split(AFFORDANCE_DELIMITER)
- } else {
- emptyList()
- }
- slotId to affordanceIds
- }
- .toMutableMap()
-
- // If the result map is missing keys, it means that the system has never set anything for
- // those slots. This is where we need examine our defaults and see if there should be a
- // default value for the affordances in the slot IDs that are missing from the result.
- //
- // Once the user makes any selection for a slot, even when they select "None", this class
- // will persist a key for that slot ID. In the case of "None", it will have a value of the
- // empty string. This is why this system works.
- defaults.forEach { (slotId, affordanceIds) ->
- if (!result.containsKey(slotId)) {
- result[slotId] = affordanceIds
- }
- }
-
- return result
- }
+ fun getSelections(): Map<String, List<String>>
/**
* Updates the IDs of affordances to show at the slot with the given ID. The order of affordance
@@ -138,17 +41,9 @@ constructor(
fun setSelections(
slotId: String,
affordanceIds: List<String>,
- ) {
- val key = "$KEY_PREFIX_SLOT$slotId"
- val value = affordanceIds.joinToString(AFFORDANCE_DELIMITER)
- sharedPrefs.edit().putString(key, value).apply()
- }
+ )
companion object {
- private const val TAG = "KeyguardQuickAffordanceSelectionManager"
- @VisibleForTesting const val FILE_NAME = "quick_affordance_selections"
- private const val KEY_PREFIX_SLOT = "slot_"
- private const val SLOT_AFFORDANCES_DELIMITER = ":"
- private const val AFFORDANCE_DELIMITER = ","
+ const val FILE_NAME = "quick_affordance_selections"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index d95a1a726bf5..e3f5e90b2300 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -18,45 +18,93 @@
package com.android.systemui.keyguard.data.repository
import android.content.Context
+import android.os.UserHandle
import com.android.systemui.Dumpable
import com.android.systemui.R
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
+import com.android.systemui.settings.UserTracker
import java.io.PrintWriter
import javax.inject.Inject
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.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** Abstracts access to application state related to keyguard quick affordances. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class KeyguardQuickAffordanceRepository
@Inject
constructor(
@Application private val appContext: Context,
@Application private val scope: CoroutineScope,
- private val selectionManager: KeyguardQuickAffordanceSelectionManager,
+ private val localUserSelectionManager: KeyguardQuickAffordanceLocalUserSelectionManager,
+ private val remoteUserSelectionManager: KeyguardQuickAffordanceRemoteUserSelectionManager,
+ private val userTracker: UserTracker,
legacySettingSyncer: KeyguardQuickAffordanceLegacySettingSyncer,
private val configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>,
dumpManager: DumpManager,
+ userHandle: UserHandle,
) {
+ private val userId: Flow<Int> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ trySendWithFailureLogging(newUser, TAG)
+ }
+ }
+
+ userTracker.addCallback(callback) { it.run() }
+ trySendWithFailureLogging(userTracker.userId, TAG)
+
+ awaitClose { userTracker.removeCallback(callback) }
+ }
+
+ private val selectionManager: StateFlow<KeyguardQuickAffordanceSelectionManager> =
+ userId
+ .distinctUntilChanged()
+ .map { selectedUserId ->
+ if (userHandle.identifier == selectedUserId) {
+ localUserSelectionManager
+ } else {
+ remoteUserSelectionManager
+ }
+ }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = localUserSelectionManager,
+ )
+
/**
* List of [KeyguardQuickAffordanceConfig] instances of the affordances at the slot with the
* given ID. The configs are sorted in descending priority order.
*/
val selections: StateFlow<Map<String, List<KeyguardQuickAffordanceConfig>>> =
- selectionManager.selections
- .map { selectionsBySlotId ->
- selectionsBySlotId.mapValues { (_, selections) ->
- configs.filter { selections.contains(it.key) }
+ selectionManager
+ .flatMapLatest { selectionManager ->
+ selectionManager.selections.map { selectionsBySlotId ->
+ selectionsBySlotId.mapValues { (_, selections) ->
+ configs.filter { selections.contains(it.key) }
+ }
}
}
.stateIn(
@@ -99,7 +147,7 @@ constructor(
* slot with the given ID. The configs are sorted in descending priority order.
*/
fun getSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
- val selections = selectionManager.getSelections().getOrDefault(slotId, emptyList())
+ val selections = selectionManager.value.getSelections().getOrDefault(slotId, emptyList())
return configs.filter { selections.contains(it.key) }
}
@@ -108,7 +156,7 @@ constructor(
* are sorted in descending priority order.
*/
fun getSelections(): Map<String, List<String>> {
- return selectionManager.getSelections()
+ return selectionManager.value.getSelections()
}
/**
@@ -119,7 +167,7 @@ constructor(
slotId: String,
affordanceIds: List<String>,
) {
- selectionManager.setSelections(
+ selectionManager.value.setSelections(
slotId = slotId,
affordanceIds = affordanceIds,
)
@@ -188,6 +236,7 @@ constructor(
}
companion object {
+ private const val TAG = "KeyguardQuickAffordanceRepository"
private const val SLOT_CONFIG_DELIMITER = ":"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 796f2b44effc..148792ba779b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -16,8 +16,11 @@
package com.android.systemui.keyguard.data.repository
+import android.graphics.Point
+import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
@@ -27,8 +30,8 @@ import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -88,8 +91,8 @@ interface KeyguardRepository {
* enter to conserve battery when the device is locked and inactive.
*
* Note that it is possible for the system to be transitioning into doze while this flow still
- * returns `false`. In order to account for that, observers should also use the [dozeAmount]
- * flow to check if it's greater than `0`
+ * returns `false`. In order to account for that, observers should also use the
+ * [linearDozeAmount] flow to check if it's greater than `0`
*/
val isDozing: Flow<Boolean>
@@ -111,7 +114,7 @@ interface KeyguardRepository {
* happens during an animation/transition into doze mode. An observer would be wise to account
* for both flows if needed.
*/
- val dozeAmount: Flow<Float>
+ val linearDozeAmount: Flow<Float>
/** Doze state information, as it transitions */
val dozeTransitionModel: Flow<DozeTransitionModel>
@@ -120,11 +123,20 @@ interface KeyguardRepository {
val statusBarState: Flow<StatusBarState>
/** Observable for device wake/sleep state */
- val wakefulnessState: Flow<WakefulnessModel>
+ val wakefulness: Flow<WakefulnessModel>
/** Observable for biometric unlock modes */
val biometricUnlockState: Flow<BiometricUnlockModel>
+ /** Approximate location on the screen of the fingerprint sensor. */
+ val fingerprintSensorLocation: Flow<Point?>
+
+ /** Approximate location on the screen of the face unlock sensor/front facing camera. */
+ val faceSensorLocation: Flow<Point?>
+
+ /** Source of the most recent biometric unlock, such as fingerprint or face. */
+ val biometricUnlockSource: Flow<BiometricUnlockSource?>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -163,6 +175,7 @@ constructor(
private val keyguardStateController: KeyguardStateController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dozeTransitionListener: DozeTransitionListener,
+ private val authController: AuthController,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -281,11 +294,11 @@ constructor(
}
.distinctUntilChanged()
- override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
+ override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
val callback =
object : StatusBarStateController.StateListener {
override fun onDozeAmountChanged(linear: Float, eased: Float) {
- trySendWithFailureLogging(eased, TAG, "updated dozeAmount")
+ trySendWithFailureLogging(linear, TAG, "updated dozeAmount")
}
}
@@ -348,56 +361,137 @@ constructor(
awaitClose { statusBarStateController.removeCallback(callback) }
}
- override val wakefulnessState: Flow<WakefulnessModel> = conflatedCallbackFlow {
+ override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
+ fun dispatchUpdate() {
+ trySendWithFailureLogging(
+ biometricModeIntToObject(biometricUnlockController.mode),
+ TAG,
+ "biometric mode"
+ )
+ }
+
val callback =
+ object : BiometricUnlockController.BiometricModeListener {
+ override fun onModeChanged(@WakeAndUnlockMode mode: Int) {
+ dispatchUpdate()
+ }
+
+ override fun onResetMode() {
+ dispatchUpdate()
+ }
+ }
+
+ biometricUnlockController.addBiometricModeListener(callback)
+ dispatchUpdate()
+
+ awaitClose { biometricUnlockController.removeBiometricModeListener(callback) }
+ }
+
+ override val wakefulness: Flow<WakefulnessModel> = conflatedCallbackFlow {
+ val observer =
object : WakefulnessLifecycle.Observer {
override fun onStartedWakingUp() {
- trySendWithFailureLogging(
- WakefulnessModel.STARTING_TO_WAKE,
- TAG,
- "Wakefulness: starting to wake"
- )
+ dispatchNewState()
}
+
override fun onFinishedWakingUp() {
- trySendWithFailureLogging(WakefulnessModel.AWAKE, TAG, "Wakefulness: awake")
+ dispatchNewState()
+ }
+
+ override fun onPostFinishedWakingUp() {
+ dispatchNewState()
}
+
override fun onStartedGoingToSleep() {
+ dispatchNewState()
+ }
+
+ override fun onFinishedGoingToSleep() {
+ dispatchNewState()
+ }
+
+ private fun dispatchNewState() {
trySendWithFailureLogging(
- WakefulnessModel.STARTING_TO_SLEEP,
+ WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
TAG,
- "Wakefulness: starting to sleep"
+ "updated wakefulness state"
)
}
- override fun onFinishedGoingToSleep() {
- trySendWithFailureLogging(WakefulnessModel.ASLEEP, TAG, "Wakefulness: asleep")
- }
}
- wakefulnessLifecycle.addObserver(callback)
+
+ wakefulnessLifecycle.addObserver(observer)
trySendWithFailureLogging(
- wakefulnessIntToObject(wakefulnessLifecycle.getWakefulness()),
+ WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
TAG,
"initial wakefulness state"
)
- awaitClose { wakefulnessLifecycle.removeObserver(callback) }
+ awaitClose { wakefulnessLifecycle.removeObserver(observer) }
}
- override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
+ override val fingerprintSensorLocation: Flow<Point?> = conflatedCallbackFlow {
+ fun sendFpLocation() {
+ trySendWithFailureLogging(
+ authController.fingerprintSensorLocation,
+ TAG,
+ "AuthController.Callback#onFingerprintLocationChanged"
+ )
+ }
+
val callback =
- object : BiometricUnlockController.BiometricModeListener {
- override fun onModeChanged(@WakeAndUnlockMode mode: Int) {
- trySendWithFailureLogging(biometricModeIntToObject(mode), TAG, "biometric mode")
+ object : AuthController.Callback {
+ override fun onFingerprintLocationChanged() {
+ sendFpLocation()
}
}
- biometricUnlockController.addBiometricModeListener(callback)
- trySendWithFailureLogging(
- biometricModeIntToObject(biometricUnlockController.getMode()),
- TAG,
- "initial biometric mode"
- )
+ authController.addCallback(callback)
+ sendFpLocation()
- awaitClose { biometricUnlockController.removeBiometricModeListener(callback) }
+ awaitClose { authController.removeCallback(callback) }
+ }
+
+ override val faceSensorLocation: Flow<Point?> = conflatedCallbackFlow {
+ fun sendSensorLocation() {
+ trySendWithFailureLogging(
+ authController.faceSensorLocation,
+ TAG,
+ "AuthController.Callback#onFingerprintLocationChanged"
+ )
+ }
+
+ val callback =
+ object : AuthController.Callback {
+ override fun onFaceSensorLocationChanged() {
+ sendSensorLocation()
+ }
+ }
+
+ authController.addCallback(callback)
+ sendSensorLocation()
+
+ awaitClose { authController.removeCallback(callback) }
+ }
+
+ override val biometricUnlockSource: Flow<BiometricUnlockSource?> = conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricAuthenticated(
+ userId: Int,
+ biometricSourceType: BiometricSourceType?,
+ isStrongBiometric: Boolean
+ ) {
+ trySendWithFailureLogging(
+ BiometricUnlockSource.fromBiometricSourceType(biometricSourceType),
+ TAG,
+ "onBiometricAuthenticated"
+ )
+ }
+ }
+
+ keyguardUpdateMonitor.registerCallback(callback)
+ trySendWithFailureLogging(null, TAG, "initial value")
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
override fun setAnimateDozingTransitions(animate: Boolean) {
@@ -423,16 +517,6 @@ constructor(
}
}
- private fun wakefulnessIntToObject(@Wakefulness value: Int): WakefulnessModel {
- return when (value) {
- 0 -> WakefulnessModel.ASLEEP
- 1 -> WakefulnessModel.STARTING_TO_WAKE
- 2 -> WakefulnessModel.AWAKE
- 3 -> WakefulnessModel.STARTING_TO_SLEEP
- else -> throw IllegalArgumentException("Invalid Wakefulness value: $value")
- }
- }
-
private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockModel {
return when (value) {
0 -> BiometricUnlockModel.NONE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index 0c725208e22d..26f853f3ad1c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -27,4 +27,7 @@ interface KeyguardRepositoryModule {
fun keyguardTransitionRepository(
impl: KeyguardTransitionRepositoryImpl
): KeyguardTransitionRepository
+
+ @Binds
+ fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository
}
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
new file mode 100644
index 000000000000..a17481a9978b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.data.repository
+
+import android.content.Context
+import android.graphics.Point
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
+import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.statusbar.PowerButtonReveal
+import javax.inject.Inject
+import kotlin.math.max
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+val DEFAULT_REVEAL_EFFECT = LiftReveal
+
+/**
+ * Encapsulates state relevant to the light reveal scrim, the view used to reveal/hide screen
+ * contents during transitions between AOD and lockscreen/unlocked.
+ */
+interface LightRevealScrimRepository {
+
+ /**
+ * The reveal effect that should be used for the next lock/unlock. We switch between either the
+ * biometric unlock effect (if wake and unlocking) or the non-biometric effect, and position it
+ * at the current screen position of the appropriate sensor.
+ */
+ val revealEffect: Flow<LightRevealEffect>
+}
+
+@SysUISingleton
+class LightRevealScrimRepositoryImpl
+@Inject
+constructor(
+ keyguardRepository: KeyguardRepository,
+ val context: Context,
+) : LightRevealScrimRepository {
+
+ /** The reveal effect used if the device was locked/unlocked via the power button. */
+ private val powerButtonReveal =
+ PowerButtonReveal(
+ context.resources
+ .getDimensionPixelSize(R.dimen.physical_power_button_center_screen_location_y)
+ .toFloat()
+ )
+
+ /**
+ * Reveal effect to use for a fingerprint unlock. This is reconstructed if the fingerprint
+ * sensor location on the screen (in pixels) changes due to configuration changes.
+ */
+ private val fingerprintRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.fingerprintSensorLocation.map {
+ it?.let { constructCircleRevealFromPoint(it) }
+ }
+
+ /**
+ * Reveal effect to use for a face unlock. This is reconstructed if the face sensor/front camera
+ * location on the screen (in pixels) changes due to configuration changes.
+ */
+ private val faceRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.faceSensorLocation.map { it?.let { constructCircleRevealFromPoint(it) } }
+
+ /**
+ * The reveal effect we'll use for the next biometric unlock animation. We switch between the
+ * fingerprint/face unlock effect flows depending on the biometric unlock source.
+ */
+ private val biometricRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.biometricUnlockSource.flatMapLatest { source ->
+ when (source) {
+ BiometricUnlockSource.FINGERPRINT_SENSOR -> fingerprintRevealEffect
+ BiometricUnlockSource.FACE_SENSOR -> faceRevealEffect
+ else -> flowOf(null)
+ }
+ }
+
+ /** The reveal effect we'll use for the next non-biometric unlock (tap, power button, etc). */
+ private val nonBiometricRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.wakefulness.map { wakefulnessModel ->
+ val wakingUpFromPowerButton =
+ wakefulnessModel.isWakingUpOrAwake &&
+ wakefulnessModel.lastWakeReason == WakeSleepReason.POWER_BUTTON
+ val sleepingFromPowerButton =
+ !wakefulnessModel.isWakingUpOrAwake &&
+ wakefulnessModel.lastSleepReason == WakeSleepReason.POWER_BUTTON
+
+ if (wakingUpFromPowerButton || sleepingFromPowerButton) {
+ powerButtonReveal
+ } else {
+ LiftReveal
+ }
+ }
+
+ override val revealEffect =
+ combine(
+ keyguardRepository.biometricUnlockState,
+ biometricRevealEffect,
+ nonBiometricRevealEffect
+ ) { biometricUnlockState, biometricReveal, nonBiometricReveal ->
+
+ // Use the biometric reveal for any flavor of wake and unlocking.
+ when (biometricUnlockState) {
+ BiometricUnlockModel.WAKE_AND_UNLOCK,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal
+ else -> nonBiometricReveal
+ }
+ ?: DEFAULT_REVEAL_EFFECT
+ }
+ .distinctUntilChanged()
+
+ private fun constructCircleRevealFromPoint(point: Point): LightRevealEffect {
+ return with(point) {
+ CircleReveal(
+ x,
+ y,
+ startRadius = 0,
+ endRadius =
+ max(max(x, context.display.width - x), max(y, context.display.height - y)),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
new file mode 100644
index 000000000000..0e865cee0b76
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/backup/KeyguardQuickAffordanceBackupHelper.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.keyguard.domain.backup
+
+import android.app.backup.SharedPreferencesBackupHelper
+import android.content.Context
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.settings.UserFileManagerImpl
+
+/** Handles backup & restore for keyguard quick affordances. */
+class KeyguardQuickAffordanceBackupHelper(
+ context: Context,
+ userId: Int,
+) :
+ SharedPreferencesBackupHelper(
+ context,
+ if (UserFileManagerImpl.isPrimaryUser(userId)) {
+ KeyguardQuickAffordanceSelectionManager.FILE_NAME
+ } else {
+ UserFileManagerImpl.secondaryUserFile(
+ context = context,
+ fileName = KeyguardQuickAffordanceSelectionManager.FILE_NAME,
+ directoryName = UserFileManagerImpl.SHARED_PREFS,
+ userId = userId,
+ )
+ .also { UserFileManagerImpl.ensureParentDirExists(it) }
+ .toString()
+ }
+ )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
index 2dbacd5d3c69..9b193533805e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
@@ -27,7 +27,6 @@ import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
index 9e2b7241ade2..e34981eabcac 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
@@ -42,7 +42,7 @@ constructor(
override fun start() {
scope.launch {
- keyguardInteractor.wakefulnessState
+ keyguardInteractor.wakefulnessModel
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.collect { pair ->
val (wakefulnessState, keyguardState) = pair
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
index 0e2a54c57bdb..483041a1b236 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
@@ -23,11 +23,10 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@SysUISingleton
@@ -42,13 +41,13 @@ constructor(
override fun start() {
scope.launch {
- keyguardInteractor.wakefulnessState
+ keyguardInteractor.wakefulnessModel
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.collect { pair ->
val (wakefulnessState, keyguardState) = pair
if (
keyguardState == KeyguardState.GONE &&
- wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP
+ wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 7cfd117d8eb4..6912e1d3558e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
+import android.graphics.Point
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
@@ -41,7 +42,7 @@ constructor(
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
* all.
*/
- val dozeAmount: Flow<Float> = repository.dozeAmount
+ val dozeAmount: Flow<Float> = repository.linearDozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
/** Doze transition information. */
@@ -58,7 +59,7 @@ constructor(
/** Whether the bouncer is showing or not. */
val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
/** The device wake/sleep state */
- val wakefulnessState: Flow<WakefulnessModel> = repository.wakefulnessState
+ val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState> = repository.statusBarState
/**
@@ -67,10 +68,15 @@ constructor(
*/
val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
+ /** The approximate location on the screen of the fingerprint sensor, if one is available. */
+ val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation
+
+ /** The approximate location on the screen of the face unlock sensor, if one is available. */
+ val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation
+
fun dozeTransitionTo(state: DozeStateModel): Flow<DozeTransitionModel> {
return dozeTransitionModel.filter { it.to == state }
}
-
fun isKeyguardShowing(): Boolean {
return repository.isKeyguardShowing()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 2d94d760cb54..748c6e8b75b9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -34,8 +34,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentati
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
@@ -190,8 +190,6 @@ constructor(
/** Returns affordance IDs indexed by slot ID, for all known slots. */
suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
- check(isUsingRepository)
-
val slots = repository.get().getSlotPickerRepresentations()
val selections = repository.get().getSelections()
val affordanceById =
@@ -312,8 +310,6 @@ constructor(
suspend fun getAffordancePickerRepresentations():
List<KeyguardQuickAffordancePickerRepresentation> {
- check(isUsingRepository)
-
return repository.get().getAffordancePickerRepresentations()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 58a8093d49d2..e30e7f6ece11 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -37,7 +37,7 @@ constructor(
fun start() {
scope.launch {
- keyguardInteractor.wakefulnessState.collect { logger.v("WakefulnessState", it) }
+ keyguardInteractor.wakefulnessModel.collect { logger.v("WakefulnessModel", it) }
}
scope.launch {
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
new file mode 100644
index 000000000000..6e25200bc2f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+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.statusbar.LightRevealEffect
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LightRevealScrimInteractor
+@Inject
+constructor(
+ transitionRepository: KeyguardTransitionRepository,
+ transitionInteractor: KeyguardTransitionInteractor,
+ lightRevealScrimRepository: LightRevealScrimRepository,
+) {
+
+ /**
+ * Whenever a keyguard transition starts, sample the latest reveal effect from the repository
+ * and use that for the starting transition.
+ *
+ * We can't simply use the nextRevealEffect since the effect may change midway through a
+ * transition, but we don't want to change effects part way through. For example, if we're using
+ * a CircleReveal to animate a biometric unlock, but the biometric unlock mode changes to NONE
+ * from WAKE_AND_UNLOCK before the unlock animation ends, we don't want to end up switching to a
+ * LiftReveal.
+ */
+ val lightRevealEffect: Flow<LightRevealEffect> =
+ transitionInteractor.startedKeyguardTransitionStep.sample(
+ lightRevealScrimRepository.revealEffect
+ )
+
+ /**
+ * The reveal amount to use for the light reveal scrim, which is derived from the keyguard
+ * transition steps.
+ */
+ val revealAmount: Flow<Float> =
+ transitionRepository.transitions
+ // Only listen to transitions that change the reveal amount.
+ .filter { willTransitionAffectRevealAmount(it) }
+ // Use the transition amount as the reveal amount, inverting it if we're transitioning
+ // to a non-revealed (hidden) state.
+ .map { step -> if (willBeRevealedInState(step.to)) step.value else 1f - step.value }
+
+ 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 willTransitionAffectRevealAmount(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.BOUNCER -> true
+ KeyguardState.LOCKSCREEN -> true
+ KeyguardState.GONE -> true
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index 3bb8241257cc..3218f9699f35 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -25,7 +25,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample
import java.util.UUID
@@ -58,22 +58,26 @@ constructor(
keyguardInteractor.isBouncerShowing
.sample(
combine(
- keyguardInteractor.wakefulnessState,
+ keyguardInteractor.wakefulnessModel,
keyguardTransitionInteractor.startedKeyguardTransitionStep,
- ) { a, b ->
- Pair(a, b)
- },
- { a, bc -> Triple(a, bc.first, bc.second) }
- )
- .collect { triple ->
- val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
+ ) { wakefulnessModel, transitionStep ->
+ Pair(wakefulnessModel, transitionStep)
+ }
+ ) { bouncerShowing, wakefulnessAndTransition ->
+ Triple(
+ bouncerShowing,
+ wakefulnessAndTransition.first,
+ wakefulnessAndTransition.second
+ )
+ }
+ .collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) ->
if (
!isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
) {
val to =
if (
- wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP ||
- wakefulnessState == WakefulnessModel.ASLEEP
+ wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
+ wakefulnessState.state == WakefulnessState.ASLEEP
) {
KeyguardState.AOD
} else {
@@ -100,14 +104,17 @@ constructor(
combine(
keyguardTransitionInteractor.finishedKeyguardState,
keyguardInteractor.statusBarState,
- ) { a, b ->
- Pair(a, b)
- },
- { a, bc -> Triple(a, bc.first, bc.second) }
- )
- .collect { triple ->
- val (shadeModel, keyguardState, statusBarState) = triple
-
+ ) { finishedKeyguardState, statusBarState ->
+ Pair(finishedKeyguardState, statusBarState)
+ }
+ ) { shadeModel, keyguardStateAndStatusBarState ->
+ Triple(
+ shadeModel,
+ keyguardStateAndStatusBarState.first,
+ keyguardStateAndStatusBarState.second
+ )
+ }
+ .collect { (shadeModel, keyguardState, statusBarState) ->
val id = transitionId
if (id != null) {
// An existing `id` means a transition is started, and calls to
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 84a80744185e..2cf5fb98d07e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.content.res.ColorStateList
+import android.hardware.biometrics.BiometricSourceType
import android.os.Handler
import android.os.Trace
import android.os.UserHandle
@@ -71,7 +72,7 @@ constructor(
KeyguardUpdateMonitor.getCurrentUser()
) &&
!needsFullscreenBouncer() &&
- !keyguardUpdateMonitor.userNeedsStrongAuth() &&
+ keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) &&
!keyguardBypassController.bypassEnabled
/** Runnable to show the primary bouncer. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt
new file mode 100644
index 000000000000..b403416c572c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.hardware.biometrics.BiometricSourceType
+
+/** Biometric unlock sensor sources, which we use to play sensor-specific animations. */
+enum class BiometricUnlockSource {
+ /** The unlock was initiated by a fingerprint sensor authentication. */
+ FINGERPRINT_SENSOR,
+
+ /** The unlock was initiated by the front-facing camera or a nearby sensor. */
+ FACE_SENSOR;
+
+ companion object {
+ fun fromBiometricSourceType(type: BiometricSourceType?): BiometricUnlockSource? {
+ return when (type) {
+ BiometricSourceType.FINGERPRINT -> FINGERPRINT_SENSOR
+ BiometricSourceType.FACE -> FACE_SENSOR
+ BiometricSourceType.IRIS -> FACE_SENSOR
+ else -> null
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
new file mode 100644
index 000000000000..b32597d5cff0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.keyguard.shared.model
+
+import android.os.PowerManager
+
+/** The reason we're waking up or going to sleep, such as pressing the power button. */
+enum class WakeSleepReason {
+ /** The physical power button was pressed to wake up or sleep the device. */
+ POWER_BUTTON,
+
+ /** Something else happened to wake up or sleep the device. */
+ OTHER;
+
+ companion object {
+ fun fromPowerManagerWakeReason(reason: Int): WakeSleepReason {
+ return when (reason) {
+ PowerManager.WAKE_REASON_POWER_BUTTON -> POWER_BUTTON
+ else -> OTHER
+ }
+ }
+
+ fun fromPowerManagerSleepReason(reason: Int): WakeSleepReason {
+ return when (reason) {
+ PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON -> POWER_BUTTON
+ else -> OTHER
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
index 92040f4f0348..03dee0045c10 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
@@ -15,24 +15,34 @@
*/
package com.android.systemui.keyguard.shared.model
-/** Model device wakefulness states. */
-enum class WakefulnessModel {
- /** The device is asleep and not interactive. */
- ASLEEP,
- /** Received a signal that the device is beginning to wake up. */
- STARTING_TO_WAKE,
- /** Device is now fully awake and interactive. */
- AWAKE,
- /** Signal that the device is now going to sleep. */
- STARTING_TO_SLEEP;
+import com.android.systemui.keyguard.WakefulnessLifecycle
+/** Model device wakefulness states. */
+data class WakefulnessModel(
+ val state: WakefulnessState,
+ val isWakingUpOrAwake: Boolean,
+ val lastWakeReason: WakeSleepReason,
+ val lastSleepReason: WakeSleepReason,
+) {
companion object {
fun isSleepingOrStartingToSleep(model: WakefulnessModel): Boolean {
- return model == ASLEEP || model == STARTING_TO_SLEEP
+ return model.state == WakefulnessState.ASLEEP ||
+ model.state == WakefulnessState.STARTING_TO_SLEEP
}
fun isWakingOrStartingToWake(model: WakefulnessModel): Boolean {
- return model == AWAKE || model == STARTING_TO_WAKE
+ return model.state == WakefulnessState.AWAKE ||
+ model.state == WakefulnessState.STARTING_TO_WAKE
+ }
+
+ fun fromWakefulnessLifecycle(wakefulnessLifecycle: WakefulnessLifecycle): WakefulnessModel {
+ return WakefulnessModel(
+ WakefulnessState.fromWakefulnessLifecycleInt(wakefulnessLifecycle.wakefulness),
+ wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_WAKING ||
+ wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_AWAKE,
+ WakeSleepReason.fromPowerManagerWakeReason(wakefulnessLifecycle.lastWakeReason),
+ WakeSleepReason.fromPowerManagerSleepReason(wakefulnessLifecycle.lastSleepReason),
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt
new file mode 100644
index 000000000000..6791d88f16d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.keyguard.shared.model
+
+import com.android.systemui.keyguard.WakefulnessLifecycle
+
+enum class WakefulnessState {
+ /** The device is asleep and not interactive. */
+ ASLEEP,
+ /** Received a signal that the device is beginning to wake up. */
+ STARTING_TO_WAKE,
+ /** Device is now fully awake and interactive. */
+ AWAKE,
+ /** Signal that the device is now going to sleep. */
+ STARTING_TO_SLEEP;
+
+ companion object {
+ fun fromWakefulnessLifecycleInt(
+ @WakefulnessLifecycle.Wakefulness value: Int
+ ): WakefulnessState {
+ return when (value) {
+ WakefulnessLifecycle.WAKEFULNESS_ASLEEP -> ASLEEP
+ WakefulnessLifecycle.WAKEFULNESS_WAKING -> STARTING_TO_WAKE
+ WakefulnessLifecycle.WAKEFULNESS_AWAKE -> AWAKE
+ WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP -> STARTING_TO_SLEEP
+ else -> throw IllegalArgumentException("Invalid Wakefulness value: $value")
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 3276b6dd9748..cbe512ff83ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.binder
+import android.graphics.drawable.Animatable2
import android.util.Size
import android.util.TypedValue
import android.view.View
@@ -27,12 +28,11 @@ import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.LockIconViewController
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.Interpolators
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
@@ -73,7 +73,8 @@ object KeyguardBottomAreaViewBinder {
fun onConfigurationChanged()
/**
- * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+ * Returns whether the keyguard bottom area should be constrained to the top of the lock
+ * icon
*/
fun shouldConstrainToTopOfLockIcon(): Boolean
}
@@ -217,7 +218,7 @@ object KeyguardBottomAreaViewBinder {
}
override fun shouldConstrainToTopOfLockIcon(): Boolean =
- viewModel.shouldConstrainToTopOfLockIcon()
+ viewModel.shouldConstrainToTopOfLockIcon()
}
}
@@ -248,6 +249,27 @@ object KeyguardBottomAreaViewBinder {
IconViewBinder.bind(viewModel.icon, view)
+ (view.drawable as? Animatable2)?.let { animatable ->
+ (viewModel.icon as? Icon.Resource)?.res?.let { iconResourceId ->
+ // Always start the animation (we do call stop() below, if we need to skip it).
+ animatable.start()
+
+ if (view.tag != iconResourceId) {
+ // Here when we haven't run the animation on a previous update.
+ //
+ // Save the resource ID for next time, so we know not to re-animate the same
+ // animation again.
+ view.tag = iconResourceId
+ } else {
+ // Here when we've already done this animation on a previous update and want to
+ // skip directly to the final frame of the animation to avoid running it.
+ //
+ // By calling stop after start, we go to the final frame of the animation.
+ animatable.stop()
+ }
+ }
+ }
+
view.isActivated = viewModel.isActivated
view.drawable.setTint(
Utils.getColorAttrDefaultColor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
new file mode 100644
index 000000000000..f1da8826a22c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.LightRevealScrim
+import kotlinx.coroutines.launch
+
+object LightRevealScrimViewBinder {
+ @JvmStatic
+ fun bind(revealScrim: LightRevealScrim, viewModel: LightRevealScrimViewModel) {
+ revealScrim.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ viewModel.revealAmount.collect { amount -> revealScrim.revealAmount = amount }
+ }
+
+ launch {
+ viewModel.lightRevealEffect.collect { effect ->
+ revealScrim.revealEffect = effect
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
new file mode 100644
index 000000000000..a46d441613ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.LightRevealScrimInteractor
+import com.android.systemui.statusbar.LightRevealEffect
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Models UI state for the light reveal scrim, which is used during screen on and off animations to
+ * draw a gradient that reveals/hides the contents of the screen.
+ */
+class LightRevealScrimViewModel @Inject constructor(interactor: LightRevealScrimInteractor) {
+ val lightRevealEffect: Flow<LightRevealEffect> = interactor.lightRevealEffect
+ val revealAmount: Flow<Float> = interactor.revealAmount
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
index f9e341c8629a..d6e29e0f2067 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
@@ -16,9 +16,9 @@
package com.android.systemui.log
-import android.app.ActivityManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogcatEchoTracker
@@ -29,15 +29,6 @@ class LogBufferFactory @Inject constructor(
private val dumpManager: DumpManager,
private val logcatEchoTracker: LogcatEchoTracker
) {
- /* limitiometricMessageDeferralLogger the size of maxPoolSize for low ram (Go) devices */
- private fun adjustMaxSize(requestedMaxSize: Int): Int {
- return if (ActivityManager.isLowRamDeviceStatic()) {
- minOf(requestedMaxSize, 20) /* low ram max log size*/
- } else {
- requestedMaxSize
- }
- }
-
@JvmOverloads
fun create(
name: String,
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBufferHelper.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferHelper.kt
new file mode 100644
index 000000000000..619eac1dbdc3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferHelper.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+import android.app.ActivityManager
+
+class LogBufferHelper {
+ companion object {
+ /** If necessary, returns a limited maximum size for low ram (Go) devices */
+ fun adjustMaxSize(requestedMaxSize: Int): Int {
+ return if (ActivityManager.isLowRamDeviceStatic()) {
+ minOf(requestedMaxSize, 20) /* low ram max log size*/
+ } else {
+ requestedMaxSize
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
new file mode 100644
index 000000000000..bb04b6b41a33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.table
+
+import com.android.systemui.util.kotlin.pairwiseBy
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * An interface that enables logging the difference between values in table format.
+ *
+ * Many objects that we want to log are data-y objects with a collection of fields. When logging
+ * these objects, we want to log each field separately. This allows ABT (Android Bug Tool) to easily
+ * highlight changes in individual fields.
+ *
+ * See [TableLogBuffer].
+ */
+interface Diffable<T> {
+ /**
+ * Finds the differences between [prevVal] and this object and logs those diffs to [row].
+ *
+ * Each implementer should determine which individual fields have changed between [prevVal] and
+ * this object, and only log the fields that have actually changed. This helps save buffer
+ * space.
+ *
+ * For example, if:
+ * - prevVal = Object(val1=100, val2=200, val3=300)
+ * - this = Object(val1=100, val2=200, val3=333)
+ *
+ * Then only the val3 change should be logged.
+ */
+ fun logDiffs(prevVal: T, row: TableRowLogger)
+
+ /**
+ * Logs all the relevant fields of this object to [row].
+ *
+ * As opposed to [logDiffs], this method should log *all* fields.
+ *
+ * Implementation is optional. This method will only be used with [logDiffsForTable] in order to
+ * fully log the initial value of the flow.
+ */
+ fun logFull(row: TableRowLogger) {}
+}
+
+/**
+ * Each time the flow is updated with a new value, logs the differences between the previous value
+ * and the new value to the given [tableLogBuffer].
+ *
+ * The new value's [Diffable.logDiffs] method will be used to log the differences to the table.
+ *
+ * @param columnPrefix a prefix that will be applied to every column name that gets logged.
+ */
+fun <T : Diffable<T>> Flow<T>.logDiffsForTable(
+ tableLogBuffer: TableLogBuffer,
+ columnPrefix: String,
+ initialValue: T,
+): Flow<T> {
+ // Fully log the initial value to the table.
+ val getInitialValue = {
+ tableLogBuffer.logChange(columnPrefix) { row -> initialValue.logFull(row) }
+ initialValue
+ }
+ return this.pairwiseBy(getInitialValue) { prevVal: T, newVal: T ->
+ tableLogBuffer.logDiffs(columnPrefix, prevVal, newVal)
+ newVal
+ }
+}
+
+/**
+ * Each time the boolean flow is updated with a new value that's different from the previous value,
+ * logs the new value to the given [tableLogBuffer].
+ */
+fun Flow<Boolean>.logDiffsForTable(
+ tableLogBuffer: TableLogBuffer,
+ columnPrefix: String,
+ columnName: String,
+ initialValue: Boolean,
+): Flow<Boolean> {
+ val initialValueFun = {
+ tableLogBuffer.logChange(columnPrefix, columnName, initialValue)
+ initialValue
+ }
+ return this.pairwiseBy(initialValueFun) { prevVal, newVal: Boolean ->
+ if (prevVal != newVal) {
+ tableLogBuffer.logChange(columnPrefix, columnName, newVal)
+ }
+ newVal
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
new file mode 100644
index 000000000000..68c297f76ab7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.table
+
+/**
+ * A object used with [TableLogBuffer] to store changes in variables over time. Is recyclable.
+ *
+ * Each message represents a change to exactly 1 type, specified by [DataType].
+ */
+data class TableChange(
+ var timestamp: Long = 0,
+ var columnPrefix: String = "",
+ var columnName: String = "",
+ var type: DataType = DataType.EMPTY,
+ var bool: Boolean = false,
+ var int: Int = 0,
+ var str: String? = null,
+) {
+ /** Resets to default values so that the object can be recycled. */
+ fun reset(timestamp: Long, columnPrefix: String, columnName: String) {
+ this.timestamp = timestamp
+ this.columnPrefix = columnPrefix
+ this.columnName = columnName
+ this.type = DataType.EMPTY
+ this.bool = false
+ this.int = 0
+ this.str = null
+ }
+
+ /** Sets this to store a string change. */
+ fun set(value: String?) {
+ type = DataType.STRING
+ str = value
+ }
+
+ /** Sets this to store a boolean change. */
+ fun set(value: Boolean) {
+ type = DataType.BOOLEAN
+ bool = value
+ }
+
+ /** Sets this to store an int change. */
+ fun set(value: Int) {
+ type = DataType.INT
+ int = value
+ }
+
+ /** Returns true if this object has a change. */
+ fun hasData(): Boolean {
+ return columnName.isNotBlank() && type != DataType.EMPTY
+ }
+
+ fun getName(): String {
+ return if (columnPrefix.isNotBlank()) {
+ "$columnPrefix.$columnName"
+ } else {
+ columnName
+ }
+ }
+
+ fun getVal(): String {
+ return when (type) {
+ DataType.EMPTY -> null
+ DataType.STRING -> str
+ DataType.INT -> int
+ DataType.BOOLEAN -> bool
+ }.toString()
+ }
+
+ enum class DataType {
+ STRING,
+ BOOLEAN,
+ INT,
+ EMPTY,
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
new file mode 100644
index 000000000000..9d0b833d26cc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.table
+
+import com.android.systemui.Dumpable
+import com.android.systemui.plugins.util.RingBuffer
+import com.android.systemui.util.time.SystemClock
+import java.io.PrintWriter
+import java.text.SimpleDateFormat
+import java.util.Locale
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * A logger that logs changes in table format.
+ *
+ * Some parts of System UI maintain a lot of pieces of state at once.
+ * [com.android.systemui.plugins.log.LogBuffer] allows us to easily log change events:
+ *
+ * - 10-10 10:10:10.456: state2 updated to newVal2
+ * - 10-10 10:11:00.000: stateN updated to StateN(val1=true, val2=1)
+ * - 10-10 10:11:02.123: stateN updated to StateN(val1=true, val2=2)
+ * - 10-10 10:11:05.123: state1 updated to newVal1
+ * - 10-10 10:11:06.000: stateN updated to StateN(val1=false, val2=3)
+ *
+ * However, it can sometimes be more useful to view the state changes in table format:
+ *
+ * - timestamp--------- | state1- | state2- | ... | stateN.val1 | stateN.val2
+ * - -------------------------------------------------------------------------
+ * - 10-10 10:10:10.123 | val1--- | val2--- | ... | false------ | 0-----------
+ * - 10-10 10:10:10.456 | val1--- | newVal2 | ... | false------ | 0-----------
+ * - 10-10 10:11:00.000 | val1--- | newVal2 | ... | true------- | 1-----------
+ * - 10-10 10:11:02.123 | val1--- | newVal2 | ... | true------- | 2-----------
+ * - 10-10 10:11:05.123 | newVal1 | newVal2 | ... | true------- | 2-----------
+ * - 10-10 10:11:06.000 | newVal1 | newVal2 | ... | false------ | 3-----------
+ *
+ * This class enables easy logging of the state changes in both change event format and table
+ * format.
+ *
+ * This class also enables easy logging of states that are a collection of fields. For example,
+ * stateN in the above example consists of two fields -- val1 and val2. It's useful to put each
+ * field into its own column so that ABT (Android Bug Tool) can easily highlight changes to
+ * individual fields.
+ *
+ * How it works:
+ *
+ * 1) Create an instance of this buffer via [TableLogBufferFactory].
+ *
+ * 2) For any states being logged, implement [Diffable]. Implementing [Diffable] allows the state to
+ * only log the fields that have *changed* since the previous update, instead of always logging all
+ * fields.
+ *
+ * 3) Each time a change in a state happens, call [logDiffs]. If your state is emitted using a
+ * [Flow], you should use the [logDiffsForTable] extension function to automatically log diffs any
+ * time your flow emits a new value.
+ *
+ * When a dump occurs, there will be two dumps:
+ *
+ * 1) The change events under the dumpable name "$name-changes".
+ *
+ * 2) This class will coalesce all the diffs into a table format and log them under the dumpable
+ * name "$name-table".
+ *
+ * @param maxSize the maximum size of the buffer. Must be > 0.
+ */
+class TableLogBuffer(
+ maxSize: Int,
+ private val name: String,
+ private val systemClock: SystemClock,
+) : Dumpable {
+ init {
+ if (maxSize <= 0) {
+ throw IllegalArgumentException("maxSize must be > 0")
+ }
+ }
+
+ private val buffer = RingBuffer(maxSize) { TableChange() }
+
+ // A [TableRowLogger] object, re-used each time [logDiffs] is called.
+ // (Re-used to avoid object allocation.)
+ private val tempRow = TableRowLoggerImpl(0, columnPrefix = "", this)
+
+ /**
+ * Log the differences between [prevVal] and [newVal].
+ *
+ * The [newVal] object's method [Diffable.logDiffs] will be used to fetch the diffs.
+ *
+ * @param columnPrefix a prefix that will be applied to every column name that gets logged. This
+ * ensures that all the columns related to the same state object will be grouped together in the
+ * table.
+ *
+ * @throws IllegalArgumentException if [columnPrefix] or column name contain "|". "|" is used as
+ * the separator token for parsing, so it can't be present in any part of the column name.
+ */
+ @Synchronized
+ fun <T : Diffable<T>> logDiffs(columnPrefix: String, prevVal: T, newVal: T) {
+ val row = tempRow
+ row.timestamp = systemClock.currentTimeMillis()
+ row.columnPrefix = columnPrefix
+ newVal.logDiffs(prevVal, row)
+ }
+
+ /**
+ * Logs change(s) to the buffer using [rowInitializer].
+ *
+ * @param rowInitializer a function that will be called immediately to store relevant data on
+ * the row.
+ */
+ @Synchronized
+ fun logChange(columnPrefix: String, rowInitializer: (TableRowLogger) -> Unit) {
+ val row = tempRow
+ row.timestamp = systemClock.currentTimeMillis()
+ row.columnPrefix = columnPrefix
+ rowInitializer(row)
+ }
+
+ /** Logs a boolean change. */
+ fun logChange(prefix: String, columnName: String, value: Boolean) {
+ logChange(systemClock.currentTimeMillis(), prefix, columnName, value)
+ }
+
+ // Keep these individual [logChange] methods private (don't let clients give us their own
+ // timestamps.)
+
+ private fun logChange(timestamp: Long, prefix: String, columnName: String, value: String?) {
+ val change = obtain(timestamp, prefix, columnName)
+ change.set(value)
+ }
+
+ private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Boolean) {
+ val change = obtain(timestamp, prefix, columnName)
+ change.set(value)
+ }
+
+ private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Int) {
+ val change = obtain(timestamp, prefix, columnName)
+ change.set(value)
+ }
+
+ // TODO(b/259454430): Add additional change types here.
+
+ @Synchronized
+ private fun obtain(timestamp: Long, prefix: String, columnName: String): TableChange {
+ verifyValidName(prefix, columnName)
+ val tableChange = buffer.advance()
+ tableChange.reset(timestamp, prefix, columnName)
+ return tableChange
+ }
+
+ private fun verifyValidName(prefix: String, columnName: String) {
+ if (prefix.contains(SEPARATOR)) {
+ throw IllegalArgumentException("columnPrefix cannot contain $SEPARATOR but was $prefix")
+ }
+ if (columnName.contains(SEPARATOR)) {
+ throw IllegalArgumentException(
+ "columnName cannot contain $SEPARATOR but was $columnName"
+ )
+ }
+ }
+
+ @Synchronized
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println(HEADER_PREFIX + name)
+ pw.println("version $VERSION")
+ for (i in 0 until buffer.size) {
+ buffer[i].dump(pw)
+ }
+ pw.println(FOOTER_PREFIX + name)
+ }
+
+ /** Dumps an individual [TableChange]. */
+ private fun TableChange.dump(pw: PrintWriter) {
+ if (!this.hasData()) {
+ return
+ }
+ val formattedTimestamp = TABLE_LOG_DATE_FORMAT.format(timestamp)
+ pw.print(formattedTimestamp)
+ pw.print(SEPARATOR)
+ pw.print(this.getName())
+ pw.print(SEPARATOR)
+ pw.print(this.getVal())
+ pw.println()
+ }
+
+ /**
+ * A private implementation of [TableRowLogger].
+ *
+ * Used so that external clients can't modify [timestamp].
+ */
+ private class TableRowLoggerImpl(
+ var timestamp: Long,
+ var columnPrefix: String,
+ val tableLogBuffer: TableLogBuffer,
+ ) : TableRowLogger {
+ /** Logs a change to a string value. */
+ override fun logChange(columnName: String, value: String?) {
+ tableLogBuffer.logChange(timestamp, columnPrefix, columnName, value)
+ }
+
+ /** Logs a change to a boolean value. */
+ override fun logChange(columnName: String, value: Boolean) {
+ tableLogBuffer.logChange(timestamp, columnPrefix, columnName, value)
+ }
+
+ /** Logs a change to an int value. */
+ override fun logChange(columnName: String, value: Int) {
+ tableLogBuffer.logChange(timestamp, columnPrefix, columnName, value)
+ }
+ }
+}
+
+val TABLE_LOG_DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
+
+private const val HEADER_PREFIX = "SystemUI StateChangeTableSection START: "
+private const val FOOTER_PREFIX = "SystemUI StateChangeTableSection END: "
+private const val SEPARATOR = "|"
+private const val VERSION = "1"
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
new file mode 100644
index 000000000000..7a90a7470cd2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.table
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+
+@SysUISingleton
+class TableLogBufferFactory
+@Inject
+constructor(
+ private val dumpManager: DumpManager,
+ private val systemClock: SystemClock,
+) {
+ fun create(
+ name: String,
+ maxSize: Int,
+ ): TableLogBuffer {
+ val tableBuffer = TableLogBuffer(adjustMaxSize(maxSize), name, systemClock)
+ dumpManager.registerNormalDumpable(name, tableBuffer)
+ return tableBuffer
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableRowLogger.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableRowLogger.kt
new file mode 100644
index 000000000000..a7ba13b55ed8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableRowLogger.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.table
+
+/**
+ * A class that logs a row to [TableLogBuffer].
+ *
+ * Objects that implement [Diffable] will receive an instance of this class, and can log any changes
+ * to individual fields using the [logChange] methods. All logged changes will be associated with
+ * the same timestamp.
+ */
+interface TableRowLogger {
+ /** Logs a change to a string value. */
+ fun logChange(columnName: String, value: String?)
+
+ /** Logs a change to a boolean value. */
+ fun logChange(columnName: String, value: Boolean)
+
+ /** Logs a change to an int value. */
+ fun logChange(columnName: String, value: Int)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 22f91f359de8..bfa67a89baca 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -109,7 +109,6 @@ public class MediaProjectionPermissionActivity extends Activity
CharSequence dialogTitle = null;
String appName = null;
if (Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName)) {
- // TODO(b/253438807): handle special app name
dialogText = getString(R.string.media_projection_dialog_service_text);
dialogTitle = getString(R.string.media_projection_dialog_service_title);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
index 4891297dbcf9..2d10b823f784 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
@@ -32,10 +32,12 @@ import com.android.systemui.Dumpable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.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.settings.UserTracker
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.Utils
import com.android.systemui.util.time.SystemClock
@@ -55,6 +57,8 @@ class MediaResumeListener
constructor(
private val context: Context,
private val broadcastDispatcher: BroadcastDispatcher,
+ private val userTracker: UserTracker,
+ @Main private val mainExecutor: Executor,
@Background private val backgroundExecutor: Executor,
private val tunerService: TunerService,
private val mediaBrowserFactory: ResumeMediaBrowserFactory,
@@ -77,18 +81,26 @@ constructor(
private var currentUserId: Int = context.userId
@VisibleForTesting
- val userChangeReceiver =
+ val userUnlockReceiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_USER_UNLOCKED == intent.action) {
- loadMediaResumptionControls()
- } else if (Intent.ACTION_USER_SWITCHED == intent.action) {
- currentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
- loadSavedComponents()
+ val userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
+ if (userId == currentUserId) {
+ loadMediaResumptionControls()
+ }
}
}
}
+ private val userTrackerCallback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ currentUserId = newUser
+ loadSavedComponents()
+ }
+ }
+
private val mediaBrowserCallback =
object : ResumeMediaBrowser.Callback() {
override fun addTrack(
@@ -126,13 +138,13 @@ constructor(
dumpManager.registerDumpable(TAG, this)
val unlockFilter = IntentFilter()
unlockFilter.addAction(Intent.ACTION_USER_UNLOCKED)
- unlockFilter.addAction(Intent.ACTION_USER_SWITCHED)
broadcastDispatcher.registerReceiver(
- userChangeReceiver,
+ userUnlockReceiver,
unlockFilter,
null,
UserHandle.ALL
)
+ userTracker.addCallback(userTrackerCallback, mainExecutor)
loadSavedComponents()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 8aaee81a57dd..1fdbc99333cb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -184,6 +184,7 @@ constructor(
private val configListener =
object : ConfigurationController.ConfigurationListener {
+
override fun onDensityOrFontScaleChanged() {
// System font changes should only happen when UMO is offscreen or a flicker may
// occur
@@ -199,6 +200,7 @@ constructor(
override fun onConfigChanged(newConfig: Configuration?) {
if (newConfig == null) return
isRtl = newConfig.layoutDirection == View.LAYOUT_DIRECTION_RTL
+ updatePlayers(recreateMedia = true)
}
override fun onUiModeChanged() {
@@ -635,7 +637,7 @@ constructor(
val existingSmartspaceMediaKey = MediaPlayerData.smartspaceMediaKey()
existingSmartspaceMediaKey?.let {
val removedPlayer =
- MediaPlayerData.removeMediaPlayer(existingSmartspaceMediaKey, true)
+ removePlayer(existingSmartspaceMediaKey, dismissMediaData = false)
removedPlayer?.run {
debugLogger.logPotentialMemoryLeak(existingSmartspaceMediaKey)
}
@@ -685,7 +687,7 @@ constructor(
key: String,
dismissMediaData: Boolean = true,
dismissRecommendation: Boolean = true
- ) {
+ ): MediaControlPanel? {
if (key == MediaPlayerData.smartspaceMediaKey()) {
MediaPlayerData.smartspaceMediaData?.let {
logger.logRecommendationRemoved(it.packageName, it.instanceId)
@@ -693,7 +695,7 @@ constructor(
}
val removed =
MediaPlayerData.removeMediaPlayer(key, dismissMediaData || dismissRecommendation)
- removed?.apply {
+ return removed?.apply {
mediaCarouselScrollHandler.onPrePlayerRemoved(removed)
mediaContent.removeView(removed.mediaViewHolder?.player)
mediaContent.removeView(removed.recommendationViewHolder?.recommendations)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 21e64e28ff19..827ac789073a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -458,7 +458,9 @@ public class MediaControlPanel {
if (mMediaViewHolder == null) {
return;
}
- Trace.beginSection("MediaControlPanel#bindPlayer<" + key + ">");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, "MediaControlPanel#bindPlayer<" + key + ">");
+ }
mKey = key;
mMediaData = data;
MediaSession.Token token = data.getToken();
@@ -1179,8 +1181,10 @@ public class MediaControlPanel {
return;
}
- Trace.beginSection(
- "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP,
+ "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">");
+ }
mRecommendationData = data;
mSmartspaceId = SmallHash.hash(data.getTargetId());
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index cbb670ebf02d..f7a9bc760caf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -799,6 +799,16 @@ constructor(
}
if (
+ desiredLocation == LOCATION_QS &&
+ previousLocation == LOCATION_LOCKSCREEN &&
+ statusbarState == StatusBarState.SHADE
+ ) {
+ // This is an invalid transition, can happen when tapping on home control and the UMO
+ // while being on landscape orientation in tablet.
+ return false
+ }
+
+ if (
statusbarState == StatusBarState.KEYGUARD &&
(currentLocation == LOCATION_LOCKSCREEN || previousLocation == LOCATION_LOCKSCREEN)
) {
@@ -1043,18 +1053,9 @@ constructor(
rootOverlay!!.add(mediaFrame)
} else {
val targetHost = getHost(newLocation)!!.hostView
- // When adding back to the host, let's make sure to reset the bounds.
- // Usually adding the view will trigger a layout that does this automatically,
- // but we sometimes suppress this.
+ // This will either do a full layout pass and remeasure, or it will bypass
+ // that and directly set the mediaFrame's bounds within the premeasured host.
targetHost.addView(mediaFrame)
- val left = targetHost.paddingLeft
- val top = targetHost.paddingTop
- mediaFrame.setLeftTopRightBottom(
- left,
- top,
- left + currentBounds.width(),
- top + currentBounds.height()
- )
if (mediaFrame.childCount > 0) {
val child = mediaFrame.getChildAt(0)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index 4bf3031c02b4..4feb9844cb8b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -420,7 +420,9 @@ constructor(
*/
fun getMeasurementsForState(hostState: MediaHostState): MeasurementOutput? =
traceSection("MediaViewController#getMeasurementsForState") {
- val viewState = obtainViewState(hostState) ?: return null
+ // measurements should never factor in the squish fraction
+ val viewState =
+ obtainViewState(hostState.copy().also { it.squishFraction = 1.0f }) ?: return null
measurement.measuredWidth = viewState.width
measurement.measuredHeight = viewState.height
return measurement
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 33021e3cde47..2c0745bf0f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -16,6 +16,8 @@
package com.android.systemui.navigationbar;
+import static android.app.StatusBarManager.WINDOW_NAVIGATION_BAR;
+import static android.app.StatusBarManager.WindowVisibleState;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
@@ -58,6 +60,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -86,7 +89,7 @@ public final class NavBarHelper implements
AccessibilityButtonModeObserver.ModeChangedListener,
AccessibilityButtonTargetsObserver.TargetsChangedListener,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
- Dumpable {
+ Dumpable, CommandQueue.Callbacks {
private final AccessibilityManager mAccessibilityManager;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
@@ -97,13 +100,18 @@ public final class NavBarHelper implements
private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver;
private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>();
private final Context mContext;
- private ContentResolver mContentResolver;
+ private final CommandQueue mCommandQueue;
+ private final ContentResolver mContentResolver;
private boolean mAssistantAvailable;
private boolean mLongPressHomeEnabled;
private boolean mAssistantTouchGestureEnabled;
private int mNavBarMode;
private int mA11yButtonState;
+ // Attributes used in NavBarHelper.CurrentSysuiState
+ private int mWindowStateDisplayId;
+ private @WindowVisibleState int mWindowState;
+
private final ContentObserver mAssistContentObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@Override
@@ -128,8 +136,10 @@ public final class NavBarHelper implements
KeyguardStateController keyguardStateController,
NavigationModeController navigationModeController,
UserTracker userTracker,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ CommandQueue commandQueue) {
mContext = context;
+ mCommandQueue = commandQueue;
mContentResolver = mContext.getContentResolver();
mAccessibilityManager = accessibilityManager;
mAssistManagerLazy = assistManagerLazy;
@@ -160,10 +170,13 @@ public final class NavBarHelper implements
false, mAssistContentObserver, UserHandle.USER_ALL);
updateAssistantAvailability();
updateA11yState();
+ mCommandQueue.addCallback(this);
+
}
public void destroy() {
mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ mCommandQueue.removeCallback(this);
}
/**
@@ -333,6 +346,20 @@ public final class NavBarHelper implements
|| (!isKeyguardShowing && (vis & InputMethodService.IME_VISIBLE) != 0);
}
+ @Override
+ public void setWindowState(int displayId, int window, int state) {
+ CommandQueue.Callbacks.super.setWindowState(displayId, window, state);
+ if (window != WINDOW_NAVIGATION_BAR) {
+ return;
+ }
+ mWindowStateDisplayId = displayId;
+ mWindowState = state;
+ }
+
+ public CurrentSysuiState getCurrentSysuiState() {
+ return new CurrentSysuiState();
+ }
+
/**
* Callbacks will get fired once immediately after registering via
* {@link #registerNavTaskStateUpdater(NavbarTaskbarStateUpdater)}
@@ -342,6 +369,17 @@ public final class NavBarHelper implements
void updateAssistantAvailable(boolean available);
}
+ /** Data class to help Taskbar/Navbar initiate state correctly when switching between the two.*/
+ public class CurrentSysuiState {
+ public final int mWindowStateDisplayId;
+ public final @WindowVisibleState int mWindowState;
+
+ public CurrentSysuiState() {
+ mWindowStateDisplayId = NavBarHelper.this.mWindowStateDisplayId;
+ mWindowState = NavBarHelper.this.mWindowState;
+ }
+ }
+
static @TransitionMode int transitionMode(boolean isTransient, int appearance) {
final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS;
if (isTransient) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 85d15dca12cb..d762b39fa1af 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -52,13 +52,12 @@ import static com.android.systemui.statusbar.phone.CentralSurfaces.dumpBarTransi
import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay;
import android.annotation.IdRes;
+import android.annotation.NonNull;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -114,7 +113,6 @@ import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
@@ -132,6 +130,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.shared.recents.utilities.Utilities;
@@ -202,7 +201,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final OverviewProxyService mOverviewProxyService;
private final NavigationModeController mNavigationModeController;
- private final BroadcastDispatcher mBroadcastDispatcher;
+ private final UserTracker mUserTracker;
private final CommandQueue mCommandQueue;
private final Optional<Pip> mPipOptional;
private final Optional<Recents> mRecentsOptional;
@@ -504,7 +503,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
StatusBarStateController statusBarStateController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
SysUiState sysUiFlagsContainer,
- BroadcastDispatcher broadcastDispatcher,
+ UserTracker userTracker,
CommandQueue commandQueue,
Optional<Pip> pipOptional,
Optional<Recents> recentsOptional,
@@ -547,7 +546,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mNotificationRemoteInputManager = notificationRemoteInputManager;
mOverviewProxyService = overviewProxyService;
mNavigationModeController = navigationModeController;
- mBroadcastDispatcher = broadcastDispatcher;
+ mUserTracker = userTracker;
mCommandQueue = commandQueue;
mPipOptional = pipOptional;
mRecentsOptional = recentsOptional;
@@ -650,6 +649,9 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mDisplayId = mContext.getDisplayId();
mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
+ // Ensure we try to get currentSysuiState from navBarHelper before command queue callbacks
+ // start firing, since the latter is source of truth
+ parseCurrentSysuiState();
mCommandQueue.addCallback(this);
mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
mNavBarHelper.init();
@@ -726,9 +728,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
prepareNavigationBarView();
checkNavBarModes();
- IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter,
- Handler.getMain(), UserHandle.ALL);
+ mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
notifyNavigationBarScreenOn();
@@ -779,7 +779,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mView.setUpdateActiveTouchRegionsCallback(null);
getBarTransitions().destroy();
mOverviewProxyService.removeCallback(mOverviewProxyListener);
- mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
+ mUserTracker.removeCallback(mUserChangedCallback);
mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
if (mOrientationHandle != null) {
resetSecondaryHandle();
@@ -937,6 +937,13 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
setOrientedHandleSamplingRegion(null);
}
+ private void parseCurrentSysuiState() {
+ NavBarHelper.CurrentSysuiState state = mNavBarHelper.getCurrentSysuiState();
+ if (state.mWindowStateDisplayId == mDisplayId) {
+ mNavigationBarWindowState = state.mWindowState;
+ }
+ }
+
private void reconfigureHomeLongClick() {
if (mView.getHomeButton().getCurrentView() == null) {
return;
@@ -1664,21 +1671,14 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
};
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO(193941146): Currently unregistering a receiver through BroadcastDispatcher is
- // async, but we've already cleared the fields. Just return early in this case.
- if (mView == null) {
- return;
- }
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- // The accessibility settings may be different for the new user
- updateAccessibilityStateFlags();
- }
- }
- };
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ // The accessibility settings may be different for the new user
+ updateAccessibilityStateFlags();
+ }
+ };
@VisibleForTesting
int getNavigationIconHints() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 73fc21ef928c..0d9af31639f2 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -214,6 +214,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
return;
}
mDisplayId = displayId;
+ parseCurrentSysuiState();
mCommandQueue.addCallback(this);
mOverviewProxyService.addCallback(this);
mEdgeBackGestureHandler.onNavigationModeChanged(
@@ -271,6 +272,13 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
return mInitialized;
}
+ private void parseCurrentSysuiState() {
+ NavBarHelper.CurrentSysuiState state = mNavBarHelper.getCurrentSysuiState();
+ if (state.mWindowStateDisplayId == mDisplayId) {
+ mTaskBarWindowState = state.mWindowState;
+ }
+ }
+
private void updateSysuiFlags() {
int a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 1da866efc08d..5a1ad96da7a9 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -39,6 +39,8 @@ import android.text.format.DateUtils;
import android.util.Log;
import android.util.Slog;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.ThreadUtils;
@@ -47,6 +49,7 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -80,6 +83,7 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
private final PowerManager mPowerManager;
private final WarningsUI mWarnings;
private final WakefulnessLifecycle mWakefulnessLifecycle;
+ private final UserTracker mUserTracker;
private InattentiveSleepWarningView mOverlayView;
private final Configuration mLastConfiguration = new Configuration();
private int mPlugType = 0;
@@ -122,12 +126,21 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
}
};
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ mWarnings.userSwitched();
+ }
+ };
+
@Inject
public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
CommandQueue commandQueue, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
WarningsUI warningsUI, EnhancedEstimates enhancedEstimates,
WakefulnessLifecycle wakefulnessLifecycle,
- PowerManager powerManager) {
+ PowerManager powerManager,
+ UserTracker userTracker) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
@@ -136,6 +149,7 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
mEnhancedEstimates = enhancedEstimates;
mPowerManager = powerManager;
mWakefulnessLifecycle = wakefulnessLifecycle;
+ mUserTracker = userTracker;
}
public void start() {
@@ -154,6 +168,7 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevels();
mReceiver.init();
+ mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
// Check to see if we need to let the user know that the phone previously shut down due
@@ -250,7 +265,6 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
IntentFilter filter = new IntentFilter();
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
// Force get initial values. Relying on Sticky behavior until API for getting info.
if (!mHasReceivedBattery) {
@@ -332,8 +346,6 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
plugged, bucket);
});
- } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mWarnings.userSwitched();
} else {
Slog.w(TAG, "unknown intent: " + intent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
new file mode 100644
index 000000000000..802db7e9d0c0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
@@ -0,0 +1,208 @@
+/*
+ * 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.reardisplay;
+
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManagerGlobal;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.CoreStartable;
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+import com.airbnb.lottie.LottieAnimationView;
+import com.airbnb.lottie.LottieDrawable;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Provides an educational dialog to the user alerting them to what
+ * they may need to do to enter rear display mode. This may be to open the
+ * device if it is currently folded, or to confirm that they would like
+ * the content to move to the screen on their device that is aligned with
+ * the rear camera. This includes a device animation to provide more context
+ * to the user.
+ *
+ * We are suppressing lint for the VisibleForTests check because the use of
+ * DeviceStateManagerGlobal as in this file should not be encouraged for other use-cases.
+ * The lint check will notify any other use-cases that they are possibly doing something
+ * incorrectly.
+ */
+@SuppressLint("VisibleForTests") // TODO(b/260264542) Migrate away from DeviceStateManagerGlobal
+@SysUISingleton
+public class RearDisplayDialogController implements CoreStartable, CommandQueue.Callbacks {
+
+ private int[] mFoldedStates;
+ private boolean mStartedFolded;
+ private boolean mServiceNotified = false;
+ private int mAnimationRepeatCount = LottieDrawable.INFINITE;
+
+ private DeviceStateManagerGlobal mDeviceStateManagerGlobal;
+ private DeviceStateManager.DeviceStateCallback mDeviceStateManagerCallback =
+ new DeviceStateManagerCallback();
+
+ private final Context mContext;
+ private final CommandQueue mCommandQueue;
+ private final Executor mExecutor;
+
+ @VisibleForTesting
+ SystemUIDialog mRearDisplayEducationDialog;
+
+ @Inject
+ public RearDisplayDialogController(Context context, CommandQueue commandQueue,
+ @Main Executor executor) {
+ mContext = context;
+ mCommandQueue = commandQueue;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void start() {
+ mCommandQueue.addCallback(this);
+ }
+
+ @Override
+ public void showRearDisplayDialog(int currentBaseState) {
+ initializeValues(currentBaseState);
+ createAndShowDialog();
+ }
+
+ private void createAndShowDialog() {
+ mServiceNotified = false;
+ Context dialogContext = mRearDisplayEducationDialog.getContext();
+
+ View dialogView;
+ if (mStartedFolded) {
+ dialogView = View.inflate(dialogContext,
+ R.layout.activity_rear_display_education, null);
+ } else {
+ dialogView = View.inflate(dialogContext,
+ R.layout.activity_rear_display_education_opened, null);
+ }
+ LottieAnimationView animationView = dialogView.findViewById(
+ R.id.rear_display_folded_animation);
+ animationView.setRepeatCount(mAnimationRepeatCount);
+ mRearDisplayEducationDialog.setView(dialogView);
+
+ configureDialogButtons();
+
+ mRearDisplayEducationDialog.show();
+ }
+
+ /**
+ * Configures the buttons on the dialog depending on the starting device posture
+ */
+ private void configureDialogButtons() {
+ // If we are open, we need to provide a confirm option
+ if (!mStartedFolded) {
+ mRearDisplayEducationDialog.setPositiveButton(
+ R.string.rear_display_bottom_sheet_confirm,
+ (dialog, which) -> closeOverlayAndNotifyService(false), true);
+ }
+ mRearDisplayEducationDialog.setNegativeButton(R.string.rear_display_bottom_sheet_cancel,
+ (dialog, which) -> closeOverlayAndNotifyService(true), true);
+ mRearDisplayEducationDialog.setOnDismissListener(dialog -> {
+ // Dialog is being dismissed before we've notified the system server
+ if (!mServiceNotified) {
+ closeOverlayAndNotifyService(true);
+ }
+ });
+ }
+
+ /**
+ * Initializes properties and values we need when getting ready to show the dialog.
+ *
+ * Ensures we're not using old values from when the dialog may have been shown previously.
+ */
+ private void initializeValues(int startingBaseState) {
+ mRearDisplayEducationDialog = new SystemUIDialog(mContext);
+ if (mFoldedStates == null) {
+ mFoldedStates = mContext.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
+ }
+ mStartedFolded = isFoldedState(startingBaseState);
+ mDeviceStateManagerGlobal = DeviceStateManagerGlobal.getInstance();
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(mDeviceStateManagerCallback,
+ mExecutor);
+ }
+
+ private boolean isFoldedState(int state) {
+ for (int i = 0; i < mFoldedStates.length; i++) {
+ if (mFoldedStates[i] == state) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Closes the educational overlay, and notifies the system service if rear display mode
+ * should be cancelled or enabled.
+ */
+ private void closeOverlayAndNotifyService(boolean shouldCancelRequest) {
+ mServiceNotified = true;
+ mDeviceStateManagerGlobal.unregisterDeviceStateCallback(mDeviceStateManagerCallback);
+ mDeviceStateManagerGlobal.onStateRequestOverlayDismissed(shouldCancelRequest);
+ }
+
+ /**
+ * TestAPI to allow us to set the folded states array, instead of reading from resources.
+ */
+ @TestApi
+ void setFoldedStates(int[] foldedStates) {
+ mFoldedStates = foldedStates;
+ }
+
+ @TestApi
+ void setDeviceStateManagerCallback(
+ DeviceStateManager.DeviceStateCallback deviceStateManagerCallback) {
+ mDeviceStateManagerCallback = deviceStateManagerCallback;
+ }
+
+ @TestApi
+ void setAnimationRepeatCount(int repeatCount) {
+ mAnimationRepeatCount = repeatCount;
+ }
+
+ private class DeviceStateManagerCallback implements DeviceStateManager.DeviceStateCallback {
+ @Override
+ public void onBaseStateChanged(int state) {
+ if (mStartedFolded && !isFoldedState(state)) {
+ // We've opened the device, we can close the overlay
+ mRearDisplayEducationDialog.dismiss();
+ closeOverlayAndNotifyService(false);
+ } else if (!mStartedFolded && isFoldedState(state)) {
+ // We've closed the device, finish activity
+ mRearDisplayEducationDialog.dismiss();
+ closeOverlayAndNotifyService(true);
+ }
+ }
+
+ // We only care about physical device changes in this scenario
+ @Override
+ public void onStateChanged(int state) {}
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 2ee5f05549cf..645b1256e5f1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -51,10 +51,13 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.util.leak.RotationUtils;
@@ -76,6 +79,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
private final AccessibilityManager mAccessibilityService;
private final WindowManager mWindowManager;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final UserTracker mUserTracker;
private RequestWindowView mRequestWindow;
private int mNavBarMode;
@@ -83,12 +87,21 @@ public class ScreenPinningRequest implements View.OnClickListener,
/** ID of task to be pinned or locked. */
private int taskId;
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ clearPrompt();
+ }
+ };
+
@Inject
public ScreenPinningRequest(
Context context,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
NavigationModeController navigationModeController,
- BroadcastDispatcher broadcastDispatcher) {
+ BroadcastDispatcher broadcastDispatcher,
+ UserTracker userTracker) {
mContext = context;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mAccessibilityService = (AccessibilityManager)
@@ -97,6 +110,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
mContext.getSystemService(Context.WINDOW_SERVICE);
mNavBarMode = navigationModeController.addListener(this);
mBroadcastDispatcher = broadcastDispatcher;
+ mUserTracker = userTracker;
}
public void clearPrompt() {
@@ -228,9 +242,9 @@ public class ScreenPinningRequest implements View.OnClickListener,
}
IntentFilter filter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_SCREEN_OFF);
mBroadcastDispatcher.registerReceiver(mReceiver, filter);
+ mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
}
private void inflateView(int rotation) {
@@ -358,6 +372,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
@Override
public void onDetachedFromWindow() {
mBroadcastDispatcher.unregisterReceiver(mReceiver);
+ mUserTracker.removeCallback(mUserChangedCallback);
}
protected void onConfigurationChanged() {
@@ -388,8 +403,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
post(mUpdateLayoutRunnable);
- } else if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)
- || intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
clearPrompt();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
index f4d59a8dd6a6..db0052a4d99e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -23,8 +23,11 @@ import android.view.ViewStub
import android.view.WindowManager
import android.widget.AdapterView
import android.widget.ArrayAdapter
+import android.widget.ImageView
import android.widget.Spinner
import android.widget.TextView
+import androidx.annotation.ColorRes
+import androidx.annotation.DrawableRes
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import com.android.systemui.R
@@ -34,7 +37,9 @@ import com.android.systemui.statusbar.phone.SystemUIDialog
open class BaseScreenSharePermissionDialog(
context: Context?,
private val screenShareOptions: List<ScreenShareOption>,
- private val appName: String?
+ private val appName: String?,
+ @DrawableRes private val dialogIconDrawable: Int? = null,
+ @ColorRes private val dialogIconTint: Int? = null
) : SystemUIDialog(context), AdapterView.OnItemSelectedListener {
private lateinit var dialogTitle: TextView
private lateinit var startButton: TextView
@@ -53,10 +58,21 @@ open class BaseScreenSharePermissionDialog(
warning = findViewById(R.id.text_warning)
startButton = findViewById(R.id.button_start)
findViewById<TextView>(R.id.button_cancel).setOnClickListener { dismiss() }
+ updateIcon()
initScreenShareOptions()
createOptionsView(getOptionsViewLayoutId())
}
+ private fun updateIcon() {
+ val icon = findViewById<ImageView>(R.id.screen_share_dialog_icon)
+ if (dialogIconTint != null) {
+ icon.setColorFilter(context.getColor(dialogIconTint))
+ }
+ if (dialogIconDrawable != null) {
+ icon.setImageDrawable(context.getDrawable(dialogIconDrawable))
+ }
+ }
+
protected fun initScreenShareOptions() {
selectedScreenShareOption = screenShareOptions.first()
warning.text = warningText
@@ -69,8 +85,12 @@ open class BaseScreenSharePermissionDialog(
private fun initScreenShareSpinner() {
val options = screenShareOptions.map { context.getString(it.spinnerText) }.toTypedArray()
val adapter =
- ArrayAdapter(context.applicationContext, android.R.layout.simple_spinner_item, options)
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ ArrayAdapter(
+ context.applicationContext,
+ R.layout.screen_share_dialog_spinner_text,
+ options
+ )
+ adapter.setDropDownViewResource(R.layout.screen_share_dialog_spinner_item_text)
screenShareModeSpinner = findViewById(R.id.screen_share_mode_spinner)
screenShareModeSpinner.adapter = adapter
screenShareModeSpinner.onItemSelectedListener = this
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
index 15b0bc4a356a..c5a82ce110de 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
@@ -23,11 +23,15 @@ import com.android.systemui.R
class MediaProjectionPermissionDialog(
context: Context?,
private val onStartRecordingClicked: Runnable,
- appName: String?
-) : BaseScreenSharePermissionDialog(context, createOptionList(), appName) {
+ private val appName: String?
+) : BaseScreenSharePermissionDialog(context, createOptionList(appName), appName) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setDialogTitle(R.string.media_projection_permission_dialog_title)
+ if (appName == null) {
+ setDialogTitle(R.string.media_projection_permission_dialog_system_service_title)
+ } else {
+ setDialogTitle(R.string.media_projection_permission_dialog_title)
+ }
setStartButtonText(R.string.media_projection_permission_dialog_continue)
setStartButtonOnClickListener {
// Note that it is important to run this callback before dismissing, so that the
@@ -38,17 +42,30 @@ class MediaProjectionPermissionDialog(
}
companion object {
- private fun createOptionList(): List<ScreenShareOption> {
+ private fun createOptionList(appName: String?): List<ScreenShareOption> {
+ val singleAppWarningText =
+ if (appName == null) {
+ R.string.media_projection_permission_dialog_system_service_warning_single_app
+ } else {
+ R.string.media_projection_permission_dialog_warning_single_app
+ }
+ val entireScreenWarningText =
+ if (appName == null) {
+ R.string.media_projection_permission_dialog_system_service_warning_entire_screen
+ } else {
+ R.string.media_projection_permission_dialog_warning_entire_screen
+ }
+
return listOf(
ScreenShareOption(
- SINGLE_APP,
- R.string.media_projection_permission_dialog_option_single_app,
- R.string.media_projection_permission_dialog_warning_single_app
+ mode = ENTIRE_SCREEN,
+ spinnerText = R.string.media_projection_permission_dialog_option_entire_screen,
+ warningText = entireScreenWarningText
),
ScreenShareOption(
- ENTIRE_SCREEN,
- R.string.media_projection_permission_dialog_option_entire_screen,
- R.string.media_projection_permission_dialog_warning_entire_screen
+ mode = SINGLE_APP,
+ spinnerText = R.string.media_projection_permission_dialog_option_single_app,
+ warningText = singleAppWarningText
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index ce4e0ecee914..b8684ee30b9a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -33,13 +33,16 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.CallbackController;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -55,8 +58,10 @@ public class RecordingController
private boolean mIsRecording;
private PendingIntent mStopIntent;
private CountDownTimer mCountDownTimer = null;
- private BroadcastDispatcher mBroadcastDispatcher;
- private UserContextProvider mUserContextProvider;
+ private final Executor mMainExecutor;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final UserContextProvider mUserContextProvider;
+ private final UserTracker mUserTracker;
protected static final String INTENT_UPDATE_STATE =
"com.android.systemui.screenrecord.UPDATE_STATE";
@@ -66,12 +71,13 @@ public class RecordingController
new CopyOnWriteArrayList<>();
@VisibleForTesting
- protected final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- stopRecording();
- }
- };
+ final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ stopRecording();
+ }
+ };
@VisibleForTesting
protected final BroadcastReceiver mStateChangeReceiver = new BroadcastReceiver() {
@@ -92,10 +98,14 @@ public class RecordingController
* Create a new RecordingController
*/
@Inject
- public RecordingController(BroadcastDispatcher broadcastDispatcher,
- UserContextProvider userContextProvider) {
+ public RecordingController(@Main Executor mainExecutor,
+ BroadcastDispatcher broadcastDispatcher,
+ UserContextProvider userContextProvider,
+ UserTracker userTracker) {
+ mMainExecutor = mainExecutor;
mBroadcastDispatcher = broadcastDispatcher;
mUserContextProvider = userContextProvider;
+ mUserTracker = userTracker;
}
/** Create a dialog to show screen recording options to the user. */
@@ -139,9 +149,7 @@ public class RecordingController
}
try {
startIntent.send();
- IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(mUserChangeReceiver, userFilter, null,
- UserHandle.ALL);
+ mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
IntentFilter stateFilter = new IntentFilter(INTENT_UPDATE_STATE);
mBroadcastDispatcher.registerReceiver(mStateChangeReceiver, stateFilter, null,
@@ -211,7 +219,7 @@ public class RecordingController
public synchronized void updateState(boolean isRecording) {
if (!isRecording && mIsRecording) {
// Unregister receivers if we have stopped recording
- mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver);
+ mUserTracker.removeCallback(mUserChangedCallback);
mBroadcastDispatcher.unregisterReceiver(mStateChangeReceiver);
}
mIsRecording = isRecording;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 19bb15a5c2d8..44b18ec4639b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -46,7 +46,14 @@ class ScreenRecordPermissionDialog(
private val dialogLaunchAnimator: DialogLaunchAnimator,
private val userContextProvider: UserContextProvider,
private val onStartRecordingClicked: Runnable?
-) : BaseScreenSharePermissionDialog(context, createOptionList(), null) {
+) :
+ BaseScreenSharePermissionDialog(
+ context,
+ createOptionList(),
+ null,
+ R.drawable.ic_screenrecord,
+ R.color.screenrecord_icon_color
+ ) {
private lateinit var tapsSwitch: Switch
private lateinit var tapsView: View
private lateinit var audioSwitch: Switch
@@ -169,14 +176,14 @@ class ScreenRecordPermissionDialog(
private fun createOptionList(): List<ScreenShareOption> {
return listOf(
ScreenShareOption(
- SINGLE_APP,
- R.string.screenrecord_option_single_app,
- R.string.screenrecord_warning_single_app
- ),
- ScreenShareOption(
ENTIRE_SCREEN,
R.string.screenrecord_option_entire_screen,
R.string.screenrecord_warning_entire_screen
+ ),
+ ScreenShareOption(
+ SINGLE_APP,
+ R.string.screenrecord_option_single_app,
+ R.string.screenrecord_warning_single_app
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
index 914d29a52b53..3d39fd82ac63 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
@@ -20,11 +20,11 @@ import androidx.annotation.StringRes
import kotlin.annotation.Retention
@Retention(AnnotationRetention.SOURCE)
-@IntDef(SINGLE_APP, ENTIRE_SCREEN)
+@IntDef(ENTIRE_SCREEN, SINGLE_APP)
annotation class ScreenShareMode
-const val SINGLE_APP = 0
-const val ENTIRE_SCREEN = 1
+const val ENTIRE_SCREEN = 0
+const val SINGLE_APP = 1
class ScreenShareOption(
@ScreenShareMode val mode: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 8609e4af13f6..a6447a5bf500 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -84,6 +84,8 @@ import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import android.window.WindowContext;
import androidx.concurrent.futures.CallbackToFutureAdapter;
@@ -279,6 +281,13 @@ public class ScreenshotController {
private final ActionIntentExecutor mActionExecutor;
private final UserManager mUserManager;
+ private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
+ if (DEBUG_INPUT) {
+ Log.d(TAG, "Predictive Back callback dispatched");
+ }
+ respondToBack();
+ };
+
private ScreenshotView mScreenshotView;
private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
@@ -465,6 +474,10 @@ public class ScreenshotController {
}
}
+ private void respondToBack() {
+ dismissScreenshot(SCREENSHOT_DISMISSED_OTHER);
+ }
+
/**
* Update resources on configuration change. Reinflate for theme/color changes.
*/
@@ -476,6 +489,26 @@ public class ScreenshotController {
// Inflate the screenshot layout
mScreenshotView = (ScreenshotView)
LayoutInflater.from(mContext).inflate(R.layout.screenshot, null);
+ mScreenshotView.addOnAttachStateChangeListener(
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(@NonNull View v) {
+ if (DEBUG_INPUT) {
+ Log.d(TAG, "Registering Predictive Back callback");
+ }
+ mScreenshotView.findOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(@NonNull View v) {
+ if (DEBUG_INPUT) {
+ Log.d(TAG, "Unregistering Predictive Back callback");
+ }
+ mScreenshotView.findOnBackInvokedDispatcher()
+ .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
+ }
+ });
mScreenshotView.init(mUiEventLogger, new ScreenshotView.ScreenshotViewCallback() {
@Override
public void onUserInteraction() {
@@ -503,7 +536,7 @@ public class ScreenshotController {
if (DEBUG_INPUT) {
Log.d(TAG, "onKeyEvent: KeyEvent.KEYCODE_BACK");
}
- dismissScreenshot(SCREENSHOT_DISMISSED_OTHER);
+ respondToBack();
return true;
}
return false;
@@ -546,9 +579,16 @@ public class ScreenshotController {
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, ComponentName topComponent, boolean showFlash, UserHandle owner) {
- withWindowAttached(() ->
+ withWindowAttached(() -> {
+ if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+ && mUserManager.isManagedProfile(owner.getIdentifier())) {
+ mScreenshotView.announceForAccessibility(mContext.getResources().getString(
+ R.string.screenshot_saving_work_profile_title));
+ } else {
mScreenshotView.announceForAccessibility(
- mContext.getResources().getString(R.string.screenshot_saving_title)));
+ mContext.getResources().getString(R.string.screenshot_saving_title));
+ }
+ });
mScreenshotView.reset();
@@ -972,13 +1012,8 @@ public class ScreenshotController {
if (imageData.uri != null) {
if (!imageData.owner.equals(Process.myUserHandle())) {
- // TODO: Handle non-primary user ownership (e.g. Work Profile)
- // This image is owned by another user. Special treatment will be
- // required in the UI (badging) as well as sending intents which can
- // correctly forward those URIs on to be read (actions).
-
- Log.d(TAG, "*** Screenshot saved to a non-primary user ("
- + imageData.owner + ") as " + imageData.uri);
+ Log.d(TAG, "Screenshot saved to user " + imageData.owner + " as "
+ + imageData.uri);
}
mScreenshotHandler.post(() -> {
if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
@@ -1059,6 +1094,11 @@ public class ScreenshotController {
R.string.screenshot_failed_to_save_text);
} else {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName);
+ if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+ && mUserManager.isManagedProfile(imageData.owner.getIdentifier())) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED_TO_WORK_PROFILE, 0,
+ mPackageName);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 7641554ede3d..fae938d542f1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -825,12 +825,23 @@ public class ScreenshotView extends FrameLayout implements
}
});
if (mQuickShareChip != null) {
- mQuickShareChip.setPendingIntent(imageData.quickShareAction.actionIntent,
- () -> {
- mUiEventLogger.log(
- ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED, 0, mPackageName);
- animateDismissal();
- });
+ if (imageData.quickShareAction != null) {
+ mQuickShareChip.setPendingIntent(imageData.quickShareAction.actionIntent,
+ () -> {
+ mUiEventLogger.log(
+ ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED, 0,
+ mPackageName);
+ animateDismissal();
+ });
+ } else {
+ // hide chip and unset pending interaction if necessary, since we don't actually
+ // have a useable quick share intent
+ Log.wtf(TAG, "Showed quick share chip, but quick share intent was null");
+ if (mPendingInteraction == PendingInteraction.QUICK_SHARE) {
+ mPendingInteraction = null;
+ }
+ mQuickShareChip.setVisibility(GONE);
+ }
}
if (mPendingInteraction != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
index d450afa59c7d..bfba6dfddfac 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
@@ -35,12 +35,14 @@ import java.io.File
import javax.inject.Inject
/**
- * Implementation for retrieving file paths for file storage of system and secondary users.
- * Files lie in {File Directory}/UserFileManager/{User Id} for secondary user.
- * For system user, we use the conventional {File Directory}
+ * Implementation for retrieving file paths for file storage of system and secondary users. Files
+ * lie in {File Directory}/UserFileManager/{User Id} for secondary user. For system user, we use the
+ * conventional {File Directory}
*/
@SysUISingleton
-class UserFileManagerImpl @Inject constructor(
+class UserFileManagerImpl
+@Inject
+constructor(
// Context of system process and system user.
private val context: Context,
val userManager: UserManager,
@@ -49,80 +51,114 @@ class UserFileManagerImpl @Inject constructor(
) : UserFileManager, CoreStartable {
companion object {
private const val FILES = "files"
- @VisibleForTesting internal const val SHARED_PREFS = "shared_prefs"
+ const val SHARED_PREFS = "shared_prefs"
@VisibleForTesting internal const val ID = "UserFileManager"
- }
- private val broadcastReceiver = object : BroadcastReceiver() {
+ /** Returns `true` if the given user ID is that for the primary/system user. */
+ fun isPrimaryUser(userId: Int): Boolean {
+ return UserHandle(userId).isSystem
+ }
+
/**
- * Listen to Intent.ACTION_USER_REMOVED to clear user data.
+ * Returns a [File] pointing to the correct path for a secondary user ID.
+ *
+ * Note that there is no check for the type of user. This should only be called for
+ * secondary users, never for the system user. For that, make sure to call [isPrimaryUser].
+ *
+ * Note also that there is no guarantee that the parent directory structure for the file
+ * exists on disk. For that, call [ensureParentDirExists].
+ *
+ * @param context The context
+ * @param fileName The name of the file
+ * @param directoryName The name of the directory that would contain the file
+ * @param userId The ID of the user to build a file path for
*/
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action == Intent.ACTION_USER_REMOVED) {
- clearDeletedUserData()
+ fun secondaryUserFile(
+ context: Context,
+ fileName: String,
+ directoryName: String,
+ userId: Int,
+ ): File {
+ return Environment.buildPath(
+ context.filesDir,
+ ID,
+ userId.toString(),
+ directoryName,
+ fileName,
+ )
+ }
+
+ /**
+ * Checks to see if parent dir of the file exists. If it does not, we create the parent dirs
+ * recursively.
+ */
+ fun ensureParentDirExists(file: File) {
+ val parent = file.parentFile
+ if (!parent.exists()) {
+ if (!parent.mkdirs()) {
+ Log.e(ID, "Could not create parent directory for file: ${file.absolutePath}")
+ }
}
}
}
- /**
- * Poll for user-specific directories to delete upon start up.
- */
+ private val broadcastReceiver =
+ object : BroadcastReceiver() {
+ /** Listen to Intent.ACTION_USER_REMOVED to clear user data. */
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == Intent.ACTION_USER_REMOVED) {
+ clearDeletedUserData()
+ }
+ }
+ }
+
+ /** Poll for user-specific directories to delete upon start up. */
override fun start() {
clearDeletedUserData()
- val filter = IntentFilter().apply {
- addAction(Intent.ACTION_USER_REMOVED)
- }
+ val filter = IntentFilter().apply { addAction(Intent.ACTION_USER_REMOVED) }
broadcastDispatcher.registerReceiver(broadcastReceiver, filter, backgroundExecutor)
}
- /**
- * Return the file based on current user.
- */
+ /** Return the file based on current user. */
override fun getFile(fileName: String, userId: Int): File {
- return if (UserHandle(userId).isSystem) {
- Environment.buildPath(
- context.filesDir,
- fileName
- )
+ return if (isPrimaryUser(userId)) {
+ Environment.buildPath(context.filesDir, fileName)
} else {
- val secondaryFile = Environment.buildPath(
- context.filesDir,
- ID,
- userId.toString(),
- FILES,
- fileName
- )
+ val secondaryFile =
+ secondaryUserFile(
+ context = context,
+ userId = userId,
+ directoryName = FILES,
+ fileName = fileName,
+ )
ensureParentDirExists(secondaryFile)
secondaryFile
}
}
- /**
- * Get shared preferences from user.
- */
+ /** Get shared preferences from user. */
override fun getSharedPreferences(
fileName: String,
@Context.PreferencesMode mode: Int,
userId: Int
): SharedPreferences {
- if (UserHandle(userId).isSystem) {
+ if (isPrimaryUser(userId)) {
return context.getSharedPreferences(fileName, mode)
}
- val secondaryUserDir = Environment.buildPath(
- context.filesDir,
- ID,
- userId.toString(),
- SHARED_PREFS,
- fileName
- )
+
+ val secondaryUserDir =
+ secondaryUserFile(
+ context = context,
+ fileName = fileName,
+ directoryName = SHARED_PREFS,
+ userId = userId,
+ )
ensureParentDirExists(secondaryUserDir)
return context.getSharedPreferences(secondaryUserDir, mode)
}
- /**
- * Remove dirs for deleted users.
- */
+ /** Remove dirs for deleted users. */
@VisibleForTesting
internal fun clearDeletedUserData() {
backgroundExecutor.execute {
@@ -133,10 +169,11 @@ class UserFileManagerImpl @Inject constructor(
dirsToDelete.forEach { dir ->
try {
- val dirToDelete = Environment.buildPath(
- file,
- dir,
- )
+ val dirToDelete =
+ Environment.buildPath(
+ file,
+ dir,
+ )
dirToDelete.deleteRecursively()
} catch (e: Exception) {
Log.e(ID, "Deletion failed.", e)
@@ -144,18 +181,4 @@ class UserFileManagerImpl @Inject constructor(
}
}
}
-
- /**
- * Checks to see if parent dir of the file exists. If it does not, we create the parent dirs
- * recursively.
- */
- @VisibleForTesting
- internal fun ensureParentDirExists(file: File) {
- val parent = file.parentFile
- if (!parent.exists()) {
- if (!parent.mkdirs()) {
- Log.e(ID, "Could not create parent directory for file: ${file.absolutePath}")
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 31e44646859f..5e47d6dd5b76 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -19,6 +19,7 @@ package com.android.systemui.shade
import android.annotation.IdRes
import android.app.StatusBarManager
import android.content.res.Configuration
+import android.os.Bundle
import android.os.Trace
import android.os.Trace.TRACE_TAG_APP
import android.util.Pair
@@ -34,6 +35,8 @@ import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.demomode.DemoMode
+import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -53,6 +56,7 @@ import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_BATTERY_CONTROLLER
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_SHADE_HEADER
+import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.VariableDateView
import com.android.systemui.statusbar.policy.VariableDateViewController
@@ -89,7 +93,8 @@ class LargeScreenShadeHeaderController @Inject constructor(
private val dumpManager: DumpManager,
private val featureFlags: FeatureFlags,
private val qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
- private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager
+ private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager,
+ private val demoModeController: DemoModeController
) : ViewController<View>(header), Dumpable {
companion object {
@@ -126,7 +131,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
private lateinit var qsCarrierGroupController: QSCarrierGroupController
private val batteryIcon: BatteryMeterView = header.findViewById(R.id.batteryRemainingIcon)
- private val clock: TextView = header.findViewById(R.id.clock)
+ private val clock: Clock = header.findViewById(R.id.clock)
private val date: TextView = header.findViewById(R.id.date)
private val iconContainer: StatusIconContainer = header.findViewById(R.id.statusIcons)
private val qsCarrierGroup: QSCarrierGroup = header.findViewById(R.id.carrier_group)
@@ -212,6 +217,14 @@ class LargeScreenShadeHeaderController @Inject constructor(
view.onApplyWindowInsets(insets)
}
+ private val demoModeReceiver = object : DemoMode {
+ override fun demoCommands() = listOf(DemoMode.COMMAND_CLOCK)
+ override fun dispatchDemoCommand(command: String, args: Bundle) =
+ clock.dispatchDemoCommand(command, args)
+ override fun onDemoModeStarted() = clock.onDemoModeStarted()
+ override fun onDemoModeFinished() = clock.onDemoModeFinished()
+ }
+
private val chipVisibilityListener: ChipVisibilityListener = object : ChipVisibilityListener {
override fun onChipVisibilityRefreshed(visible: Boolean) {
if (header is MotionLayout) {
@@ -300,6 +313,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
dumpManager.registerDumpable(this)
configurationController.addCallback(configurationControllerListener)
+ demoModeController.addCallback(demoModeReceiver)
updateVisibility()
updateTransition()
@@ -309,6 +323,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
privacyIconsController.chipVisibilityListener = null
dumpManager.unregisterDumpable(this::class.java.simpleName)
configurationController.removeCallback(configurationControllerListener)
+ demoModeController.removeCallback(demoModeReceiver)
}
fun disable(state1: Int, state2: Int, animate: Boolean) {
@@ -521,4 +536,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
updateConstraints(LARGE_SCREEN_HEADER_CONSTRAINT, updates.largeScreenConstraintsChanges)
}
}
+
+ @VisibleForTesting
+ internal fun simulateViewDetached() = this.onViewDetached()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index e68182ef85c9..f52ace7138f2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -393,6 +393,9 @@ public final class NotificationPanelViewController implements Dumpable {
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private int mQsTrackingPointer;
private VelocityTracker mQsVelocityTracker;
+ private TrackingStartedListener mTrackingStartedListener;
+ private OpenCloseListener mOpenCloseListener;
+ private GestureRecorder mGestureRecorder;
private boolean mQsTracking;
/** Whether the ongoing gesture might both trigger the expansion in both the view and QS. */
private boolean mConflictingQsExpansionGesture;
@@ -1362,6 +1365,14 @@ public final class NotificationPanelViewController implements Dumpable {
mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
}
+ void setOpenCloseListener(OpenCloseListener openCloseListener) {
+ mOpenCloseListener = openCloseListener;
+ }
+
+ void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
+ mTrackingStartedListener = trackingStartedListener;
+ }
+
private void updateGestureExclusionRect() {
Rect exclusionRect = calculateGestureExclusionRect();
mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.emptyList()
@@ -1936,9 +1947,9 @@ public final class NotificationPanelViewController implements Dumpable {
}
private void fling(float vel) {
- GestureRecorder gr = mCentralSurfaces.getGestureRecorder();
- if (gr != null) {
- gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
+ if (mGestureRecorder != null) {
+ mGestureRecorder.tag("fling " + ((vel > 0) ? "open" : "closed"),
+ "notifications,v=" + vel);
}
fling(vel, true, 1.0f /* collapseSpeedUpFactor */, false);
}
@@ -2072,6 +2083,14 @@ public final class NotificationPanelViewController implements Dumpable {
mInitialTouchX = x;
initVelocityTracker();
trackMovement(event);
+ float qsExpansionFraction = computeQsExpansionFraction();
+ // Intercept the touch if QS is between fully collapsed and fully expanded state
+ if (!mSplitShadeEnabled
+ && qsExpansionFraction > 0.0 && qsExpansionFraction < 1.0) {
+ mShadeLog.logMotionEvent(event,
+ "onQsIntercept: down action, QS partially expanded/collapsed");
+ return true;
+ }
if (mKeyguardShowing
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
// Dragging down on the lockscreen statusbar should prohibit other interactions
@@ -2324,6 +2343,14 @@ public final class NotificationPanelViewController implements Dumpable {
if (!isFullyCollapsed()) {
handleQsDown(event);
}
+ // defer touches on QQS to shade while shade is collapsing. Added margin for error
+ // as sometimes the qsExpansionFraction can be a tiny value instead of 0 when in QQS.
+ if (!mSplitShadeEnabled
+ && computeQsExpansionFraction() <= 0.01 && getExpandedFraction() < 1.0) {
+ mShadeLog.logMotionEvent(event,
+ "handleQsTouch: QQS touched while shade collapsing");
+ mQsTracking = false;
+ }
if (!mQsExpandImmediate && mQsTracking) {
onQsTouch(event);
if (!mConflictingQsExpansionGesture && !mSplitShadeEnabled) {
@@ -2564,7 +2591,6 @@ public final class NotificationPanelViewController implements Dumpable {
// Reset scroll position and apply that position to the expanded height.
float height = mQsExpansionHeight;
setQsExpansionHeight(height);
- updateExpandedHeightToMaxHeight();
mNotificationStackScrollLayoutController.checkSnoozeLeavebehind();
// When expanding QS, let's authenticate the user if possible,
@@ -3711,7 +3737,7 @@ public final class NotificationPanelViewController implements Dumpable {
mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
endClosing();
mTracking = true;
- mCentralSurfaces.onTrackingStarted();
+ mTrackingStartedListener.onTrackingStarted();
notifyExpandingStarted();
updatePanelExpansionAndVisibility();
mScrimController.onTrackingStarted();
@@ -3945,7 +3971,7 @@ public final class NotificationPanelViewController implements Dumpable {
}
private void onClosingFinished() {
- mCentralSurfaces.onClosingFinished();
+ mOpenCloseListener.onClosingFinished();
setClosingWithAlphaFadeout(false);
mMediaHierarchyManager.closeGuts();
}
@@ -4504,11 +4530,13 @@ public final class NotificationPanelViewController implements Dumpable {
*/
public void initDependencies(
CentralSurfaces centralSurfaces,
+ GestureRecorder recorder,
Runnable hideExpandedRunnable,
NotificationShelfController notificationShelfController) {
// TODO(b/254859580): this can be injected.
mCentralSurfaces = centralSurfaces;
+ mGestureRecorder = recorder;
mHideExpandedRunnable = hideExpandedRunnable;
mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
mNotificationShelfController = notificationShelfController;
@@ -4590,20 +4618,20 @@ public final class NotificationPanelViewController implements Dumpable {
return false;
}
- // If the view that would receive the touch is disabled, just have status bar
- // eat the gesture.
- if (event.getAction() == MotionEvent.ACTION_DOWN && !mView.isEnabled()) {
- Log.v(TAG,
- String.format(
- "onTouchForwardedFromStatusBar: "
- + "panel view disabled, eating touch at (%d,%d)",
- (int) event.getX(),
- (int) event.getY()
- )
- );
- return true;
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ // If the view that would receive the touch is disabled, just have status
+ // bar eat the gesture.
+ if (!mView.isEnabled()) {
+ mShadeLog.logMotionEvent(event,
+ "onTouchForwardedFromStatusBar: panel view disabled");
+ return true;
+ }
+ if (isFullyCollapsed() && event.getY() < 1f) {
+ // b/235889526 Eat events on the top edge of the phone when collapsed
+ mShadeLog.logMotionEvent(event, "top edge touch ignored");
+ return true;
+ }
}
-
return mView.dispatchTouchEvent(event);
}
};
@@ -5757,7 +5785,7 @@ public final class NotificationPanelViewController implements Dumpable {
if (mSplitShadeEnabled && !isOnKeyguard()) {
setQsExpandImmediate(true);
}
- mCentralSurfaces.makeExpandedVisible(false);
+ mOpenCloseListener.onOpenStarted();
}
if (state == STATE_CLOSED) {
setQsExpandImmediate(false);
@@ -6240,4 +6268,17 @@ public final class NotificationPanelViewController implements Dumpable {
return super.performAccessibilityAction(host, action, args);
}
}
+
+ /** Listens for when touch tracking begins. */
+ interface TrackingStartedListener {
+ void onTrackingStarted();
+ }
+
+ /** Listens for when shade begins opening of finishes closing. */
+ interface OpenCloseListener {
+ /** Called when the shade finishes closing. */
+ void onClosingFinished();
+ /** Called when the shade starts opening. */
+ void onOpenStarted();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index aa610bdcc90e..a41a15d4e2a2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -16,6 +16,9 @@
package com.android.systemui.shade;
+import android.view.MotionEvent;
+
+import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -29,31 +32,32 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
*/
public interface ShadeController {
- /**
- * Make our window larger and the panel expanded
- */
- void instantExpandNotificationsPanel();
+ /** Make our window larger and the shade expanded */
+ void instantExpandShade();
+
+ /** Collapse the shade instantly with no animation. */
+ void instantCollapseShade();
+
+ /** See {@link #animateCollapsePanels(int, boolean, boolean, float)}. */
+ void animateCollapseShade();
+
+ /** See {@link #animateCollapsePanels(int, boolean, boolean, float)}. */
+ void animateCollapseShade(int flags);
- /** See {@link #animateCollapsePanels(int, boolean)}. */
- void animateCollapsePanels();
+ /** See {@link #animateCollapsePanels(int, boolean, boolean, float)}. */
+ void animateCollapseShadeForced();
- /** See {@link #animateCollapsePanels(int, boolean)}. */
- void animateCollapsePanels(int flags);
+ /** See {@link #animateCollapsePanels(int, boolean, boolean, float)}. */
+ void animateCollapseShadeDelayed();
/**
* Collapse the shade animated, showing the bouncer when on {@link StatusBarState#KEYGUARD} or
- * dismissing {@link CentralSurfaces} when on {@link StatusBarState#SHADE}.
+ * dismissing status bar when on {@link StatusBarState#SHADE}.
*/
- void animateCollapsePanels(int flags, boolean force);
-
- /** See {@link #animateCollapsePanels(int, boolean)}. */
- void animateCollapsePanels(int flags, boolean force, boolean delayed);
-
- /** See {@link #animateCollapsePanels(int, boolean)}. */
void animateCollapsePanels(int flags, boolean force, boolean delayed, float speedUpFactor);
/**
- * If the notifications panel is not fully expanded, collapse it animated.
+ * If the shade is not fully expanded, collapse it animated.
*
* @return Seems to always return false
*/
@@ -77,9 +81,7 @@ public interface ShadeController {
*/
void addPostCollapseAction(Runnable action);
- /**
- * Run all of the runnables added by {@link #addPostCollapseAction}.
- */
+ /** Run all of the runnables added by {@link #addPostCollapseAction}. */
void runPostCollapseRunnables();
/**
@@ -87,13 +89,51 @@ public interface ShadeController {
*
* @return true if the shade was open, else false
*/
- boolean collapsePanel();
+ boolean collapseShade();
/**
- * If animate is true, does the same as {@link #collapsePanel()}. Otherwise, instantly collapse
- * the panel. Post collapse runnables will be executed
+ * If animate is true, does the same as {@link #collapseShade()}. Otherwise, instantly collapse
+ * the shade. Post collapse runnables will be executed
*
* @param animate true to animate the collapse, false for instantaneous collapse
*/
- void collapsePanel(boolean animate);
+ void collapseShade(boolean animate);
+
+ /** Makes shade expanded but not visible. */
+ void makeExpandedInvisible();
+
+ /** Makes shade expanded and visible. */
+ void makeExpandedVisible(boolean force);
+
+ /** Returns whether the shade is expanded and visible. */
+ boolean isExpandedVisible();
+
+ /** Handle status bar touch event. */
+ void onStatusBarTouch(MotionEvent event);
+
+ /** Called when the shade finishes collapsing. */
+ void onClosingFinished();
+
+ /** Sets the listener for when the visibility of the shade changes. */
+ void setVisibilityListener(ShadeVisibilityListener listener);
+
+ /** */
+ void setNotificationPresenter(NotificationPresenter presenter);
+
+ /** */
+ void setNotificationShadeWindowViewController(
+ NotificationShadeWindowViewController notificationShadeWindowViewController);
+
+ /** */
+ void setNotificationPanelViewController(
+ NotificationPanelViewController notificationPanelViewController);
+
+ /** Listens for shade visibility changes. */
+ interface ShadeVisibilityListener {
+ /** Called when the visibility of the shade changes. */
+ void visibilityChanged(boolean visible);
+
+ /** Called when shade expanded and visible state changed. */
+ void expandedVisibleChanged(boolean expandedVisible);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index d783293b95d4..638e74883f23 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -16,9 +16,12 @@
package com.android.systemui.shade;
+import android.content.ComponentCallbacks2;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
@@ -27,11 +30,12 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import java.util.ArrayList;
-import java.util.Optional;
import javax.inject.Inject;
@@ -39,68 +43,81 @@ import dagger.Lazy;
/** An implementation of {@link ShadeController}. */
@SysUISingleton
-public class ShadeControllerImpl implements ShadeController {
+public final class ShadeControllerImpl implements ShadeController {
private static final String TAG = "ShadeControllerImpl";
private static final boolean SPEW = false;
+ private final int mDisplayId;
+
private final CommandQueue mCommandQueue;
+ private final KeyguardStateController mKeyguardStateController;
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarStateController mStatusBarStateController;
- protected final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private final int mDisplayId;
- protected final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+ private final StatusBarWindowController mStatusBarWindowController;
+
private final Lazy<AssistManager> mAssistManagerLazy;
+ private final Lazy<NotificationGutsManager> mGutsManager;
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
+ private boolean mExpandedVisible;
+
+ private NotificationPanelViewController mNotificationPanelViewController;
+ private NotificationPresenter mPresenter;
+ private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
+ private ShadeVisibilityListener mShadeVisibilityListener;
+
@Inject
public ShadeControllerImpl(
CommandQueue commandQueue,
+ KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController,
- NotificationShadeWindowController notificationShadeWindowController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ StatusBarWindowController statusBarWindowController,
+ NotificationShadeWindowController notificationShadeWindowController,
WindowManager windowManager,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
- Lazy<AssistManager> assistManagerLazy
+ Lazy<AssistManager> assistManagerLazy,
+ Lazy<NotificationGutsManager> gutsManager
) {
mCommandQueue = commandQueue;
mStatusBarStateController = statusBarStateController;
+ mStatusBarWindowController = statusBarWindowController;
+ mGutsManager = gutsManager;
mNotificationShadeWindowController = notificationShadeWindowController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mDisplayId = windowManager.getDefaultDisplay().getDisplayId();
- // TODO: Remove circular reference to CentralSurfaces when possible.
- mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+ mKeyguardStateController = keyguardStateController;
mAssistManagerLazy = assistManagerLazy;
}
@Override
- public void instantExpandNotificationsPanel() {
+ public void instantExpandShade() {
// Make our window larger and the panel expanded.
- getCentralSurfaces().makeExpandedVisible(true /* force */);
- getNotificationPanelViewController().expand(false /* animate */);
+ makeExpandedVisible(true /* force */);
+ mNotificationPanelViewController.expand(false /* animate */);
mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
}
@Override
- public void animateCollapsePanels() {
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+ public void animateCollapseShade() {
+ animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE);
}
@Override
- public void animateCollapsePanels(int flags) {
- animateCollapsePanels(flags, false /* force */, false /* delayed */,
- 1.0f /* speedUpFactor */);
+ public void animateCollapseShade(int flags) {
+ animateCollapsePanels(flags, false, false, 1.0f);
}
@Override
- public void animateCollapsePanels(int flags, boolean force) {
- animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */);
+ public void animateCollapseShadeForced() {
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true, false, 1.0f);
}
@Override
- public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
- animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */);
+ public void animateCollapseShadeDelayed() {
+ animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true, true, 1.0f);
}
@Override
@@ -111,34 +128,26 @@ public class ShadeControllerImpl implements ShadeController {
return;
}
if (SPEW) {
- Log.d(TAG, "animateCollapse():"
- + " mExpandedVisible=" + getCentralSurfaces().isExpandedVisible()
- + " flags=" + flags);
+ Log.d(TAG,
+ "animateCollapse(): mExpandedVisible=" + mExpandedVisible + "flags=" + flags);
}
-
- // TODO(b/62444020): remove when this bug is fixed
- Log.v(TAG, "NotificationShadeWindow: " + getNotificationShadeWindowView()
- + " canPanelBeCollapsed(): "
- + getNotificationPanelViewController().canPanelBeCollapsed());
if (getNotificationShadeWindowView() != null
- && getNotificationPanelViewController().canPanelBeCollapsed()
+ && mNotificationPanelViewController.canPanelBeCollapsed()
&& (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
// release focus immediately to kick off focus change transition
mNotificationShadeWindowController.setNotificationShadeFocusable(false);
- getCentralSurfaces().getNotificationShadeWindowViewController().cancelExpandHelper();
- getNotificationPanelViewController()
- .collapsePanel(true /* animate */, delayed, speedUpFactor);
+ mNotificationShadeWindowViewController.cancelExpandHelper();
+ mNotificationPanelViewController.collapsePanel(true, delayed, speedUpFactor);
}
}
-
@Override
public boolean closeShadeIfOpen() {
- if (!getNotificationPanelViewController().isFullyCollapsed()) {
+ if (!mNotificationPanelViewController.isFullyCollapsed()) {
mCommandQueue.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
- getCentralSurfaces().visibilityChanged(false);
+ notifyVisibilityChanged(false);
mAssistManagerLazy.get().hideAssist();
}
return false;
@@ -146,21 +155,19 @@ public class ShadeControllerImpl implements ShadeController {
@Override
public boolean isShadeOpen() {
- NotificationPanelViewController controller =
- getNotificationPanelViewController();
- return controller.isExpanding() || controller.isFullyExpanded();
+ return mNotificationPanelViewController.isExpanding()
+ || mNotificationPanelViewController.isFullyExpanded();
}
@Override
public void postOnShadeExpanded(Runnable executable) {
- getNotificationPanelViewController().addOnGlobalLayoutListener(
+ mNotificationPanelViewController.addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
- if (getCentralSurfaces().getNotificationShadeWindowView()
- .isVisibleToUser()) {
- getNotificationPanelViewController().removeOnGlobalLayoutListener(this);
- getNotificationPanelViewController().postToView(executable);
+ if (getNotificationShadeWindowView().isVisibleToUser()) {
+ mNotificationPanelViewController.removeOnGlobalLayoutListener(this);
+ mNotificationPanelViewController.postToView(executable);
}
}
});
@@ -183,12 +190,11 @@ public class ShadeControllerImpl implements ShadeController {
}
@Override
- public boolean collapsePanel() {
- if (!getNotificationPanelViewController().isFullyCollapsed()) {
+ public boolean collapseShade() {
+ if (!mNotificationPanelViewController.isFullyCollapsed()) {
// close the shade if it was open
- animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed */);
- getCentralSurfaces().visibilityChanged(false);
+ animateCollapseShadeDelayed();
+ notifyVisibilityChanged(false);
return true;
} else {
@@ -197,33 +203,154 @@ public class ShadeControllerImpl implements ShadeController {
}
@Override
- public void collapsePanel(boolean animate) {
+ public void collapseShade(boolean animate) {
if (animate) {
- boolean willCollapse = collapsePanel();
+ boolean willCollapse = collapseShade();
if (!willCollapse) {
runPostCollapseRunnables();
}
- } else if (!getPresenter().isPresenterFullyCollapsed()) {
- getCentralSurfaces().instantCollapseNotificationPanel();
- getCentralSurfaces().visibilityChanged(false);
+ } else if (!mPresenter.isPresenterFullyCollapsed()) {
+ instantCollapseShade();
+ notifyVisibilityChanged(false);
} else {
runPostCollapseRunnables();
}
}
- private CentralSurfaces getCentralSurfaces() {
- return mCentralSurfacesOptionalLazy.get().get();
+ @Override
+ public void onStatusBarTouch(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (mExpandedVisible) {
+ animateCollapseShade();
+ }
+ }
}
- private NotificationPresenter getPresenter() {
- return getCentralSurfaces().getPresenter();
+ @Override
+ public void onClosingFinished() {
+ runPostCollapseRunnables();
+ if (!mPresenter.isPresenterFullyCollapsed()) {
+ // if we set it not to be focusable when collapsing, we have to undo it when we aborted
+ // the closing
+ mNotificationShadeWindowController.setNotificationShadeFocusable(true);
+ }
}
- protected NotificationShadeWindowView getNotificationShadeWindowView() {
- return getCentralSurfaces().getNotificationShadeWindowView();
+ @Override
+ public void instantCollapseShade() {
+ mNotificationPanelViewController.instantCollapse();
+ runPostCollapseRunnables();
+ }
+
+ @Override
+ public void makeExpandedVisible(boolean force) {
+ if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
+ if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) {
+ return;
+ }
+
+ mExpandedVisible = true;
+
+ // Expand the window to encompass the full screen in anticipation of the drag.
+ // It's only possible to do atomically because the status bar is at the top of the screen!
+ mNotificationShadeWindowController.setPanelVisible(true);
+
+ notifyVisibilityChanged(true);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, !force /* animate */);
+ notifyExpandedVisibleChanged(true);
}
- private NotificationPanelViewController getNotificationPanelViewController() {
- return getCentralSurfaces().getNotificationPanelViewController();
+ @Override
+ public void makeExpandedInvisible() {
+ if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible);
+
+ if (!mExpandedVisible || getNotificationShadeWindowView() == null) {
+ return;
+ }
+
+ // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
+ mNotificationPanelViewController.collapsePanel(false, false, 1.0f);
+
+ mNotificationPanelViewController.closeQs();
+
+ mExpandedVisible = false;
+ notifyVisibilityChanged(false);
+
+ // Update the visibility of notification shade and status bar window.
+ mNotificationShadeWindowController.setPanelVisible(false);
+ mStatusBarWindowController.setForceStatusBarVisible(false);
+
+ // Close any guts that might be visible
+ mGutsManager.get().closeAndSaveGuts(
+ true /* removeLeavebehind */,
+ true /* force */,
+ true /* removeControls */,
+ -1 /* x */,
+ -1 /* y */,
+ true /* resetMenu */);
+
+ runPostCollapseRunnables();
+ notifyExpandedVisibleChanged(false);
+ mCommandQueue.recomputeDisableFlags(
+ mDisplayId,
+ mNotificationPanelViewController.hideStatusBarIconsWhenExpanded() /* animate */);
+
+ // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
+ // the bouncer appear animation.
+ if (!mKeyguardStateController.isShowing()) {
+ WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+ }
+ }
+
+ @Override
+ public boolean isExpandedVisible() {
+ return mExpandedVisible;
+ }
+
+ @Override
+ public void setVisibilityListener(ShadeVisibilityListener listener) {
+ mShadeVisibilityListener = listener;
+ }
+
+ private void notifyVisibilityChanged(boolean visible) {
+ mShadeVisibilityListener.visibilityChanged(visible);
+ }
+
+ private void notifyExpandedVisibleChanged(boolean expandedVisible) {
+ mShadeVisibilityListener.expandedVisibleChanged(expandedVisible);
+ }
+
+ @Override
+ public void setNotificationPresenter(NotificationPresenter presenter) {
+ mPresenter = presenter;
+ }
+
+ @Override
+ public void setNotificationShadeWindowViewController(
+ NotificationShadeWindowViewController controller) {
+ mNotificationShadeWindowViewController = controller;
+ }
+
+ private NotificationShadeWindowView getNotificationShadeWindowView() {
+ return mNotificationShadeWindowViewController.getView();
+ }
+
+ @Override
+ public void setNotificationPanelViewController(
+ NotificationPanelViewController notificationPanelViewController) {
+ mNotificationPanelViewController = notificationPanelViewController;
+ mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables);
+ mNotificationPanelViewController.setOpenCloseListener(
+ new NotificationPanelViewController.OpenCloseListener() {
+ @Override
+ public void onClosingFinished() {
+ ShadeControllerImpl.this.onClosingFinished();
+ }
+
+ @Override
+ public void onOpenStarted() {
+ makeExpandedVisible(false);
+ }
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 3d161d9bfa75..24c66eb5d442 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -17,9 +17,12 @@
package com.android.systemui.statusbar;
import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
import android.annotation.StringRes;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
@@ -33,16 +36,30 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
public class EmptyShadeView extends StackScrollerDecorView {
private TextView mEmptyText;
+ private TextView mEmptyFooterText;
+
private @StringRes int mText = R.string.empty_shade_text;
+ private @DrawableRes int mFooterIcon = R.drawable.ic_friction_lock_closed;
+ private @StringRes int mFooterText = R.string.unlock_to_see_notif_text;
+ private @Visibility int mFooterVisibility = View.GONE;
+ private int mSize;
+
public EmptyShadeView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mSize = getResources().getDimensionPixelSize(
+ R.dimen.notifications_unseen_footer_icon_size);
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ mSize = getResources().getDimensionPixelSize(
+ R.dimen.notifications_unseen_footer_icon_size);
mEmptyText.setText(mText);
+ mEmptyFooterText.setVisibility(mFooterVisibility);
+ setFooterText(mFooterText);
+ setFooterIcon(mFooterIcon);
}
@Override
@@ -52,11 +69,13 @@ public class EmptyShadeView extends StackScrollerDecorView {
@Override
protected View findSecondaryView() {
- return null;
+ return findViewById(R.id.no_notifications_footer);
}
public void setTextColor(@ColorInt int color) {
mEmptyText.setTextColor(color);
+ mEmptyFooterText.setTextColor(color);
+ mEmptyFooterText.setCompoundDrawableTintList(ColorStateList.valueOf(color));
}
public void setText(@StringRes int text) {
@@ -64,14 +83,53 @@ public class EmptyShadeView extends StackScrollerDecorView {
mEmptyText.setText(mText);
}
+ public void setFooterVisibility(@Visibility int visibility) {
+ mFooterVisibility = visibility;
+ setSecondaryVisible(visibility == View.VISIBLE, false);
+ }
+
+ public void setFooterText(@StringRes int text) {
+ mFooterText = text;
+ if (text != 0) {
+ mEmptyFooterText.setText(mFooterText);
+ } else {
+ mEmptyFooterText.setText(null);
+ }
+ }
+
+ public void setFooterIcon(@DrawableRes int icon) {
+ mFooterIcon = icon;
+ Drawable drawable;
+ if (icon == 0) {
+ drawable = null;
+ } else {
+ drawable = getResources().getDrawable(icon);
+ drawable.setBounds(0, 0, mSize, mSize);
+ }
+ mEmptyFooterText.setCompoundDrawablesRelative(drawable, null, null, null);
+ }
+
+ @StringRes
public int getTextResource() {
return mText;
}
+ @StringRes
+ public int getFooterTextResource() {
+ return mFooterText;
+ }
+
+ @DrawableRes
+ public int getFooterIconResource() {
+ return mFooterIcon;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mEmptyText = (TextView) findContentView();
+ mEmptyFooterText = (TextView) findSecondaryView();
+ mEmptyFooterText.setCompoundDrawableTintList(mEmptyFooterText.getTextColors());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index bc456d5d4613..2334a4c27af8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -15,6 +15,7 @@ import android.os.Trace
import android.util.AttributeSet
import android.util.MathUtils.lerp
import android.view.View
+import android.view.animation.PathInterpolator
import com.android.systemui.animation.Interpolators
import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
import com.android.systemui.util.getColorWithAlpha
@@ -88,10 +89,12 @@ object LiftReveal : LightRevealEffect {
class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect {
- private val INTERPOLATOR = Interpolators.FAST_OUT_SLOW_IN_REVERSE
+ // Interpolator that reveals >80% of the content at 0.5 progress, makes revealing faster
+ private val interpolator = PathInterpolator(/* controlX1= */ 0.4f, /* controlY1= */ 0f,
+ /* controlX2= */ 0.2f, /* controlY2= */ 1f)
override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
- val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
+ val interpolatedAmount = interpolator.getInterpolation(amount)
scrim.interpolatedRevealAmount = interpolatedAmount
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index cdefae6b87f9..f4cd985adbdb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -30,6 +30,7 @@ import android.content.IntentSender;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -37,6 +38,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.internal.statusbar.NotificationVisibility;
@@ -127,21 +129,6 @@ public class NotificationLockscreenUserManagerImpl implements
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
- case Intent.ACTION_USER_SWITCHED:
- mCurrentUserId = intent.getIntExtra(
- Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
- updateCurrentProfilesCache();
-
- Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
-
- updateLockscreenNotificationSetting();
- updatePublicMode();
- mPresenter.onUserSwitched(mCurrentUserId);
-
- for (UserChangedListener listener : mListeners) {
- listener.onUserChanged(mCurrentUserId);
- }
- break;
case Intent.ACTION_USER_REMOVED:
int removedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (removedUserId != -1) {
@@ -181,6 +168,25 @@ public class NotificationLockscreenUserManagerImpl implements
}
};
+ protected final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ mCurrentUserId = newUser;
+ updateCurrentProfilesCache();
+
+ Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+
+ updateLockscreenNotificationSetting();
+ updatePublicMode();
+ mPresenter.onUserSwitched(mCurrentUserId);
+
+ for (UserChangedListener listener : mListeners) {
+ listener.onUserChanged(mCurrentUserId);
+ }
+ }
+ };
+
protected final Context mContext;
private final Handler mMainHandler;
protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
@@ -284,7 +290,6 @@ public class NotificationLockscreenUserManagerImpl implements
null /* handler */, UserHandle.ALL);
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
@@ -298,6 +303,8 @@ public class NotificationLockscreenUserManagerImpl implements
mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null,
Context.RECEIVER_EXPORTED_UNAUDITED);
+ mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mMainHandler));
+
mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late
updateCurrentProfilesCache();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index 7eb890677206..39daa13ae168 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -44,4 +44,8 @@ class NotifPipelineFlags @Inject constructor(
val shouldFilterUnseenNotifsOnKeyguard: Boolean by lazy {
featureFlags.isEnabled(Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD)
}
+
+ val isNoHunForOldWhenEnabled: Boolean by lazy {
+ featureFlags.isEnabled(Flags.NO_HUN_FOR_OLD_WHEN)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index d97b712df030..3e2dd053d938 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -38,6 +38,7 @@ import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.min
+
@SysUISingleton
class NotificationWakeUpCoordinator @Inject constructor(
dumpManager: DumpManager,
@@ -45,7 +46,8 @@ class NotificationWakeUpCoordinator @Inject constructor(
private val statusBarStateController: StatusBarStateController,
private val bypassController: KeyguardBypassController,
private val dozeParameters: DozeParameters,
- private val screenOffAnimationController: ScreenOffAnimationController
+ private val screenOffAnimationController: ScreenOffAnimationController,
+ private val logger: NotificationWakeUpCoordinatorLogger,
) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, ShadeExpansionListener,
Dumpable {
@@ -242,6 +244,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
override fun onDozeAmountChanged(linear: Float, eased: Float) {
+ logger.logOnDozeAmountChanged(linear, eased)
if (overrideDozeAmountIfAnimatingScreenOff(linear)) {
return
}
@@ -273,6 +276,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
override fun onStateChanged(newState: Int) {
+ logger.logOnStateChanged(newState)
if (state == StatusBarState.SHADE && newState == StatusBarState.SHADE) {
// The SHADE -> SHADE transition is only possible as part of cancelling the screen-off
// animation (e.g. by fingerprint unlock). This is done because the system is in an
@@ -320,8 +324,12 @@ class NotificationWakeUpCoordinator @Inject constructor(
private fun overrideDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ logger.logSetDozeAmount("1.0", "1.0",
+ "Override: bypass (keyguard)", StatusBarState.KEYGUARD)
setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
} else {
+ logger.logSetDozeAmount("0.0", "0.0",
+ "Override: bypass (shade)", statusBarStateController.state)
setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
}
return true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
new file mode 100644
index 000000000000..b40ce25c58d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.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.statusbar.notification
+
+import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel.DEBUG
+import javax.inject.Inject
+
+class NotificationWakeUpCoordinatorLogger
+@Inject
+constructor(@NotificationLog private val buffer: LogBuffer) {
+ fun logSetDozeAmount(linear: String, eased: String, source: String, state: Int) {
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = linear
+ str2 = eased
+ str3 = source
+ int1 = state
+ },
+ { "setDozeAmount: linear: $str1, eased: $str2, source: $str3, state: $int1" }
+ )
+ }
+
+ fun logOnDozeAmountChanged(linear: Float, eased: Float) {
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ double1 = linear.toDouble()
+ str2 = eased.toString()
+ },
+ { "onDozeAmountChanged($double1, $str2)" }
+ )
+ }
+
+ fun logOnStateChanged(newState: Int) {
+ buffer.log(TAG, DEBUG, { int1 = newState }, { "onStateChanged($int1)" })
+ }
+}
+
+private const val TAG = "NotificationWakeUpCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 6e5fcebf780f..9da94ce968c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -28,6 +28,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
+import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
@@ -49,6 +50,7 @@ constructor(
private val notifPipelineFlags: NotifPipelineFlags,
@Application private val scope: CoroutineScope,
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
+ private val seenNotifsProvider: SeenNotificationsProviderImpl,
private val statusBarStateController: StatusBarStateController,
) : Coordinator {
@@ -105,6 +107,9 @@ constructor(
@VisibleForTesting
internal val unseenNotifFilter =
object : NotifFilter("$TAG-unseen") {
+
+ var hasFilteredAnyNotifs = false
+
override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean =
when {
// Don't apply filter if the keyguard isn't currently showing
@@ -115,7 +120,12 @@ constructor(
// - summary will be pruned if necessary, depending on if children are filtered
entry.parent?.summary == entry -> false
else -> true
- }
+ }.also { hasFiltered -> hasFilteredAnyNotifs = hasFilteredAnyNotifs || hasFiltered }
+
+ override fun onCleanup() {
+ seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs
+ hasFilteredAnyNotifs = false
+ }
}
private val notifFilter: NotifFilter =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
index 966ab4c61b50..afdadeb74e23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
@@ -51,7 +51,9 @@ public abstract class Pluggable<This> {
*/
public final void invalidateList(@Nullable String reason) {
if (mListener != null) {
- Trace.beginSection("Pluggable<" + mName + ">.invalidateList");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, "Pluggable<" + mName + ">.invalidateList");
+ }
mListener.onPluggableInvalidated((This) this, reason);
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt
new file mode 100644
index 000000000000..cff47e220299
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.provider
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+
+/** Keeps track of whether "seen" notification content has been filtered out of the shade. */
+interface SeenNotificationsProvider {
+ /** Are any already-seen notifications currently filtered out of the shade? */
+ val hasFilteredOutSeenNotifications: Boolean
+}
+
+@Module
+interface SeenNotificationsProviderModule {
+ @Binds
+ fun bindSeenNotificationsProvider(
+ impl: SeenNotificationsProviderImpl
+ ): SeenNotificationsProvider
+}
+
+@SysUISingleton
+class SeenNotificationsProviderImpl @Inject constructor() : SeenNotificationsProvider {
+ override var hasFilteredOutSeenNotifications: Boolean = false
+}
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 a7b7a239f4c2..808638a99dfa 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
@@ -52,6 +52,7 @@ import com.android.systemui.statusbar.notification.collection.inflation.OnUserIn
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
+import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderModule;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
@@ -96,6 +97,7 @@ import dagger.Provides;
@Module(includes = {
CoordinatorsModule.class,
KeyguardNotificationVisibilityProviderModule.class,
+ SeenNotificationsProviderModule.class,
ShadeEventsModule.class,
NotifPipelineChoreographerModule.class,
NotificationSectionHeadersModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index e6dbcee10f60..7513aa7fa2a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -2,22 +2,20 @@ package com.android.systemui.statusbar.notification.interruption
import android.app.Notification
import android.app.Notification.VISIBILITY_SECRET
-import android.content.BroadcastReceiver
import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
import android.database.ContentObserver
import android.net.Uri
import android.os.Handler
+import android.os.HandlerExecutor
import android.os.UserHandle
import android.provider.Settings
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.CoreStartable
-import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -78,7 +76,7 @@ private class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val highPriorityProvider: HighPriorityProvider,
private val statusBarStateController: SysuiStatusBarStateController,
- private val broadcastDispatcher: BroadcastDispatcher,
+ private val userTracker: UserTracker,
private val secureSettings: SecureSettings,
private val globalSettings: GlobalSettings
) : CoreStartable, KeyguardNotificationVisibilityProvider {
@@ -87,6 +85,15 @@ private class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
private val onStateChangedListeners = ListenerSet<Consumer<String>>()
private var hideSilentNotificationsOnLockscreen: Boolean = false
+ private val userTrackerCallback = object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ if (isLockedOrLocking) {
+ // maybe public mode changed
+ notifyStateChanged("onUserSwitched")
+ }
+ }
+ }
+
override fun start() {
readShowSilentNotificationSetting()
keyguardStateController.addCallback(object : KeyguardStateController.Callback {
@@ -143,14 +150,7 @@ private class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
notifyStateChanged("onStatusBarUpcomingStateChanged")
}
})
- broadcastDispatcher.registerReceiver(object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (isLockedOrLocking) {
- // maybe public mode changed
- notifyStateChanged(intent.action!!)
- }
- }
- }, IntentFilter(Intent.ACTION_USER_SWITCHED))
+ userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
}
override fun addOnStateChangedListener(listener: Consumer<String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 073b6b041b81..13b3aca2a88f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -106,6 +106,36 @@ class NotificationInterruptLogger @Inject constructor(
})
}
+ fun logNoHeadsUpOldWhen(
+ entry: NotificationEntry,
+ notifWhen: Long,
+ notifAge: Long
+ ) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
+ long1 = notifWhen
+ long2 = notifAge
+ }, {
+ "No heads up: old when $long1 (age=$long2 ms): $str1"
+ })
+ }
+
+ fun logMaybeHeadsUpDespiteOldWhen(
+ entry: NotificationEntry,
+ notifWhen: Long,
+ notifAge: Long,
+ reason: String
+ ) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
+ str2 = reason
+ long1 = notifWhen
+ long2 = notifAge
+ }, {
+ "Maybe heads up: old when $long1 (age=$long2 ms) but $str2: $str1"
+ })
+ }
+
fun logNoHeadsUpSuppressedBy(
entry: NotificationEntry,
suppressor: NotificationInterruptSuppressor
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index c4f5a3a30608..ec5bd6894283 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -20,6 +20,7 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD;
import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR;
+import android.app.Notification;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.database.ContentObserver;
@@ -82,7 +83,10 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(1235),
@UiEvent(doc = "FSI suppressed for requiring neither HUN nor keyguard")
- FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236);
+ FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236),
+
+ @UiEvent(doc = "HUN suppressed for old when")
+ HUN_SUPPRESSED_OLD_WHEN(1237);
private final int mId;
@@ -346,6 +350,10 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
return false;
}
+ if (shouldSuppressHeadsUpWhenAwakeForOldWhen(entry, log)) {
+ return false;
+ }
+
for (int i = 0; i < mSuppressors.size(); i++) {
if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) {
if (log) mLogger.logNoHeadsUpSuppressedBy(entry, mSuppressors.get(i));
@@ -470,4 +478,51 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
private boolean isSnoozedPackage(StatusBarNotification sbn) {
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
+
+ private boolean shouldSuppressHeadsUpWhenAwakeForOldWhen(NotificationEntry entry, boolean log) {
+ if (!mFlags.isNoHunForOldWhenEnabled()) {
+ return false;
+ }
+
+ final Notification notification = entry.getSbn().getNotification();
+ if (notification == null) {
+ return false;
+ }
+
+ final long when = notification.when;
+ final long now = System.currentTimeMillis();
+ final long age = now - when;
+
+ if (age < MAX_HUN_WHEN_AGE_MS) {
+ return false;
+ }
+
+ if (when <= 0) {
+ // Some notifications (including many system notifications) are posted with the "when"
+ // field set to 0. Nothing in the Javadocs for Notification mentions a special meaning
+ // for a "when" of 0, but Android didn't even exist at the dawn of the Unix epoch.
+ // Therefore, assume that these notifications effectively don't have a "when" value,
+ // and don't suppress HUNs.
+ if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "when <= 0");
+ return false;
+ }
+
+ if (notification.fullScreenIntent != null) {
+ if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "full-screen intent");
+ return false;
+ }
+
+ if (notification.isForegroundService()) {
+ if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "foreground service");
+ return false;
+ }
+
+ if (log) mLogger.logNoHeadsUpOldWhen(entry, when, age);
+ final int uid = entry.getSbn().getUid();
+ final String packageName = entry.getSbn().getPackageName();
+ mUiEventLogger.log(NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN, uid, packageName);
+ return true;
+ }
+
+ public static final long MAX_HUN_WHEN_AGE_MS = 24 * 60 * 60 * 1000;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 1eccc9879a87..d7d5ac961249 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
import android.animation.Animator;
@@ -1404,6 +1405,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mKeepInParentForDismissAnimation = keepInParent;
}
+ /** @return true if the User has dismissed this notif's parent */
+ public boolean isParentDismissed() {
+ return getEntry().getDismissState() == PARENT_DISMISSED;
+ }
+
@Override
public boolean isRemoved() {
return mRemoved;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index f9e9a2d38138..8a400d5fef99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -359,10 +359,15 @@ public class ExpandableNotificationRowController implements NotifViewController
@Override
public boolean offerToKeepInParentForAnimation() {
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)) {
+ //If the User dismissed the notification's parent, we want to keep it attached until the
+ //dismiss animation is ongoing. Therefore we don't want to remove it in the ShadeViewDiffer.
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)
+ && mView.isParentDismissed()) {
mView.setKeepInParentForDismissAnimation(true);
return true;
}
+
+ //Otherwise the view system doesn't do the removal, so we rely on the ShadeViewDiffer
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index 64f87cabaf74..b56bae12be6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -54,8 +54,6 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import java.util.Collections;
-
import javax.inject.Inject;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 0ce9656a21b5..f21db0bde59a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -154,7 +154,7 @@ public class NotificationConversationInfo extends LinearLayout implements
// If the user selected Priority and the previous selection was not priority, show a
// People Tile add request.
if (mSelectedAction == ACTION_FAVORITE && getPriority() != mSelectedAction) {
- mShadeController.animateCollapsePanels();
+ mShadeController.animateCollapseShade();
mPeopleSpaceWidgetManager.requestPinAppWidget(mShortcutInfo, new Bundle());
}
mGutsContainer.closeControls(v, /* save= */ true);
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 073bd4bf302b..7c3e52cf9630 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
@@ -32,10 +32,12 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.TimeAnimator;
import android.animation.ValueAnimator;
import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -1401,10 +1403,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mExpandedHeight = height;
setIsExpanded(height > 0);
int minExpansionHeight = getMinExpansionHeight();
- if (height < minExpansionHeight) {
+ if (height < minExpansionHeight && !mShouldUseSplitNotificationShade) {
mClipRect.left = 0;
mClipRect.right = getWidth();
- mClipRect.top = getNotificationsClippingTopBound();
+ mClipRect.top = 0;
mClipRect.bottom = (int) height;
height = minExpansionHeight;
setRequestedClipBounds(mClipRect);
@@ -1466,17 +1468,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
notifyAppearChangedListeners();
}
- private int getNotificationsClippingTopBound() {
- if (isHeadsUpTransition()) {
- // HUN in split shade can go higher than bottom of NSSL when swiping up so we want
- // to give it extra clipping margin. Because clipping has rounded corners, we also
- // need to account for that corner clipping.
- return -mAmbientState.getStackTopMargin() - mCornerRadius;
- } else {
- return 0;
- }
- }
-
private void notifyAppearChangedListeners() {
float appear;
float expandAmount;
@@ -4236,7 +4227,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mShadeNeedsToClose = false;
postDelayed(
() -> {
- mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+ mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE);
},
DELAY_BEFORE_SHADE_CLOSE /* delayMillis */);
}
@@ -4567,7 +4558,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- void setEmptyShadeView(EmptyShadeView emptyShadeView) {
+ public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
int index = -1;
if (mEmptyShadeView != null) {
index = indexOfChild(mEmptyShadeView);
@@ -4578,15 +4569,43 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade) {
+ void updateEmptyShadeView(
+ boolean visible, boolean areNotificationsHiddenInShade, boolean areSeenNotifsFiltered) {
mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
+ if (areNotificationsHiddenInShade) {
+ updateEmptyShadeView(R.string.dnd_suppressing_shade_text, 0, 0);
+ } else if (areSeenNotifsFiltered) {
+ updateEmptyShadeView(
+ R.string.no_unseen_notif_text,
+ R.string.unlock_to_see_notif_text,
+ R.drawable.ic_friction_lock_closed);
+ } else {
+ updateEmptyShadeView(R.string.empty_shade_text, 0, 0);
+ }
+ }
+
+ private void updateEmptyShadeView(
+ @StringRes int newTextRes,
+ @StringRes int newFooterTextRes,
+ @DrawableRes int newFooterIconRes) {
int oldTextRes = mEmptyShadeView.getTextResource();
- int newTextRes = areNotificationsHiddenInShade
- ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text;
if (oldTextRes != newTextRes) {
mEmptyShadeView.setText(newTextRes);
}
+ int oldFooterTextRes = mEmptyShadeView.getFooterTextResource();
+ if (oldFooterTextRes != newFooterTextRes) {
+ mEmptyShadeView.setFooterText(newFooterTextRes);
+ }
+ int oldFooterIconRes = mEmptyShadeView.getFooterIconResource();
+ if (oldFooterIconRes != newFooterIconRes) {
+ mEmptyShadeView.setFooterIcon(newFooterIconRes);
+ }
+ if (newFooterIconRes != 0 || newFooterTextRes != 0) {
+ mEmptyShadeView.setFooterVisibility(View.VISIBLE);
+ } else {
+ mEmptyShadeView.setFooterVisibility(View.GONE);
+ }
}
public boolean isEmptyShadeViewVisible() {
@@ -5139,6 +5158,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
println(pw, "intrinsicPadding", mIntrinsicPadding);
println(pw, "topPadding", mTopPadding);
println(pw, "bottomPadding", mBottomPadding);
+ mNotificationStackSizeCalculator.dump(pw, args);
});
pw.println();
pw.println("Contents:");
@@ -5358,9 +5378,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void inflateEmptyShadeView() {
+ EmptyShadeView oldView = mEmptyShadeView;
EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_no_notifications, this, false);
- view.setText(R.string.empty_shade_text);
view.setOnClickListener(v -> {
final boolean showHistory = mController.isHistoryEnabled();
Intent intent = showHistory
@@ -5369,6 +5389,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mCentralSurfaces.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
});
setEmptyShadeView(view);
+ updateEmptyShadeView(
+ oldView == null ? R.string.empty_shade_text : oldView.getTextResource(),
+ oldView == null ? 0 : oldView.getFooterTextResource(),
+ oldView == null ? 0 : oldView.getFooterIconResource());
}
/**
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 ad4501a75ebb..e3336b269ca1 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
@@ -81,6 +81,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -89,6 +90,7 @@ import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
import com.android.systemui.statusbar.notification.collection.PipelineDumper;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
@@ -174,6 +176,8 @@ public class NotificationStackScrollLayoutController {
private final StackStateLogger mStackStateLogger;
private final NotificationStackScrollLogger mLogger;
private final GroupExpansionManager mGroupExpansionManager;
+ private final NotifPipelineFlags mNotifPipelineFlags;
+ private final SeenNotificationsProvider mSeenNotificationsProvider;
private NotificationStackScrollLayout mView;
private boolean mFadeNotificationsOnDismiss;
@@ -639,12 +643,14 @@ public class NotificationStackScrollLayoutController {
GroupExpansionManager groupManager,
@SilentHeader SectionHeaderController silentHeaderController,
NotifPipeline notifPipeline,
+ NotifPipelineFlags notifPipelineFlags,
NotifCollection notifCollection,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
ShadeTransitionController shadeTransitionController,
UiEventLogger uiEventLogger,
NotificationRemoteInputManager remoteInputManager,
VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
+ SeenNotificationsProvider seenNotificationsProvider,
ShadeController shadeController,
InteractionJankMonitor jankMonitor,
StackStateLogger stackLogger,
@@ -683,10 +689,12 @@ public class NotificationStackScrollLayoutController {
mGroupExpansionManager = groupManager;
mSilentHeaderController = silentHeaderController;
mNotifPipeline = notifPipeline;
+ mNotifPipelineFlags = notifPipelineFlags;
mNotifCollection = notifCollection;
mUiEventLogger = uiEventLogger;
mRemoteInputManager = remoteInputManager;
mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
+ mSeenNotificationsProvider = seenNotificationsProvider;
mShadeController = shadeController;
mFeatureFlags = featureFlags;
mNotificationTargetsHelper = notificationTargetsHelper;
@@ -1212,7 +1220,11 @@ public class NotificationStackScrollLayoutController {
// For more details, see: b/228790482
&& !isInTransitionToKeyguard();
- mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade());
+ mView.updateEmptyShadeView(
+ shouldShow,
+ mZenModeController.areNotificationsHiddenInShade(),
+ mNotifPipelineFlags.getShouldFilterUnseenNotifsOnKeyguard()
+ && mSeenNotificationsProvider.getHasFilteredOutSeenNotifications());
Trace.endSection();
}
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 ae854e2df91a..25f99c69d454 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
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.util.Compile
import com.android.systemui.util.children
+import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.min
@@ -53,6 +54,8 @@ constructor(
@Main private val resources: Resources
) {
+ private lateinit var lastComputeHeightLog : String
+
/**
* Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow shelf.
* If there are exactly 1 + mMaxKeyguardNotifications, and they fit in the available space
@@ -114,7 +117,9 @@ constructor(
shelfIntrinsicHeight: Float
): Int {
log { "\n" }
- val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight)
+
+ val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight,
+ /* computeHeight= */ false)
var maxNotifications =
stackHeightSequence.lastIndexWhile { heightResult ->
@@ -157,18 +162,21 @@ constructor(
shelfIntrinsicHeight: Float
): Float {
log { "\n" }
+ lastComputeHeightLog = ""
val heightPerMaxNotifications =
- computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight)
+ computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight,
+ /* computeHeight= */ true)
val (notificationsHeight, shelfHeightWithSpaceBefore) =
heightPerMaxNotifications.elementAtOrElse(maxNotifications) {
heightPerMaxNotifications.last() // Height with all notifications visible.
}
- log {
- "computeHeight(maxNotifications=$maxNotifications," +
+ lastComputeHeightLog += "\ncomputeHeight(maxNotifications=$maxNotifications," +
"shelfIntrinsicHeight=$shelfIntrinsicHeight) -> " +
"${notificationsHeight + shelfHeightWithSpaceBefore}" +
" = ($notificationsHeight + $shelfHeightWithSpaceBefore)"
+ log {
+ lastComputeHeightLog
}
return notificationsHeight + shelfHeightWithSpaceBefore
}
@@ -184,7 +192,8 @@ constructor(
private fun computeHeightPerNotificationLimit(
stack: NotificationStackScrollLayout,
- shelfHeight: Float
+ shelfHeight: Float,
+ computeHeight: Boolean
): Sequence<StackHeight> = sequence {
log { "computeHeightPerNotificationLimit" }
@@ -213,9 +222,14 @@ constructor(
currentIndex = firstViewInShelfIndex)
spaceBeforeShelf + shelfHeight
}
+
+ val currentLog = "computeHeight | i=$i notificationsHeight=$notifications " +
+ "shelfHeightWithSpaceBefore=$shelfWithSpaceBefore"
+ if (computeHeight) {
+ lastComputeHeightLog += "\n" + currentLog
+ }
log {
- "i=$i notificationsHeight=$notifications " +
- "shelfHeightWithSpaceBefore=$shelfWithSpaceBefore"
+ currentLog
}
yield(
StackHeight(
@@ -260,6 +274,10 @@ constructor(
return size
}
+ fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("NotificationStackSizeCalculator lastComputeHeightLog = $lastComputeHeightLog")
+ }
+
private fun ExpandableView.isShowable(onLockscreen: Boolean): Boolean {
if (visibility == GONE || hasNoContentHeight()) return false
if (onLockscreen) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 679bcea00f74..556431114ff1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -514,7 +514,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
&& mPendingAuthenticated.userId == KeyguardUpdateMonitor.getCurrentUser();
}
- public int getMode() {
+ public @WakeAndUnlockMode int getMode() {
return mMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 3557b4ab34f5..c6f64f3e56ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -51,7 +51,6 @@ import com.android.systemui.qs.QSPanelController;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
-import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.LightRevealScrim;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -191,8 +190,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void animateExpandSettingsPanel(@Nullable String subpanel);
- void animateCollapsePanels(int flags, boolean force);
-
void collapsePanelOnMainThread();
void togglePanel();
@@ -280,8 +277,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void postAnimateOpenPanels();
- boolean isExpandedVisible();
-
boolean isPanelExpanded();
void onInputFocusTransfer(boolean start, boolean cancel, float velocity);
@@ -290,8 +285,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void onTouchEvent(MotionEvent event);
- GestureRecorder getGestureRecorder();
-
BiometricUnlockController getBiometricUnlockController();
void showWirelessChargingAnimation(int batteryLevel);
@@ -406,10 +399,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
LightRevealScrim getLightRevealScrim();
- void onTrackingStarted();
-
- void onClosingFinished();
-
// TODO: Figure out way to remove these.
NavigationBarView getNavigationBarView();
@@ -493,12 +482,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void updateNotificationPanelTouchState();
- void makeExpandedVisible(boolean force);
-
- void instantCollapseNotificationPanel();
-
- void visibilityChanged(boolean visible);
-
int getDisplayId();
int getRotation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 9e5a66f1e306..72ada0e17a01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -209,7 +209,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
public void animateExpandNotificationsPanel() {
if (CentralSurfaces.SPEW) {
Log.d(CentralSurfaces.TAG,
- "animateExpand: mExpandedVisible=" + mCentralSurfaces.isExpandedVisible());
+ "animateExpand: mExpandedVisible=" + mShadeController.isExpandedVisible());
}
if (!mCommandQueue.panelsEnabled()) {
return;
@@ -222,7 +222,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
public void animateExpandSettingsPanel(@Nullable String subPanel) {
if (CentralSurfaces.SPEW) {
Log.d(CentralSurfaces.TAG,
- "animateExpand: mExpandedVisible=" + mCentralSurfaces.isExpandedVisible());
+ "animateExpand: mExpandedVisible=" + mShadeController.isExpandedVisible());
}
if (!mCommandQueue.panelsEnabled()) {
return;
@@ -276,7 +276,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
- mShadeController.animateCollapsePanels();
+ mShadeController.animateCollapseShade();
}
}
@@ -293,7 +293,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
mCentralSurfaces.updateQsExpansionEnabled();
if ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
- mShadeController.animateCollapsePanels();
+ mShadeController.animateCollapseShade();
}
}
@@ -550,7 +550,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
@Override
public void togglePanel() {
if (mCentralSurfaces.isPanelExpanded()) {
- mShadeController.animateCollapsePanels();
+ mShadeController.animateCollapseShade();
} else {
animateExpandNotificationsPanel();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index bc2ee1fd4f7f..2d48ffcf9810 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -58,7 +58,6 @@ import android.app.WallpaperInfo;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -161,6 +160,8 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder;
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -423,12 +424,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
/** */
@Override
- public void animateCollapsePanels(int flags, boolean force) {
- mCommandQueueCallbacks.animateCollapsePanels(flags, force);
- }
-
- /** */
- @Override
public void togglePanel() {
mCommandQueueCallbacks.togglePanel();
}
@@ -498,6 +493,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final OngoingCallController mOngoingCallController;
private final StatusBarSignalPolicy mStatusBarSignalPolicy;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
+ private final Lazy<LightRevealScrimViewModel> mLightRevealScrimViewModelLazy;
/** Controller for the Shade. */
@VisibleForTesting
@@ -510,8 +506,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private View mReportRejectedTouch;
- private boolean mExpandedVisible;
-
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
@@ -766,7 +760,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
DeviceStateManager deviceStateManager,
WiredChargingRippleController wiredChargingRippleController,
IDreamManager dreamManager,
- Lazy<CameraLauncher> cameraLauncherLazy) {
+ Lazy<CameraLauncher> cameraLauncherLazy,
+ Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy) {
mContext = context;
mNotificationsController = notificationsController;
mFragmentService = fragmentService;
@@ -880,6 +875,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
deviceStateManager.registerCallback(mMainExecutor,
new FoldStateListener(mContext, this::onFoldedStateChanged));
wiredChargingRippleController.registerCallbacks();
+
+ mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
}
@Override
@@ -910,6 +907,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
updateDisplaySize();
mStatusBarHideIconsForBouncerManager.setDisplayId(mDisplayId);
+ initShadeVisibilityListener();
+
// start old BaseStatusBar.start().
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
@@ -994,6 +993,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy.init();
+ // Based on teamfood flag, turn predictive back dispatch on at runtime.
+ if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
+ mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
+ }
+
mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onUnlockedChanged() {
@@ -1002,6 +1006,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onKeyguardGoingAwayChanged() {
+ if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ // This code path is not used if the KeyguardTransitionRepository is managing
+ // the lightreveal scrim.
+ return;
+ }
+
// The light reveal scrim should always be fully revealed by the time the keyguard
// is done going away. Double check that this is true.
if (!mKeyguardStateController.isKeyguardGoingAway()) {
@@ -1095,6 +1105,25 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
requestTopUi, componentTag))));
}
+ @VisibleForTesting
+ void initShadeVisibilityListener() {
+ mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() {
+ @Override
+ public void visibilityChanged(boolean visible) {
+ onShadeVisibilityChanged(visible);
+ }
+
+ @Override
+ public void expandedVisibleChanged(boolean expandedVisible) {
+ if (expandedVisible) {
+ setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
+ } else {
+ onExpandedInvisible();
+ }
+ }
+ });
+ }
+
private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) {
Trace.beginSection("CentralSurfaces#onFoldedStateChanged");
onFoldedStateChangedInternal(isFolded, willGoToSleep);
@@ -1219,6 +1248,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
+
+ if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ LightRevealScrimViewBinder.bind(
+ mLightRevealScrim, mLightRevealScrimViewModelLazy.get());
+ }
+
mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> {
Runnable updateOpaqueness = () -> {
mNotificationShadeWindowController.setLightRevealScrimOpaque(
@@ -1240,7 +1275,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNotificationPanelViewController.initDependencies(
this,
- this::makeExpandedInvisible,
+ mGestureRec,
+ mShadeController::makeExpandedInvisible,
mNotificationShelfController);
BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop);
@@ -1443,6 +1479,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
+ mShadeController.setNotificationPresenter(mPresenter);
mNotificationsController.initialize(
this,
mPresenter,
@@ -1492,11 +1529,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return (v, event) -> {
mAutoHideController.checkUserAutoHide(event);
mRemoteInputManager.checkRemoteInputOutside(event);
- if (event.getAction() == MotionEvent.ACTION_UP) {
- if (mExpandedVisible) {
- mShadeController.animateCollapsePanels();
- }
- }
+ mShadeController.onStatusBarTouch(event);
return mNotificationShadeWindowView.onTouchEvent(event);
};
}
@@ -1518,6 +1551,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNotificationShadeWindowViewController.setupExpandedStatusBar();
mNotificationPanelViewController =
mCentralSurfacesComponent.getNotificationPanelViewController();
+ mShadeController.setNotificationPanelViewController(mNotificationPanelViewController);
+ mShadeController.setNotificationShadeWindowViewController(
+ mNotificationShadeWindowViewController);
mCentralSurfacesComponent.getLockIconViewController().init();
mStackScrollerController =
mCentralSurfacesComponent.getNotificationStackScrollLayoutController();
@@ -1838,9 +1874,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
public void onLaunchAnimationCancelled(boolean isLaunchForActivity) {
if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
&& isLaunchForActivity) {
- onClosingFinished();
+ mShadeController.onClosingFinished();
} else {
- mShadeController.collapsePanel(true /* animate */);
+ mShadeController.collapseShade(true /* animate */);
}
}
@@ -1848,10 +1884,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onLaunchAnimationEnd(boolean launchIsFullScreen) {
if (!mPresenter.isCollapsing()) {
- onClosingFinished();
+ mShadeController.onClosingFinished();
}
if (launchIsFullScreen) {
- instantCollapseNotificationPanel();
+ mShadeController.instantCollapseShade();
}
}
@@ -1943,33 +1979,13 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
@Override
- public void makeExpandedVisible(boolean force) {
- if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
- if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) {
- return;
- }
-
- mExpandedVisible = true;
-
- // Expand the window to encompass the full screen in anticipation of the drag.
- // This is only possible to do atomically because the status bar is at the top of the screen!
- mNotificationShadeWindowController.setPanelVisible(true);
-
- visibilityChanged(true);
- mCommandQueue.recomputeDisableFlags(mDisplayId, !force /* animate */);
- setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
- }
-
- @Override
public void postAnimateCollapsePanels() {
- mMainExecutor.execute(mShadeController::animateCollapsePanels);
+ mMainExecutor.execute(mShadeController::animateCollapseShade);
}
@Override
public void postAnimateForceCollapsePanels() {
- mMainExecutor.execute(
- () -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE,
- true /* force */));
+ mMainExecutor.execute(mShadeController::animateCollapseShadeForced);
}
@Override
@@ -1978,11 +1994,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
@Override
- public boolean isExpandedVisible() {
- return mExpandedVisible;
- }
-
- @Override
public boolean isPanelExpanded() {
return mPanelExpanded;
}
@@ -2011,46 +2022,13 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
}
- void makeExpandedInvisible() {
- if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible);
-
- if (!mExpandedVisible || mNotificationShadeWindowView == null) {
- return;
- }
-
- // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
- mNotificationPanelViewController.collapsePanel(/*animate=*/ false, false /* delayed*/,
- 1.0f /* speedUpFactor */);
-
- mNotificationPanelViewController.closeQs();
-
- mExpandedVisible = false;
- visibilityChanged(false);
-
- // Update the visibility of notification shade and status bar window.
- mNotificationShadeWindowController.setPanelVisible(false);
- mStatusBarWindowController.setForceStatusBarVisible(false);
-
- // Close any guts that might be visible
- mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
-
- mShadeController.runPostCollapseRunnables();
+ private void onExpandedInvisible() {
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
if (!mNotificationActivityStarter.isCollapsingToShowActivityOverLockscreen()) {
showBouncerOrLockScreenIfKeyguard();
} else if (DEBUG) {
Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
}
- mCommandQueue.recomputeDisableFlags(
- mDisplayId,
- mNotificationPanelViewController.hideStatusBarIconsWhenExpanded() /* animate */);
-
- // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
- // the bouncer appear animation.
- if (!mKeyguardStateController.isShowing()) {
- WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
- }
}
/** Called when a touch event occurred on {@link PhoneStatusBarView}. */
@@ -2087,16 +2065,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
final boolean upOrCancel =
event.getAction() == MotionEvent.ACTION_UP ||
event.getAction() == MotionEvent.ACTION_CANCEL;
- setInteracting(StatusBarManager.WINDOW_STATUS_BAR, !upOrCancel || mExpandedVisible);
+ setInteracting(StatusBarManager.WINDOW_STATUS_BAR,
+ !upOrCancel || mShadeController.isExpandedVisible());
}
}
@Override
- public GestureRecorder getGestureRecorder() {
- return mGestureRec;
- }
-
- @Override
public BiometricUnlockController getBiometricUnlockController() {
return mBiometricUnlockController;
}
@@ -2236,7 +2210,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
synchronized (mQueueLock) {
pw.println("Current Status Bar state:");
- pw.println(" mExpandedVisible=" + mExpandedVisible);
+ pw.println(" mExpandedVisible=" + mShadeController.isExpandedVisible());
pw.println(" mDisplayMetrics=" + mDisplayMetrics);
pw.println(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller));
pw.println(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller)
@@ -2551,10 +2525,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
}
if (dismissShade) {
- if (mExpandedVisible && !mBouncerShowing) {
- mShadeController.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed*/);
+ if (mShadeController.isExpandedVisible() && !mBouncerShowing) {
+ mShadeController.animateCollapseShadeDelayed();
} else {
// Do it after DismissAction has been processed to conserve the needed
// ordering.
@@ -2596,7 +2568,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
flags |= CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL;
}
}
- mShadeController.animateCollapsePanels(flags);
+ mShadeController.animateCollapseShade(flags);
}
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
if (mNotificationShadeWindowController != null) {
@@ -2711,10 +2683,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
com.android.systemui.R.dimen.physical_power_button_center_screen_location_y));
}
- // Visibility reporting
protected void handleVisibleToUserChanged(boolean visibleToUser) {
if (visibleToUser) {
- handleVisibleToUserChangedImpl(visibleToUser);
+ onVisibleToUser();
mNotificationLogger.startNotificationLogging();
if (!mIsBackCallbackRegistered) {
@@ -2731,7 +2702,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
} else {
mNotificationLogger.stopNotificationLogging();
- handleVisibleToUserChangedImpl(visibleToUser);
+ onInvisibleToUser();
if (mIsBackCallbackRegistered) {
ViewRootImpl viewRootImpl = getViewRootImpl();
@@ -2751,41 +2722,38 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
}
- // Visibility reporting
- void handleVisibleToUserChangedImpl(boolean visibleToUser) {
- if (visibleToUser) {
- /* The LEDs are turned off when the notification panel is shown, even just a little bit.
- * See also CentralSurfaces.setPanelExpanded for another place where we attempt to do
- * this.
- */
- boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
- boolean clearNotificationEffects =
- !mPresenter.isPresenterFullyCollapsed() &&
- (mState == StatusBarState.SHADE
- || mState == StatusBarState.SHADE_LOCKED);
- int notificationLoad = mNotificationsController.getActiveNotificationsCount();
- if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
- notificationLoad = 1;
- }
- final int finalNotificationLoad = notificationLoad;
- mUiBgExecutor.execute(() -> {
- try {
- mBarService.onPanelRevealed(clearNotificationEffects,
- finalNotificationLoad);
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- });
- } else {
- mUiBgExecutor.execute(() -> {
- try {
- mBarService.onPanelHidden();
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- });
+ void onVisibleToUser() {
+ /* The LEDs are turned off when the notification panel is shown, even just a little bit.
+ * See also CentralSurfaces.setPanelExpanded for another place where we attempt to do
+ * this.
+ */
+ boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
+ boolean clearNotificationEffects =
+ !mPresenter.isPresenterFullyCollapsed() && (mState == StatusBarState.SHADE
+ || mState == StatusBarState.SHADE_LOCKED);
+ int notificationLoad = mNotificationsController.getActiveNotificationsCount();
+ if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
+ notificationLoad = 1;
}
+ final int finalNotificationLoad = notificationLoad;
+ mUiBgExecutor.execute(() -> {
+ try {
+ mBarService.onPanelRevealed(clearNotificationEffects,
+ finalNotificationLoad);
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ }
+ });
+ }
+ void onInvisibleToUser() {
+ mUiBgExecutor.execute(() -> {
+ try {
+ mBarService.onPanelHidden();
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ }
+ });
}
private void logStateToEventlog() {
@@ -2963,7 +2931,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private void updatePanelExpansionForKeyguard() {
if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode()
!= BiometricUnlockController.MODE_WAKE_AND_UNLOCK && !mBouncerShowing) {
- mShadeController.instantExpandNotificationsPanel();
+ mShadeController.instantExpandShade();
}
}
@@ -3082,7 +3050,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// too heavy for the CPU and GPU on any device.
mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay);
} else if (!mNotificationPanelViewController.isCollapsing()) {
- instantCollapseNotificationPanel();
+ mShadeController.instantCollapseShade();
}
// Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
@@ -3240,8 +3208,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public boolean onMenuPressed() {
if (shouldUnlockOnMenuPressed()) {
- mShadeController.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
+ mShadeController.animateCollapseShadeForced();
return true;
}
return false;
@@ -3286,7 +3253,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED
&& !isBouncerShowingOverDream()) {
if (mNotificationPanelViewController.canPanelBeCollapsed()) {
- mShadeController.animateCollapsePanels();
+ mShadeController.animateCollapseShade();
}
return true;
}
@@ -3296,8 +3263,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public boolean onSpacePressed() {
if (mDeviceInteractive && mState != StatusBarState.SHADE) {
- mShadeController.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
+ mShadeController.animateCollapseShadeForced();
return true;
}
return false;
@@ -3336,12 +3302,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
}
- @Override
- public void instantCollapseNotificationPanel() {
- mNotificationPanelViewController.instantCollapse();
- mShadeController.runPostCollapseRunnables();
- }
-
/**
* Collapse the panel directly if we are on the main thread, post the collapsing on the main
* thread if we are not.
@@ -3349,9 +3309,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void collapsePanelOnMainThread() {
if (Looper.getMainLooper().isCurrentThread()) {
- mShadeController.collapsePanel();
+ mShadeController.collapseShade();
} else {
- mContext.getMainExecutor().execute(mShadeController::collapsePanel);
+ mContext.getMainExecutor().execute(mShadeController::collapseShade);
}
}
@@ -3366,6 +3326,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return;
}
+ if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ return;
+ }
+
final boolean wakingUpFromPowerButton = wakingUp
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
&& mWakefulnessLifecycle.getLastWakeReason()
@@ -3392,21 +3356,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return mLightRevealScrim;
}
- @Override
- public void onTrackingStarted() {
- mShadeController.runPostCollapseRunnables();
- }
-
- @Override
- public void onClosingFinished() {
- mShadeController.runPostCollapseRunnables();
- if (!mPresenter.isPresenterFullyCollapsed()) {
- // if we set it not to be focusable when collapsing, we have to undo it when we aborted
- // the closing
- mNotificationShadeWindowController.setNotificationShadeFocusable(true);
- }
- }
-
// TODO: Figure out way to remove these.
@Override
public NavigationBarView getNavigationBarView() {
@@ -3491,7 +3440,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNotificationShadeWindowViewController.cancelCurrentTouch();
}
if (mPanelExpanded && mState == StatusBarState.SHADE) {
- mShadeController.animateCollapsePanels();
+ mShadeController.animateCollapseShade();
}
}
@@ -3554,7 +3503,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// The unlocked screen off and fold to aod animations might use our LightRevealScrim -
// we need to be expanded for it to be visible.
if (mDozeParameters.shouldShowLightRevealScrim()) {
- makeExpandedVisible(true);
+ mShadeController.makeExpandedVisible(true);
}
DejankUtils.stopDetectingBlockingIpcs(tag);
@@ -3583,7 +3532,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// If we are waking up during the screen off animation, we should undo making the
// expanded visible (we did that so the LightRevealScrim would be visible).
if (mScreenOffAnimationController.shouldHideLightRevealScrimOnWakeUp()) {
- makeExpandedInvisible();
+ mShadeController.makeExpandedInvisible();
}
});
@@ -3918,8 +3867,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
if (BANNER_ACTION_SETUP.equals(action)) {
- mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */);
+ mShadeController.animateCollapseShadeForced();
mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -3981,7 +3929,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
action.run();
}).start();
- return collapsePanel ? mShadeController.collapsePanel() : willAnimateOnKeyguard;
+ return collapsePanel ? mShadeController.collapseShade() : willAnimateOnKeyguard;
}
@Override
@@ -4076,8 +4024,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mMainExecutor.execute(runnable);
}
- @Override
- public void visibilityChanged(boolean visible) {
+ private void onShadeVisibilityChanged(boolean visible) {
if (mVisible != visible) {
mVisible = visible;
if (!visible) {
@@ -4147,7 +4094,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return;
}
- mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
+ if (!mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
+ }
}
@Override
@@ -4328,6 +4277,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onDozeAmountChanged(float linear, float eased) {
if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
+ && !mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index aa0757e1d572..000fe140882c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -240,8 +240,8 @@ public class KeyguardBouncer {
&& !mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
KeyguardUpdateMonitor.getCurrentUser())
&& !needsFullscreenBouncer()
- && !mKeyguardUpdateMonitor.isFaceLockedOut()
- && !mKeyguardUpdateMonitor.userNeedsStrongAuth()
+ && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FACE)
&& !mKeyguardBypassController.getBypassEnabled()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index 26e6db664e07..4beb87ddae2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -15,23 +15,21 @@
package com.android.systemui.statusbar.phone;
import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.annotation.NonNull;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -43,9 +41,9 @@ public class ManagedProfileControllerImpl implements ManagedProfileController {
private final List<Callback> mCallbacks = new ArrayList<>();
private final Context mContext;
+ private final Executor mMainExecutor;
private final UserManager mUserManager;
private final UserTracker mUserTracker;
- private final BroadcastDispatcher mBroadcastDispatcher;
private final LinkedList<UserInfo> mProfiles;
private boolean mListening;
private int mCurrentUser;
@@ -53,12 +51,12 @@ public class ManagedProfileControllerImpl implements ManagedProfileController {
/**
*/
@Inject
- public ManagedProfileControllerImpl(Context context, UserTracker userTracker,
- BroadcastDispatcher broadcastDispatcher) {
+ public ManagedProfileControllerImpl(Context context, @Main Executor mainExecutor,
+ UserTracker userTracker) {
mContext = context;
+ mMainExecutor = mainExecutor;
mUserManager = UserManager.get(mContext);
mUserTracker = userTracker;
- mBroadcastDispatcher = broadcastDispatcher;
mProfiles = new LinkedList<UserInfo>();
}
@@ -130,30 +128,34 @@ public class ManagedProfileControllerImpl implements ManagedProfileController {
}
private void setListening(boolean listening) {
+ if (mListening == listening) {
+ return;
+ }
mListening = listening;
if (listening) {
reloadManagedProfiles();
-
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- mBroadcastDispatcher.registerReceiver(
- mReceiver, filter, null /* handler */, UserHandle.ALL);
+ mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
} else {
- mBroadcastDispatcher.unregisterReceiver(mReceiver);
+ mUserTracker.removeCallback(mUserChangedCallback);
}
}
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- reloadManagedProfiles();
- for (Callback callback : mCallbacks) {
- callback.onManagedProfileChanged();
- }
- }
- };
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ reloadManagedProfiles();
+ for (Callback callback : mCallbacks) {
+ callback.onManagedProfileChanged();
+ }
+ }
+
+ @Override
+ public void onProfilesChanged(@NonNull List<UserInfo> profiles) {
+ reloadManagedProfiles();
+ for (Callback callback : mCallbacks) {
+ callback.onManagedProfileChanged();
+ }
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fb0d3e4406bf..d500f999b5e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -352,6 +352,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
.getBoolean(R.bool.notification_scrim_transparent);
updateScrims();
mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
+
+ // prepare() sets proper initial values for most states
+ for (ScrimState state : ScrimState.values()) {
+ state.prepare(state);
+ }
}
/**
@@ -641,10 +646,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private void setTransitionToFullShade(boolean transitioning) {
if (transitioning != mTransitioningToFullShade) {
mTransitioningToFullShade = transitioning;
- if (transitioning) {
- // Let's make sure the shade locked is ready
- ScrimState.SHADE_LOCKED.prepare(mState);
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 52430d33cbf0..0e9d3ce33d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -146,18 +146,12 @@ public enum ScrimState {
mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
mNotifAlpha = 1f;
mFrontAlpha = 0f;
- mBehindTint = Color.BLACK;
+ mBehindTint = mClipQsScrim ? Color.TRANSPARENT : Color.BLACK;
if (mClipQsScrim) {
updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
}
}
-
- // to make sure correct color is returned before "prepare" is called
- @Override
- public int getBehindTint() {
- return Color.BLACK;
- }
},
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index dcbabaac8a5b..3b160c85ca15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -469,6 +469,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// Don't expand to the bouncer. Instead transition back to the lock screen (see
// CentralSurfaces#showBouncerOrLockScreenIfKeyguard)
return;
+ } else if (mKeyguardStateController.isOccluded()
+ && !mDreamOverlayStateController.isOverlayActive()) {
+ return;
} else if (needsFullscreenBouncer()) {
if (mPrimaryBouncer != null) {
mPrimaryBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index b6ae4a088880..05bf8604c2c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -260,11 +260,11 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
if (showOverLockscreen) {
mShadeController.addPostCollapseAction(runnable);
- mShadeController.collapsePanel(true /* animate */);
+ mShadeController.collapseShade(true /* animate */);
} else if (mKeyguardStateController.isShowing()
&& mCentralSurfaces.isOccluded()) {
mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
- mShadeController.collapsePanel();
+ mShadeController.collapseShade();
} else {
runnable.run();
}
@@ -406,7 +406,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
private void expandBubbleStack(NotificationEntry entry) {
mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
- mShadeController.collapsePanel();
+ mShadeController.collapseShade();
}
private void startNotificationIntent(
@@ -593,9 +593,9 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
private void collapseOnMainThread() {
if (Looper.getMainLooper().isCurrentThread()) {
- mShadeController.collapsePanel();
+ mShadeController.collapseShade();
} else {
- mMainThreadHandler.post(mShadeController::collapsePanel);
+ mMainThreadHandler.post(mShadeController::collapseShade);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 8a49850b1822..7fe01825890f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -180,7 +180,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
}
};
mShadeController.postOnShadeExpanded(clickPendingViewRunnable);
- mShadeController.instantExpandNotificationsPanel();
+ mShadeController.instantExpandShade();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
index 946d7e4a3e75..4d914fe0adef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
@@ -52,6 +52,6 @@ class StatusBarPipelineFlags @Inject constructor(private val featureFlags: Featu
* Returns true if we should apply some coloring to the wifi icon that was rendered with the new
* pipeline to help with debugging.
*/
- // For now, just always apply the debug coloring if we've enabled the new icon.
- fun useWifiDebugColoring(): Boolean = useNewWifiIcon()
+ fun useWifiDebugColoring(): Boolean =
+ featureFlags.isEnabled(Flags.NEW_STATUS_BAR_ICONS_DEBUG_COLORING)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
index 7aa5ee1389f3..8ff9198da119 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
@@ -23,9 +23,10 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.qs.SettingObserver
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
+import com.android.systemui.statusbar.pipeline.dagger.AirplaneTableLog
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -58,7 +59,7 @@ class AirplaneModeRepositoryImpl
constructor(
@Background private val bgHandler: Handler,
private val globalSettings: GlobalSettings,
- logger: ConnectivityPipelineLogger,
+ @AirplaneTableLog logger: TableLogBuffer,
@Application scope: CoroutineScope,
) : AirplaneModeRepository {
// TODO(b/254848912): Replace this with a generic SettingObserver coroutine once we have it.
@@ -82,7 +83,12 @@ constructor(
awaitClose { observer.isListening = false }
}
.distinctUntilChanged()
- .logInputChange(logger, "isAirplaneMode")
+ .logDiffsForTable(
+ logger,
+ columnPrefix = "",
+ columnName = "isAirplaneMode",
+ initialValue = false
+ )
.stateIn(
scope,
started = SharingStarted.WhileSubscribed(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
index fe30c0169021..4a5342e0f765 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
@@ -36,16 +36,20 @@ import kotlinx.coroutines.flow.stateIn
* [com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository] for
* more details.
*/
+interface AirplaneModeViewModel {
+ /** True if the airplane mode icon is currently visible in the status bar. */
+ val isAirplaneModeIconVisible: StateFlow<Boolean>
+}
+
@SysUISingleton
-class AirplaneModeViewModel
+class AirplaneModeViewModelImpl
@Inject
constructor(
interactor: AirplaneModeInteractor,
logger: ConnectivityPipelineLogger,
@Application private val scope: CoroutineScope,
-) {
- /** True if the airplane mode icon is currently visible in the status bar. */
- val isAirplaneModeIconVisible: StateFlow<Boolean> =
+) : AirplaneModeViewModel {
+ override val isAirplaneModeIconVisible: StateFlow<Boolean> =
combine(interactor.isAirplaneMode, interactor.isForceHidden) {
isAirplaneMode,
isAirplaneIconForceHidden ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt
new file mode 100644
index 000000000000..4f70f660187f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Airplane mode logs in table format. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class AirplaneTableLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index fcd1b8abefe4..fb67f1a1bf50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -16,8 +16,13 @@
package com.android.systemui.statusbar.pipeline.dagger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryImpl
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
@@ -30,8 +35,11 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.Connectivi
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import dagger.Binds
import dagger.Module
+import dagger.Provides
@Module
abstract class StatusBarPipelineModule {
@@ -39,12 +47,18 @@ abstract class StatusBarPipelineModule {
abstract fun airplaneModeRepository(impl: AirplaneModeRepositoryImpl): AirplaneModeRepository
@Binds
+ abstract fun airplaneModeViewModel(impl: AirplaneModeViewModelImpl): AirplaneModeViewModel
+
+ @Binds
abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
@Binds
abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
@Binds
+ abstract fun wifiInteractor(impl: WifiInteractorImpl): WifiInteractor
+
+ @Binds
abstract fun mobileConnectionsRepository(
impl: MobileConnectionsRepositoryImpl
): MobileConnectionsRepository
@@ -57,4 +71,23 @@ abstract class StatusBarPipelineModule {
@Binds
abstract fun mobileIconsInteractor(impl: MobileIconsInteractorImpl): MobileIconsInteractor
+
+ @Module
+ companion object {
+ @JvmStatic
+ @Provides
+ @SysUISingleton
+ @WifiTableLog
+ fun provideWifiTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
+ return factory.create("WifiTableLog", 100)
+ }
+
+ @JvmStatic
+ @Provides
+ @SysUISingleton
+ @AirplaneTableLog
+ fun provideAirplaneTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
+ return factory.create("AirplaneTableLog", 30)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/WifiTableLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/WifiTableLog.kt
new file mode 100644
index 000000000000..ac395a9d521b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/WifiTableLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Wifi logs in table format. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class WifiTableLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
index 062c3d1a4b10..a682a5711a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
@@ -17,12 +17,34 @@
package com.android.systemui.statusbar.pipeline.wifi.data.model
import androidx.annotation.VisibleForTesting
+import com.android.systemui.log.table.TableRowLogger
+import com.android.systemui.log.table.Diffable
/** Provides information about the current wifi network. */
-sealed class WifiNetworkModel {
+sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
+
/** A model representing that we have no active wifi network. */
object Inactive : WifiNetworkModel() {
override fun toString() = "WifiNetwork.Inactive"
+
+ override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
+ if (prevVal is Inactive) {
+ return
+ }
+
+ if (prevVal is CarrierMerged) {
+ // The only difference between CarrierMerged and Inactive is the type
+ row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
+ return
+ }
+
+ // When changing from Active to Inactive, we need to log diffs to all the fields.
+ logFullNonActiveNetwork(TYPE_INACTIVE, row)
+ }
+
+ override fun logFull(row: TableRowLogger) {
+ logFullNonActiveNetwork(TYPE_INACTIVE, row)
+ }
}
/**
@@ -33,6 +55,21 @@ sealed class WifiNetworkModel {
*/
object CarrierMerged : WifiNetworkModel() {
override fun toString() = "WifiNetwork.CarrierMerged"
+
+ override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
+ if (prevVal is CarrierMerged) {
+ return
+ }
+
+ if (prevVal is Inactive) {
+ // The only difference between CarrierMerged and Inactive is the type.
+ row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
+ return
+ }
+
+ // When changing from Active to CarrierMerged, we need to log diffs to all the fields.
+ logFullNonActiveNetwork(TYPE_CARRIER_MERGED, row)
+ }
}
/** Provides information about an active wifi network. */
@@ -76,6 +113,44 @@ sealed class WifiNetworkModel {
}
}
+ override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
+ if (prevVal !is Active) {
+ row.logChange(COL_NETWORK_TYPE, TYPE_ACTIVE)
+ }
+
+ if (prevVal !is Active || prevVal.networkId != networkId) {
+ row.logChange(COL_NETWORK_ID, networkId)
+ }
+ if (prevVal !is Active || prevVal.isValidated != isValidated) {
+ row.logChange(COL_VALIDATED, isValidated)
+ }
+ if (prevVal !is Active || prevVal.level != level) {
+ if (level != null) {
+ row.logChange(COL_LEVEL, level)
+ } else {
+ row.logChange(COL_LEVEL, LEVEL_DEFAULT)
+ }
+ }
+ if (prevVal !is Active || prevVal.ssid != ssid) {
+ row.logChange(COL_SSID, ssid)
+ }
+
+ // TODO(b/238425913): The passpoint-related values are frequently never used, so it
+ // would be great to not log them when they're not used.
+ if (prevVal !is Active || prevVal.isPasspointAccessPoint != isPasspointAccessPoint) {
+ row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
+ }
+ if (prevVal !is Active ||
+ prevVal.isOnlineSignUpForPasspointAccessPoint !=
+ isOnlineSignUpForPasspointAccessPoint) {
+ row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
+ }
+ if (prevVal !is Active ||
+ prevVal.passpointProviderFriendlyName != passpointProviderFriendlyName) {
+ row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName)
+ }
+ }
+
override fun toString(): String {
// Only include the passpoint-related values in the string if we have them. (Most
// networks won't have them so they'll be mostly clutter.)
@@ -101,4 +176,31 @@ sealed class WifiNetworkModel {
internal const val MAX_VALID_LEVEL = 4
}
}
+
+ internal fun logFullNonActiveNetwork(type: String, row: TableRowLogger) {
+ row.logChange(COL_NETWORK_TYPE, type)
+ row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
+ row.logChange(COL_VALIDATED, false)
+ row.logChange(COL_LEVEL, LEVEL_DEFAULT)
+ row.logChange(COL_SSID, null)
+ row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
+ row.logChange(COL_ONLINE_SIGN_UP, false)
+ row.logChange(COL_PASSPOINT_NAME, null)
+ }
}
+
+const val TYPE_CARRIER_MERGED = "CarrierMerged"
+const val TYPE_INACTIVE = "Inactive"
+const val TYPE_ACTIVE = "Active"
+
+const val COL_NETWORK_TYPE = "type"
+const val COL_NETWORK_ID = "networkId"
+const val COL_VALIDATED = "isValidated"
+const val COL_LEVEL = "level"
+const val COL_SSID = "ssid"
+const val COL_PASSPOINT_ACCESS_POINT = "isPasspointAccessPoint"
+const val COL_ONLINE_SIGN_UP = "isOnlineSignUpForPasspointAccessPoint"
+const val COL_PASSPOINT_NAME = "passpointProviderFriendlyName"
+
+val LEVEL_DEFAULT: String? = null
+val NETWORK_ID_DEFAULT: String? = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index 93448c1dee0e..0c9c1cc51ca4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -36,10 +36,14 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.ACTIVITY_PREFIX
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -82,6 +86,7 @@ class WifiRepositoryImpl @Inject constructor(
broadcastDispatcher: BroadcastDispatcher,
connectivityManager: ConnectivityManager,
logger: ConnectivityPipelineLogger,
+ @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
@Main mainExecutor: Executor,
@Application scope: CoroutineScope,
wifiManager: WifiManager?,
@@ -105,7 +110,12 @@ class WifiRepositoryImpl @Inject constructor(
merge(wifiNetworkChangeEvents, wifiStateChangeEvents)
.mapLatest { wifiManager.isWifiEnabled }
.distinctUntilChanged()
- .logInputChange(logger, "enabled")
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = "",
+ columnName = "isWifiEnabled",
+ initialValue = wifiManager.isWifiEnabled,
+ )
.stateIn(
scope = scope,
started = SharingStarted.WhileSubscribed(),
@@ -139,7 +149,12 @@ class WifiRepositoryImpl @Inject constructor(
awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
}
.distinctUntilChanged()
- .logInputChange(logger, "isWifiDefault")
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = "",
+ columnName = "isWifiDefault",
+ initialValue = false,
+ )
.stateIn(
scope,
started = SharingStarted.WhileSubscribed(),
@@ -199,6 +214,12 @@ class WifiRepositoryImpl @Inject constructor(
awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
}
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = "wifiNetwork",
+ initialValue = WIFI_NETWORK_DEFAULT,
+ )
// There will be multiple wifi icons in different places that will frequently
// subscribe/unsubscribe to flows as the views attach/detach. Using [stateIn] ensures that
// new subscribes will get the latest value immediately upon subscription. Otherwise, the
@@ -223,6 +244,11 @@ class WifiRepositoryImpl @Inject constructor(
awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
}
}
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = ACTIVITY_PREFIX,
+ initialValue = ACTIVITY_DEFAULT,
+ )
.stateIn(
scope,
started = SharingStarted.WhileSubscribed(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 3a3e611de96a..ec935fe23d39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -34,16 +34,36 @@ import kotlinx.coroutines.flow.map
* This interactor processes information from our data layer into information that the UI layer can
* use.
*/
-@SysUISingleton
-class WifiInteractor @Inject constructor(
- connectivityRepository: ConnectivityRepository,
- wifiRepository: WifiRepository,
-) {
+interface WifiInteractor {
/**
* The SSID (service set identifier) of the wifi network. Null if we don't have a network, or
* have a network but no valid SSID.
*/
- val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
+ val ssid: Flow<String?>
+
+ /** Our current enabled status. */
+ val isEnabled: Flow<Boolean>
+
+ /** Our current default status. */
+ val isDefault: Flow<Boolean>
+
+ /** Our current wifi network. See [WifiNetworkModel]. */
+ val wifiNetwork: Flow<WifiNetworkModel>
+
+ /** Our current wifi activity. See [WifiActivityModel]. */
+ val activity: StateFlow<WifiActivityModel>
+
+ /** True if we're configured to force-hide the wifi icon and false otherwise. */
+ val isForceHidden: Flow<Boolean>
+}
+
+@SysUISingleton
+class WifiInteractorImpl @Inject constructor(
+ connectivityRepository: ConnectivityRepository,
+ wifiRepository: WifiRepository,
+) : WifiInteractor {
+
+ override val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
when (info) {
is WifiNetworkModel.Inactive -> null
is WifiNetworkModel.CarrierMerged -> null
@@ -56,20 +76,15 @@ class WifiInteractor @Inject constructor(
}
}
- /** Our current enabled status. */
- val isEnabled: Flow<Boolean> = wifiRepository.isWifiEnabled
+ override val isEnabled: Flow<Boolean> = wifiRepository.isWifiEnabled
- /** Our current default status. */
- val isDefault: Flow<Boolean> = wifiRepository.isWifiDefault
+ override val isDefault: Flow<Boolean> = wifiRepository.isWifiDefault
- /** Our current wifi network. See [WifiNetworkModel]. */
- val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
+ override val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
- /** Our current wifi activity. See [WifiActivityModel]. */
- val activity: StateFlow<WifiActivityModel> = wifiRepository.wifiActivity
+ override val activity: StateFlow<WifiActivityModel> = wifiRepository.wifiActivity
- /** True if we're configured to force-hide the wifi icon and false otherwise. */
- val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
+ override val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
it.contains(ConnectivitySlot.WIFI)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiActivityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiActivityModel.kt
index 574610605b4e..a4ca41c22ad1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiActivityModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiActivityModel.kt
@@ -16,10 +16,32 @@
package com.android.systemui.statusbar.pipeline.wifi.shared.model
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
/** Provides information on the current wifi activity. */
data class WifiActivityModel(
/** True if the wifi has activity in (download). */
val hasActivityIn: Boolean,
/** True if the wifi has activity out (upload). */
val hasActivityOut: Boolean,
-)
+) : Diffable<WifiActivityModel> {
+
+ override fun logDiffs(prevVal: WifiActivityModel, row: TableRowLogger) {
+ if (prevVal.hasActivityIn != hasActivityIn) {
+ row.logChange(COL_ACTIVITY_IN, hasActivityIn)
+ }
+ if (prevVal.hasActivityOut != hasActivityOut) {
+ row.logChange(COL_ACTIVITY_OUT, hasActivityOut)
+ }
+ }
+
+ override fun logFull(row: TableRowLogger) {
+ row.logChange(COL_ACTIVITY_IN, hasActivityIn)
+ row.logChange(COL_ACTIVITY_OUT, hasActivityOut)
+ }
+}
+
+const val ACTIVITY_PREFIX = "wifiActivity"
+private const val COL_ACTIVITY_IN = "in"
+private const val COL_ACTIVITY_OUT = "out"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index d84cbcc60853..6875b523a962 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -120,6 +120,7 @@ public class Clock extends TextView implements
@Override
public void onUserChanged(int newUser, @NonNull Context userContext) {
mCurrentUserId = newUser;
+ updateClock();
}
};
@@ -190,7 +191,6 @@ public class Clock extends TextView implements
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
// NOTE: This receiver could run before this method returns, as it's not dispatching
// on the main thread and BroadcastDispatcher may not need to register with Context.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
index b234e9c4e746..63b9ff9717d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -28,11 +28,14 @@ import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.settings.UserTracker;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -45,22 +48,34 @@ public class NextAlarmControllerImpl extends BroadcastReceiver
private final ArrayList<NextAlarmChangeCallback> mChangeCallbacks = new ArrayList<>();
+ private final UserTracker mUserTracker;
private AlarmManager mAlarmManager;
private AlarmManager.AlarmClockInfo mNextAlarm;
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ updateNextAlarm();
+ }
+ };
+
/**
*/
@Inject
public NextAlarmControllerImpl(
+ @Main Executor mainExecutor,
AlarmManager alarmManager,
BroadcastDispatcher broadcastDispatcher,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ UserTracker userTracker) {
dumpManager.registerDumpable("NextAlarmController", this);
mAlarmManager = alarmManager;
+ mUserTracker = userTracker;
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
broadcastDispatcher.registerReceiver(this, filter, null, UserHandle.ALL);
+ mUserTracker.addCallback(mUserChangedCallback, mainExecutor);
updateNextAlarm();
}
@@ -98,14 +113,13 @@ public class NextAlarmControllerImpl extends BroadcastReceiver
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (action.equals(Intent.ACTION_USER_SWITCHED)
- || action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
+ if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
updateNextAlarm();
}
}
private void updateNextAlarm() {
- mNextAlarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
+ mNextAlarm = mAlarmManager.getNextAlarmClock(mUserTracker.getUserId());
fireNextAlarmChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
index 0c72b78a3c46..2b29885db682 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserModule.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.user;
import android.app.Activity;
+import android.os.UserHandle;
import com.android.settingslib.users.EditUserInfoController;
import com.android.systemui.user.data.repository.UserRepositoryModule;
@@ -51,4 +52,22 @@ public abstract class UserModule {
@IntoMap
@ClassKey(UserSwitcherActivity.class)
public abstract Activity provideUserSwitcherActivity(UserSwitcherActivity activity);
+
+ /**
+ * Provides the {@link UserHandle} for the user associated with this System UI process.
+ *
+ * <p>Note that this is static and unchanging for the life-time of the process we are running
+ * in. It can be <i>different</i> from the user that is the currently-selected user, which may
+ * be associated with a different System UI process.
+ *
+ * <p>For example, the System UI process which creates all the windows and renders UI is always
+ * the one associated with the primary user on the device. However, if the user is switched to
+ * another, non-primary user (for example user "X"), then a secondary System UI process will be
+ * spawned. While the original primary user process continues to be the only one rendering UI,
+ * the new system UI process may be used for things like file or content access.
+ */
+ @Provides
+ public static UserHandle provideUserHandle() {
+ return new UserHandle(UserHandle.myUserId());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 4c9b8e4639ca..c0f03902202a 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -242,7 +242,15 @@ constructor(
val isUserSwitcherEnabled =
globalSettings.getIntForUser(
Settings.Global.USER_SWITCHER_ENABLED,
- 0,
+ if (
+ appContext.resources.getBoolean(
+ com.android.internal.R.bool.config_showUserSwitcherByDefault
+ )
+ ) {
+ 1
+ } else {
+ 0
+ },
UserHandle.USER_SYSTEM,
) != 0
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index c5b697c90e0c..d7b0971d9126 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -114,9 +114,9 @@ constructor(
private val callbackMutex = Mutex()
private val callbacks = mutableSetOf<UserCallback>()
- private val userInfos =
- combine(repository.userSwitcherSettings, repository.userInfos) { settings, userInfos ->
- userInfos.filter { !it.isGuest || canCreateGuestUser(settings) }.filter { it.isFull }
+ private val userInfos: Flow<List<UserInfo>> =
+ repository.userInfos.map { userInfos ->
+ userInfos.filter { it.isFull }
}
/** List of current on-device users to select from. */
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index 0910ea36b7ff..37115ad53880 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -19,6 +19,8 @@ package com.android.systemui.user.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.drawable.CircularDrawable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.user.domain.interactor.GuestUserInteractor
@@ -144,7 +146,12 @@ private constructor(
): UserViewModel {
return UserViewModel(
viewKey = model.id,
- name = model.name,
+ name =
+ if (model.isGuest && model.isSelected) {
+ Text.Resource(R.string.guest_exit_quick_settings_button)
+ } else {
+ model.name
+ },
image = CircularDrawable(model.image),
isSelectionMarkerVisible = model.isSelected,
alpha =
diff --git a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
index 5b16ae999aa3..b311318fb111 100644
--- a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
@@ -22,11 +22,22 @@ import android.os.Trace
* Run a block within a [Trace] section.
* Calls [Trace.beginSection] before and [Trace.endSection] after the passed block.
*/
-inline fun <T> traceSection(tag: String, block: () -> T): T {
- Trace.beginSection(tag)
- try {
- return block()
- } finally {
- Trace.endSection()
+inline fun <T> traceSection(tag: String, block: () -> T): T =
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, tag)
+ try {
+ block()
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_APP)
+ }
+ } else {
+ block()
+ }
+
+class TraceUtils {
+ companion object {
+ inline fun traceRunnable(tag: String, crossinline block: () -> Unit): Runnable {
+ return Runnable { traceSection(tag) { block() } }
+ }
}
-} \ No newline at end of file
+}
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 f71d596ff835..b61b2e66fb22 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -20,7 +20,6 @@ import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onStart
@@ -58,6 +57,22 @@ fun <T, R> Flow<T>.pairwiseBy(
onStart { emit(initialValue) }.pairwiseBy(transform)
/**
+ * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
+ *
+ *
+ * The output of [getInitialValue] will be used as the "old" value for the first emission. As
+ * opposed to the initial value in the above [pairwiseBy], [getInitialValue] can do some work before
+ * returning the initial value.
+ *
+ * Useful for code that needs to compare the current value to the previous value.
+ */
+fun <T, R> Flow<T>.pairwiseBy(
+ getInitialValue: suspend () -> T,
+ transform: suspend (previousValue: T, newValue: T) -> R,
+): Flow<R> =
+ onStart { emit(getInitialValue()) }.pairwiseBy(transform)
+
+/**
* Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new
* Flow will not start emitting until it has received two emissions from the upstream Flow.
*
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index a4384d5810ce..7033ccde8c7d 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -549,7 +549,7 @@ public class BubblesManager {
} catch (RemoteException e) {
Log.e(TAG, e.getMessage());
}
- mShadeController.collapsePanel(true);
+ mShadeController.collapseShade(true);
if (entry.getRow() != null) {
entry.getRow().updateBubbleButton();
}
@@ -597,7 +597,7 @@ public class BubblesManager {
}
if (shouldBubble) {
- mShadeController.collapsePanel(true);
+ mShadeController.collapseShade(true);
if (entry.getRow() != null) {
entry.getRow().updateBubbleButton();
}
diff --git a/packages/SystemUI/tests/robolectric/config/robolectric.properties b/packages/SystemUI/tests/robolectric/config/robolectric.properties
new file mode 100644
index 000000000000..2a75bd98bfe8
--- /dev/null
+++ b/packages/SystemUI/tests/robolectric/config/robolectric.properties
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+sdk=NEWEST_SDK \ No newline at end of file
diff --git a/packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiResourceLoadingTest.java b/packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiResourceLoadingTest.java
new file mode 100644
index 000000000000..188dff21efa4
--- /dev/null
+++ b/packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiResourceLoadingTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.robotests;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import static com.google.common.truth.Truth.assertThat;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SysuiResourceLoadingTest extends SysuiRoboBase {
+ @Test
+ public void testResources() {
+ assertThat(getContext().getString(com.android.systemui.R.string.app_label))
+ .isEqualTo("System UI");
+ assertThat(getContext().getString(com.android.systemui.tests.R.string.test_content))
+ .isNotEmpty();
+ }
+}
diff --git a/packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiRoboBase.java b/packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiRoboBase.java
new file mode 100644
index 000000000000..d9686bbeb0cd
--- /dev/null
+++ b/packages/SystemUI/tests/robolectric/src/com/android/systemui/robotests/SysuiRoboBase.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.robotests;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+
+public class SysuiRoboBase {
+ public Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index 88396628017b..afd582a3b822 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -63,7 +63,6 @@ private fun fingerprintModel(user: Int) = KeyguardFingerprintListenModel(
credentialAttempted = false,
deviceInteractive = false,
dreaming = false,
- encryptedOrLockdown = false,
fingerprintDisabled = false,
fingerprintLockedOut = false,
goingToSleep = false,
@@ -74,6 +73,7 @@ private fun fingerprintModel(user: Int) = KeyguardFingerprintListenModel(
primaryUser = false,
shouldListenSfpsState = false,
shouldListenForFingerprintAssistant = false,
+ strongerAuthRequired = false,
switchingUser = false,
udfps = false,
userDoesNotHaveTrust = false
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 4d58b09f1076..84f6d913b310 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -379,9 +379,9 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
}
@Test
- public void onBouncerVisibilityChanged_needsStrongAuth_sideFpsHintHidden() {
+ public void onBouncerVisibilityChanged_unlockingWithFingerprintNotAllowed_sideFpsHintHidden() {
setupConditionsToEnableSideFpsHint();
- setNeedsStrongAuth(true);
+ setUnlockingWithFingerprintAllowed(false);
reset(mSideFpsController);
mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
@@ -558,11 +558,40 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
configurationListenerArgumentCaptor.getValue().onDensityOrFontScaleChanged();
verify(mView).onDensityOrFontScaleChanged();
- verify(mKeyguardSecurityViewFlipperController).onDensityOrFontScaleChanged();
+ verify(mKeyguardSecurityViewFlipperController).clearViews();
verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
any(KeyguardSecurityCallback.class));
}
+ @Test
+ public void onThemeChanged() {
+ ArgumentCaptor<ConfigurationController.ConfigurationListener>
+ configurationListenerArgumentCaptor = ArgumentCaptor.forClass(
+ ConfigurationController.ConfigurationListener.class);
+ mKeyguardSecurityContainerController.onViewAttached();
+ verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+ configurationListenerArgumentCaptor.getValue().onThemeChanged();
+
+ verify(mView).reloadColors();
+ verify(mKeyguardSecurityViewFlipperController).clearViews();
+ verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
+ any(KeyguardSecurityCallback.class));
+ }
+
+ @Test
+ public void onUiModeChanged() {
+ ArgumentCaptor<ConfigurationController.ConfigurationListener>
+ configurationListenerArgumentCaptor = ArgumentCaptor.forClass(
+ ConfigurationController.ConfigurationListener.class);
+ mKeyguardSecurityContainerController.onViewAttached();
+ verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+ configurationListenerArgumentCaptor.getValue().onUiModeChanged();
+
+ verify(mView).reloadColors();
+ verify(mKeyguardSecurityViewFlipperController).clearViews();
+ verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
+ any(KeyguardSecurityCallback.class));
+ }
private KeyguardSecurityContainer.SwipeListener getRegisteredSwipeListener() {
mKeyguardSecurityContainerController.onViewAttached();
@@ -574,7 +603,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
attachView();
setSideFpsHintEnabledFromResources(true);
setFingerprintDetectionRunning(true);
- setNeedsStrongAuth(false);
+ setUnlockingWithFingerprintAllowed(true);
}
private void attachView() {
@@ -593,9 +622,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
enabled);
}
- private void setNeedsStrongAuth(boolean needed) {
- when(mKeyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(needed);
- mKeyguardUpdateMonitorCallback.getValue().onStrongAuthStateChanged(/* userId= */ 0);
+ private void setUnlockingWithFingerprintAllowed(boolean allowed) {
+ when(mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()).thenReturn(allowed);
}
private void setupGetSecurityView() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index fd02ac97cec2..1614b577a6cc 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -109,7 +109,7 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
@Test
public void onDensityOrFontScaleChanged() {
- mKeyguardSecurityViewFlipperController.onDensityOrFontScaleChanged();
+ mKeyguardSecurityViewFlipperController.clearViews();
verify(mView).removeAllViews();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index beb9a72de324..e9aa8ee2f4d3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -27,6 +27,7 @@ import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
+import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
@@ -281,7 +282,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
componentInfo, FaceSensorProperties.TYPE_UNKNOWN,
false /* supportsFaceDetection */, true /* supportsSelfIllumination */,
false /* resetLockoutRequiresChallenge */));
-
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(List.of(
@@ -594,30 +594,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
- public void testFingerprintDoesNotAuth_whenEncrypted() {
- testFingerprintWhenStrongAuth(
- STRONG_AUTH_REQUIRED_AFTER_BOOT);
- }
-
- @Test
- public void testFingerprintDoesNotAuth_whenDpmLocked() {
- testFingerprintWhenStrongAuth(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW);
- }
-
- @Test
- public void testFingerprintDoesNotAuth_whenUserLockdown() {
- testFingerprintWhenStrongAuth(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- }
-
- private void testFingerprintWhenStrongAuth(int strongAuth) {
+ public void testOnlyDetectFingerprint_whenFingerprintUnlockNotAllowed() {
// Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
// will trigger updateBiometricListeningState();
clearInvocations(mFingerprintManager);
mKeyguardUpdateMonitor.resetBiometricListeningState();
- when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
@@ -626,6 +609,64 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricAllowed() {
+ // GIVEN unlocking with biometric is allowed
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+
+ // THEN unlocking with face and fp is allowed
+ Assert.assertTrue(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FACE));
+ Assert.assertTrue(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FINGERPRINT));
+ }
+
+ @Test
+ public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricNotAllowed() {
+ // GIVEN unlocking with biometric is not allowed
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+
+ // THEN unlocking with face is not allowed
+ Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FACE));
+ }
+
+ @Test
+ public void testUnlockingWithFaceAllowed_fingerprintLockout() {
+ // GIVEN unlocking with biometric is allowed
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+
+ // WHEN fingerprint is locked out
+ fingerprintErrorLockedOut();
+
+ // THEN unlocking with face is not allowed
+ Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FACE));
+ }
+
+ @Test
+ public void testUnlockingWithFpAllowed_strongAuthTrackerUnlockingWithBiometricNotAllowed() {
+ // GIVEN unlocking with biometric is not allowed
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+
+ // THEN unlocking with fingerprint is not allowed
+ Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FINGERPRINT));
+ }
+
+ @Test
+ public void testUnlockingWithFpAllowed_fingerprintLockout() {
+ // GIVEN unlocking with biometric is allowed
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+
+ // WHEN fingerprint is locked out
+ fingerprintErrorLockedOut();
+
+ // THEN unlocking with fingeprint is not allowed
+ Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FINGERPRINT));
+ }
+
+ @Test
public void testTriesToAuthenticate_whenBouncer() {
setKeyguardBouncerVisibility(true);
@@ -797,6 +838,15 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void testFingerprintPowerPressed_restartsFingerprintListeningStateImmediately() {
+ mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
+ .onAuthenticationError(FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "");
+
+ verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+ anyInt());
+ }
+
+ @Test
public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
// test whether face will be skipped if authenticated, so the value of isStrongBiometric
// doesn't matter here
@@ -928,10 +978,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
faceLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
final boolean fpLocked =
fingerprintLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
- when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser)))
- .thenReturn(fingerprintLockoutMode);
- when(mFaceManager.getLockoutModeForUser(eq(FACE_SENSOR_ID), eq(newUser)))
- .thenReturn(faceLockoutMode);
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
@@ -941,6 +987,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
anyInt());
+ when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser)))
+ .thenReturn(fingerprintLockoutMode);
+ when(mFaceManager.getLockoutModeForUser(eq(FACE_SENSOR_ID), eq(newUser)))
+ .thenReturn(faceLockoutMode);
final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal);
mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
@@ -951,14 +1001,22 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.handleUserSwitchComplete(newUser);
mTestableLooper.processAllMessages();
- verify(faceCancel, faceLocked ? times(1) : never()).cancel();
- verify(fpCancel, fpLocked ? times(1) : never()).cancel();
- verify(callback, faceLocked ? times(1) : never()).onBiometricRunningStateChanged(
+ // THEN face and fingerprint listening are always cancelled immediately
+ verify(faceCancel).cancel();
+ verify(callback).onBiometricRunningStateChanged(
eq(false), eq(BiometricSourceType.FACE));
- verify(callback, fpLocked ? times(1) : never()).onBiometricRunningStateChanged(
+ verify(fpCancel).cancel();
+ verify(callback).onBiometricRunningStateChanged(
eq(false), eq(BiometricSourceType.FINGERPRINT));
+
+ // THEN locked out states are updated
assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(fpLocked);
assertThat(mKeyguardUpdateMonitor.isFaceLockedOut()).isEqualTo(faceLocked);
+
+ // Fingerprint should be restarted once its cancelled bc on lockout, the device
+ // can still detectFingerprint (and if it's not locked out, fingerprint can listen)
+ assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
+ .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING);
}
@Test
@@ -1144,9 +1202,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// GIVEN status bar state is on the keyguard
mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
- // WHEN user hasn't authenticated since last boot
- when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
- .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ // WHEN user hasn't authenticated since last boot, cannot unlock with FP
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
// THEN we shouldn't listen for udfps
assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
@@ -1259,8 +1316,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
// WHEN device in lock down
- when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
// THEN we shouldn't listen for udfps
assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
@@ -1294,6 +1350,24 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void testRequestFaceAuthFromOccludingApp_whenInvoked_startsFaceAuth() {
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+
+ assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isTrue();
+ }
+
+ @Test
+ public void testRequestFaceAuthFromOccludingApp_whenInvoked_stopsFaceAuth() {
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+
+ assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isTrue();
+
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
+
+ assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isFalse();
+ }
+
+ @Test
public void testRequireUnlockForNfc_Broadcast() {
KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
mKeyguardUpdateMonitor.registerCallback(callback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 181839ab512f..0627fc6c542f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -77,7 +77,6 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.decor.CornerDecorProvider;
import com.android.systemui.decor.CutoutDecorProviderFactory;
import com.android.systemui.decor.CutoutDecorProviderImpl;
@@ -132,8 +131,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
@Mock
private TunerService mTunerService;
@Mock
- private BroadcastDispatcher mBroadcastDispatcher;
- @Mock
private UserTracker mUserTracker;
@Mock
private PrivacyDotViewController mDotViewController;
@@ -223,8 +220,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mExecutor));
mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
- mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
- mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory) {
+ mTunerService, mUserTracker, mDotViewController, mThreadFactory,
+ mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory) {
@Override
public void start() {
super.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 0b528a5c8d1e..b765ab3c5eac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -26,6 +26,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.LightRevealScrim
@@ -37,7 +38,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.leak.RotationUtils
-import javax.inject.Provider
+import com.android.systemui.util.mockito.any
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -46,15 +47,16 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.Mockito.any
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
+import javax.inject.Provider
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -76,6 +78,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var lightRevealScrim: LightRevealScrim
@Mock private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
@@ -105,6 +108,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
biometricUnlockController,
udfpsControllerProvider,
statusBarStateController,
+ featureFlags,
rippleView
)
controller.init()
@@ -118,12 +122,13 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Test
fun testFingerprintTrigger_KeyguardShowing_Ripple() {
- // GIVEN fp exists, keyguard is showing, user doesn't need strong auth
+ // GIVEN fp exists, keyguard is showing, unlocking with fp allowed
val fpsLocation = Point(5, 5)
`when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
controller.onViewAttached()
`when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
// WHEN fingerprint authenticated
val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
@@ -140,11 +145,12 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Test
fun testFingerprintTrigger_KeyguardNotShowing_NoRipple() {
- // GIVEN fp exists & user doesn't need strong auth
+ // GIVEN fp exists & unlocking with fp allowed
val fpsLocation = Point(5, 5)
`when`(authController.udfpsLocation).thenReturn(fpsLocation)
controller.onViewAttached()
- `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
// WHEN keyguard is NOT showing & fingerprint authenticated
`when`(keyguardStateController.isShowing).thenReturn(false)
@@ -160,15 +166,16 @@ class AuthRippleControllerTest : SysuiTestCase() {
}
@Test
- fun testFingerprintTrigger_StrongAuthRequired_NoRipple() {
+ fun testFingerprintTrigger_biometricUnlockNotAllowed_NoRipple() {
// GIVEN fp exists & keyguard is showing
val fpsLocation = Point(5, 5)
`when`(authController.udfpsLocation).thenReturn(fpsLocation)
controller.onViewAttached()
`when`(keyguardStateController.isShowing).thenReturn(true)
- // WHEN user needs strong auth & fingerprint authenticated
- `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(true)
+ // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ eq(BiometricSourceType.FINGERPRINT))).thenReturn(false)
val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
verify(keyguardUpdateMonitor).registerCallback(captor.capture())
captor.value.onBiometricAuthenticated(
@@ -182,13 +189,14 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Test
fun testFaceTriggerBypassEnabled_Ripple() {
- // GIVEN face auth sensor exists, keyguard is showing & strong auth isn't required
+ // GIVEN face auth sensor exists, keyguard is showing & unlocking with face is allowed
val faceLocation = Point(5, 5)
`when`(authController.faceSensorLocation).thenReturn(faceLocation)
controller.onViewAttached()
`when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FACE)).thenReturn(true)
// WHEN bypass is enabled & face authenticated
`when`(bypassController.canBypass()).thenReturn(true)
@@ -275,6 +283,8 @@ class AuthRippleControllerTest : SysuiTestCase() {
`when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
controller.onViewAttached()
`when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FINGERPRINT)).thenReturn(true)
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
controller.showUnlockRipple(BiometricSourceType.FINGERPRINT)
@@ -295,6 +305,8 @@ class AuthRippleControllerTest : SysuiTestCase() {
`when`(keyguardStateController.isShowing).thenReturn(true)
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
`when`(authController.isUdfpsFingerDown).thenReturn(true)
+ `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ eq(BiometricSourceType.FACE))).thenReturn(true)
controller.showUnlockRipple(BiometricSourceType.FACE)
assertTrue("reveal didn't start on keyguardFadingAway",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index acdafe3e1c7d..b267a5c23a49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -70,8 +70,13 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.biometrics.udfps.InteractionEvent;
+import com.android.systemui.biometrics.udfps.NormalizedTouchData;
+import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
+import com.android.systemui.biometrics.udfps.TouchProcessorResult;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
@@ -190,6 +195,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private AlternateUdfpsTouchProvider mAlternateTouchProvider;
@Mock
private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+ @Mock
+ private SinglePointerTouchProcessor mSinglePointerTouchProcessor;
// Capture listeners so that they can be used to send events
@Captor
@@ -275,7 +282,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mDisplayManager, mHandler, mConfigurationController, mSystemClock,
mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker,
mActivityLaunchAnimator, alternateTouchProvider, mBiometricsExecutor,
- mPrimaryBouncerInteractor);
+ mPrimaryBouncerInteractor, mSinglePointerTouchProcessor);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -1086,4 +1093,100 @@ public class UdfpsControllerTest extends SysuiTestCase {
anyString(),
any());
}
+
+ @Test
+ public void onTouch_withoutNewTouchDetection_shouldCallOldFingerprintManagerPath()
+ throws RemoteException {
+ // Disable new touch detection.
+ when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(false);
+
+ // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+ initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // WHEN ACTION_DOWN is received
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricsExecutor.runAllReady();
+ downEvent.recycle();
+
+ // AND ACTION_MOVE is received
+ MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+ mBiometricsExecutor.runAllReady();
+ moveEvent.recycle();
+
+ // AND ACTION_UP is received
+ MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
+ mBiometricsExecutor.runAllReady();
+ upEvent.recycle();
+
+ // THEN the old FingerprintManager path is invoked.
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
+ anyFloat(), anyFloat());
+ verify(mFingerprintManager).onPointerUp(anyLong(), anyInt());
+ }
+
+ @Test
+ public void onTouch_withNewTouchDetection_shouldCallOldFingerprintManagerPath()
+ throws RemoteException {
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
+ InteractionEvent.DOWN, 1 /* pointerId */, touchData);
+ final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch(
+ InteractionEvent.UP, 1 /* pointerId */, touchData);
+
+ // Enable new touch detection.
+ when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+ // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+ initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDown);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricsExecutor.runAllReady();
+ downEvent.recycle();
+
+ // AND ACTION_UP is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultUp);
+ MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
+ mBiometricsExecutor.runAllReady();
+ upEvent.recycle();
+
+ // THEN the new FingerprintManager path is invoked.
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsEnrollViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsEnrollViewTest.java
new file mode 100644
index 000000000000..60a02582269c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsEnrollViewTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.biometrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class UdfpsEnrollViewTest extends SysuiTestCase {
+
+ private static String ENROLL_PROGRESS_COLOR_LIGHT = "#699FF3";
+ private static String ENROLL_PROGRESS_COLOR_DARK = "#7DA7F1";
+
+ @Test
+ public void fingerprintUdfpsEnroll_usesCorrectThemeCheckmarkFillColor() {
+ final Configuration config = mContext.getResources().getConfiguration();
+ final boolean isDarkThemeOn = (config.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+ final int currentColor = mContext.getColor(R.color.udfps_enroll_progress);
+
+ assertThat(currentColor).isEqualTo(Color.parseColor(isDarkThemeOn
+ ? ENROLL_PROGRESS_COLOR_DARK : ENROLL_PROGRESS_COLOR_LIGHT));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
new file mode 100644
index 000000000000..4f89b69108f4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+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
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@SmallTest
+@RunWith(Parameterized::class)
+class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
+ val underTest = BoundingBoxOverlapDetector()
+
+ @Test
+ fun isGoodOverlap() {
+ val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat())
+ val actual = underTest.isGoodOverlap(touchData, SENSOR)
+
+ assertThat(actual).isEqualTo(testCase.expected)
+ }
+
+ data class TestCase(val x: Int, val y: Int, val expected: Boolean)
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): List<TestCase> =
+ listOf(
+ genPositiveTestCases(
+ validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+ validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+ ),
+ genNegativeTestCases(
+ invalidXs = listOf(SENSOR.left - 1, SENSOR.right + 1),
+ invalidYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1),
+ validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+ validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+ )
+ )
+ .flatten()
+ }
+}
+
+/* Placeholder touch parameters. */
+private const val POINTER_ID = 42
+private const val NATIVE_MINOR = 2.71828f
+private const val NATIVE_MAJOR = 3.14f
+private const val ORIENTATION = 1.23f
+private const val TIME = 12345699L
+private const val GESTURE_START = 12345600L
+
+/* Template [NormalizedTouchData]. */
+private val TOUCH_DATA =
+ NormalizedTouchData(
+ POINTER_ID,
+ x = 0f,
+ y = 0f,
+ NATIVE_MINOR,
+ NATIVE_MAJOR,
+ ORIENTATION,
+ TIME,
+ GESTURE_START
+ )
+
+private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */)
+
+private fun genTestCases(
+ xs: List<Int>,
+ ys: List<Int>,
+ expected: Boolean
+): List<BoundingBoxOverlapDetectorTest.TestCase> {
+ return xs.flatMap { x ->
+ ys.map { y -> BoundingBoxOverlapDetectorTest.TestCase(x, y, expected) }
+ }
+}
+
+private fun genPositiveTestCases(
+ validXs: List<Int>,
+ validYs: List<Int>,
+) = genTestCases(validXs, validYs, expected = true)
+
+private fun genNegativeTestCases(
+ invalidXs: List<Int>,
+ invalidYs: List<Int>,
+ validXs: List<Int>,
+ validYs: List<Int>,
+): List<BoundingBoxOverlapDetectorTest.TestCase> {
+ return genTestCases(invalidXs, validYs, expected = false) +
+ genTestCases(validXs, invalidYs, expected = false)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
new file mode 100644
index 000000000000..834d0a69e427
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
@@ -0,0 +1,90 @@
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+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
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@SmallTest
+@RunWith(Parameterized::class)
+class NormalizedTouchDataTest(val testCase: TestCase) : SysuiTestCase() {
+
+ @Test
+ fun isWithinSensor() {
+ val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat())
+ val actual = touchData.isWithinSensor(SENSOR)
+
+ assertThat(actual).isEqualTo(testCase.expected)
+ }
+
+ data class TestCase(val x: Int, val y: Int, val expected: Boolean)
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): List<TestCase> =
+ listOf(
+ genPositiveTestCases(
+ validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+ validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+ ),
+ genNegativeTestCases(
+ invalidXs = listOf(SENSOR.left - 1, SENSOR.right + 1),
+ invalidYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1),
+ validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+ validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+ )
+ )
+ .flatten()
+ }
+}
+
+/* Placeholder touch parameters. */
+private const val POINTER_ID = 42
+private const val NATIVE_MINOR = 2.71828f
+private const val NATIVE_MAJOR = 3.14f
+private const val ORIENTATION = 1.23f
+private const val TIME = 12345699L
+private const val GESTURE_START = 12345600L
+
+/* Template [NormalizedTouchData]. */
+private val TOUCH_DATA =
+ NormalizedTouchData(
+ POINTER_ID,
+ x = 0f,
+ y = 0f,
+ NATIVE_MINOR,
+ NATIVE_MAJOR,
+ ORIENTATION,
+ TIME,
+ GESTURE_START
+ )
+
+private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */)
+
+private fun genTestCases(
+ xs: List<Int>,
+ ys: List<Int>,
+ expected: Boolean
+): List<NormalizedTouchDataTest.TestCase> {
+ return xs.flatMap { x -> ys.map { y -> NormalizedTouchDataTest.TestCase(x, y, expected) } }
+}
+
+private fun genPositiveTestCases(
+ validXs: List<Int>,
+ validYs: List<Int>,
+) = genTestCases(validXs, validYs, expected = true)
+
+private fun genNegativeTestCases(
+ invalidXs: List<Int>,
+ invalidYs: List<Int>,
+ validXs: List<Int>,
+ validYs: List<Int>,
+): List<NormalizedTouchDataTest.TestCase> {
+ return genTestCases(invalidXs, validYs, expected = false) +
+ genTestCases(validXs, invalidYs, expected = false)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
new file mode 100644
index 000000000000..95c53b408056
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
@@ -0,0 +1,506 @@
+/*
+ * 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.biometrics.udfps
+
+import android.graphics.Rect
+import android.view.MotionEvent
+import android.view.MotionEvent.INVALID_POINTER_ID
+import android.view.MotionEvent.PointerProperties
+import android.view.Surface
+import android.view.Surface.Rotation
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.UdfpsOverlayParams
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@SmallTest
+@RunWith(Parameterized::class)
+class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase() {
+ private val overlapDetector = FakeOverlapDetector()
+ private val underTest = SinglePointerTouchProcessor(overlapDetector)
+
+ @Test
+ fun processTouch() {
+ overlapDetector.shouldReturn = testCase.isGoodOverlap
+
+ val actual =
+ underTest.processTouch(
+ testCase.event,
+ testCase.previousPointerOnSensorId,
+ testCase.overlayParams,
+ )
+
+ assertThat(actual).isInstanceOf(testCase.expected.javaClass)
+ if (actual is TouchProcessorResult.ProcessedTouch) {
+ assertThat(actual).isEqualTo(testCase.expected)
+ }
+ }
+
+ data class TestCase(
+ val event: MotionEvent,
+ val isGoodOverlap: Boolean,
+ val previousPointerOnSensorId: Int,
+ val overlayParams: UdfpsOverlayParams,
+ val expected: TouchProcessorResult,
+ ) {
+ override fun toString(): String {
+ val expectedOutput =
+ if (expected is TouchProcessorResult.ProcessedTouch) {
+ expected.event.toString() +
+ ", (x: ${expected.touchData.x}, y: ${expected.touchData.y})" +
+ ", pointerOnSensorId: ${expected.pointerOnSensorId}" +
+ ", ..."
+ } else {
+ TouchProcessorResult.Failure().toString()
+ }
+ return "{" +
+ MotionEvent.actionToString(event.action) +
+ ", (x: ${event.x}, y: ${event.y})" +
+ ", scale: ${overlayParams.scaleFactor}" +
+ ", rotation: " +
+ Surface.rotationToString(overlayParams.rotation) +
+ ", previousPointerOnSensorId: $previousPointerOnSensorId" +
+ ", ...} expected: {$expectedOutput}"
+ }
+ }
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): List<TestCase> =
+ listOf(
+ // MotionEvent.ACTION_DOWN
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_DOWN,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_DOWN,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_DOWN,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_DOWN,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ // MotionEvent.ACTION_MOVE
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_MOVE,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_MOVE,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_MOVE,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_MOVE,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ // MotionEvent.ACTION_UP
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_UP,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_UP,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_UP,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_UP,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ // MotionEvent.ACTION_CANCEL
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_CANCEL,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.CANCEL,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_CANCEL,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.CANCEL,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_CANCEL,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.CANCEL,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_CANCEL,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.CANCEL,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ )
+ .flatten() +
+ listOf(
+ // Unsupported MotionEvent actions.
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_POINTER_DOWN),
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_POINTER_UP),
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_ENTER),
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_MOVE),
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_EXIT),
+ )
+ .flatten()
+ }
+}
+
+/* Display dimensions in native resolution and natural orientation. */
+private const val ROTATION_0_NATIVE_DISPLAY_WIDTH = 400
+private const val ROTATION_0_NATIVE_DISPLAY_HEIGHT = 600
+
+/*
+ * ROTATION_0 map:
+ * _ _ _ _
+ * _ _ O _
+ * _ _ _ _
+ * _ S _ _
+ * _ S _ _
+ * _ _ _ _
+ *
+ * (_) empty space
+ * (S) sensor
+ * (O) touch outside of the sensor
+ */
+private val ROTATION_0_NATIVE_SENSOR_BOUNDS =
+ Rect(
+ 100, /* left */
+ 300, /* top */
+ 200, /* right */
+ 500, /* bottom */
+ )
+private val ROTATION_0_INPUTS =
+ OrientationBasedInputs(
+ rotation = Surface.ROTATION_0,
+ nativeXWithinSensor = ROTATION_0_NATIVE_SENSOR_BOUNDS.exactCenterX(),
+ nativeYWithinSensor = ROTATION_0_NATIVE_SENSOR_BOUNDS.exactCenterY(),
+ nativeXOutsideSensor = 250f,
+ nativeYOutsideSensor = 150f,
+ )
+
+/*
+ * ROTATION_90 map:
+ * _ _ _ _ _ _
+ * _ O _ _ _ _
+ * _ _ _ S S _
+ * _ _ _ _ _ _
+ *
+ * (_) empty space
+ * (S) sensor
+ * (O) touch outside of the sensor
+ */
+private val ROTATION_90_NATIVE_SENSOR_BOUNDS =
+ Rect(
+ 300, /* left */
+ 200, /* top */
+ 500, /* right */
+ 300, /* bottom */
+ )
+private val ROTATION_90_INPUTS =
+ OrientationBasedInputs(
+ rotation = Surface.ROTATION_90,
+ nativeXWithinSensor = ROTATION_90_NATIVE_SENSOR_BOUNDS.exactCenterX(),
+ nativeYWithinSensor = ROTATION_90_NATIVE_SENSOR_BOUNDS.exactCenterY(),
+ nativeXOutsideSensor = 150f,
+ nativeYOutsideSensor = 150f,
+ )
+
+/* ROTATION_180 is not supported. It's treated the same as ROTATION_0. */
+private val ROTATION_180_INPUTS =
+ ROTATION_0_INPUTS.copy(
+ rotation = Surface.ROTATION_180,
+ )
+
+/*
+ * ROTATION_270 map:
+ * _ _ _ _ _ _
+ * _ S S _ _ _
+ * _ _ _ _ O _
+ * _ _ _ _ _ _
+ *
+ * (_) empty space
+ * (S) sensor
+ * (O) touch outside of the sensor
+ */
+private val ROTATION_270_NATIVE_SENSOR_BOUNDS =
+ Rect(
+ 100, /* left */
+ 100, /* top */
+ 300, /* right */
+ 200, /* bottom */
+ )
+private val ROTATION_270_INPUTS =
+ OrientationBasedInputs(
+ rotation = Surface.ROTATION_270,
+ nativeXWithinSensor = ROTATION_270_NATIVE_SENSOR_BOUNDS.exactCenterX(),
+ nativeYWithinSensor = ROTATION_270_NATIVE_SENSOR_BOUNDS.exactCenterY(),
+ nativeXOutsideSensor = 450f,
+ nativeYOutsideSensor = 250f,
+ )
+
+/* Placeholder touch parameters. */
+private const val POINTER_ID = 42
+private const val NATIVE_MINOR = 2.71828f
+private const val NATIVE_MAJOR = 3.14f
+private const val ORIENTATION = 1.23f
+private const val TIME = 12345699L
+private const val GESTURE_START = 12345600L
+
+/* Template [MotionEvent]. */
+private val MOTION_EVENT =
+ obtainMotionEvent(
+ action = 0,
+ pointerId = POINTER_ID,
+ x = 0f,
+ y = 0f,
+ minor = 0f,
+ major = 0f,
+ orientation = ORIENTATION,
+ time = TIME,
+ gestureStart = GESTURE_START,
+ )
+
+/* Template [NormalizedTouchData]. */
+private val NORMALIZED_TOUCH_DATA =
+ NormalizedTouchData(
+ POINTER_ID,
+ x = 0f,
+ y = 0f,
+ NATIVE_MINOR,
+ NATIVE_MAJOR,
+ ORIENTATION,
+ TIME,
+ GESTURE_START
+ )
+
+/*
+ * Contains test inputs that are tied to a particular device orientation.
+ *
+ * "native" means in native resolution (not scaled).
+ */
+private data class OrientationBasedInputs(
+ @Rotation val rotation: Int,
+ val nativeXWithinSensor: Float,
+ val nativeYWithinSensor: Float,
+ val nativeXOutsideSensor: Float,
+ val nativeYOutsideSensor: Float,
+) {
+
+ fun toOverlayParams(scaleFactor: Float): UdfpsOverlayParams =
+ UdfpsOverlayParams(
+ sensorBounds = ROTATION_0_NATIVE_SENSOR_BOUNDS.scaled(scaleFactor),
+ overlayBounds = ROTATION_0_NATIVE_SENSOR_BOUNDS.scaled(scaleFactor),
+ naturalDisplayHeight = (ROTATION_0_NATIVE_DISPLAY_HEIGHT * scaleFactor).toInt(),
+ naturalDisplayWidth = (ROTATION_0_NATIVE_DISPLAY_WIDTH * scaleFactor).toInt(),
+ scaleFactor = scaleFactor,
+ rotation = rotation
+ )
+
+ fun getNativeX(isWithinSensor: Boolean): Float {
+ return if (isWithinSensor) nativeXWithinSensor else nativeXOutsideSensor
+ }
+
+ fun getNativeY(isWithinSensor: Boolean): Float {
+ return if (isWithinSensor) nativeYWithinSensor else nativeYOutsideSensor
+ }
+}
+
+private fun genPositiveTestCases(
+ motionEventAction: Int,
+ previousPointerOnSensorId: Int,
+ isGoodOverlap: Boolean,
+ expectedInteractionEvent: InteractionEvent,
+ expectedPointerOnSensorId: Int
+): List<SinglePointerTouchProcessorTest.TestCase> {
+ val scaleFactors = listOf(0.75f, 1f, 1.5f)
+ val orientations =
+ listOf(
+ ROTATION_0_INPUTS,
+ ROTATION_90_INPUTS,
+ ROTATION_180_INPUTS,
+ ROTATION_270_INPUTS,
+ )
+ return scaleFactors.flatMap { scaleFactor ->
+ orientations.map { orientation ->
+ val overlayParams = orientation.toOverlayParams(scaleFactor)
+ val nativeX = orientation.getNativeX(isGoodOverlap)
+ val nativeY = orientation.getNativeY(isGoodOverlap)
+ val event =
+ MOTION_EVENT.copy(
+ action = motionEventAction,
+ x = nativeX * scaleFactor,
+ y = nativeY * scaleFactor,
+ minor = NATIVE_MINOR * scaleFactor,
+ major = NATIVE_MAJOR * scaleFactor,
+ )
+ val expectedTouchData =
+ NORMALIZED_TOUCH_DATA.copy(
+ x = ROTATION_0_INPUTS.getNativeX(isGoodOverlap),
+ y = ROTATION_0_INPUTS.getNativeY(isGoodOverlap),
+ )
+ val expected =
+ TouchProcessorResult.ProcessedTouch(
+ event = expectedInteractionEvent,
+ pointerOnSensorId = expectedPointerOnSensorId,
+ touchData = expectedTouchData,
+ )
+ SinglePointerTouchProcessorTest.TestCase(
+ event = event,
+ isGoodOverlap = isGoodOverlap,
+ previousPointerOnSensorId = previousPointerOnSensorId,
+ overlayParams = overlayParams,
+ expected = expected,
+ )
+ }
+ }
+}
+
+private fun genTestCasesForUnsupportedAction(
+ motionEventAction: Int
+): List<SinglePointerTouchProcessorTest.TestCase> {
+ val isGoodOverlap = true
+ val previousPointerOnSensorIds = listOf(INVALID_POINTER_ID, POINTER_ID)
+ return previousPointerOnSensorIds.map { previousPointerOnSensorId ->
+ val overlayParams = ROTATION_0_INPUTS.toOverlayParams(scaleFactor = 1f)
+ val nativeX = ROTATION_0_INPUTS.getNativeX(isGoodOverlap)
+ val nativeY = ROTATION_0_INPUTS.getNativeY(isGoodOverlap)
+ val event =
+ MOTION_EVENT.copy(
+ action = motionEventAction,
+ x = nativeX,
+ y = nativeY,
+ minor = NATIVE_MINOR,
+ major = NATIVE_MAJOR,
+ )
+ SinglePointerTouchProcessorTest.TestCase(
+ event = event,
+ isGoodOverlap = isGoodOverlap,
+ previousPointerOnSensorId = previousPointerOnSensorId,
+ overlayParams = overlayParams,
+ expected = TouchProcessorResult.Failure(),
+ )
+ }
+}
+
+private fun obtainMotionEvent(
+ action: Int,
+ pointerId: Int,
+ x: Float,
+ y: Float,
+ minor: Float,
+ major: Float,
+ orientation: Float,
+ time: Long,
+ gestureStart: Long,
+): MotionEvent {
+ val pp = PointerProperties()
+ pp.id = pointerId
+ val pc = MotionEvent.PointerCoords()
+ pc.x = x
+ pc.y = y
+ pc.touchMinor = minor
+ pc.touchMajor = major
+ pc.orientation = orientation
+ return MotionEvent.obtain(
+ gestureStart /* downTime */,
+ time /* eventTime */,
+ action /* action */,
+ 1 /* pointerCount */,
+ arrayOf(pp) /* pointerProperties */,
+ arrayOf(pc) /* pointerCoords */,
+ 0 /* metaState */,
+ 0 /* buttonState */,
+ 1f /* xPrecision */,
+ 1f /* yPrecision */,
+ 0 /* deviceId */,
+ 0 /* edgeFlags */,
+ 0 /* source */,
+ 0 /* flags */
+ )
+}
+
+private fun MotionEvent.copy(
+ action: Int = this.action,
+ pointerId: Int = this.getPointerId(0),
+ x: Float = this.rawX,
+ y: Float = this.rawY,
+ minor: Float = this.touchMinor,
+ major: Float = this.touchMajor,
+ orientation: Float = this.orientation,
+ time: Long = this.eventTime,
+ gestureStart: Long = this.downTime,
+) = obtainMotionEvent(action, pointerId, x, y, minor, major, orientation, time, gestureStart)
+
+private fun Rect.scaled(scaleFactor: Float) = Rect(this).apply { scale(scaleFactor) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt
new file mode 100644
index 000000000000..4b88b44c3f03
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt
@@ -0,0 +1,187 @@
+/*
+ * 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.controls
+
+import android.content.pm.UserInfo
+import android.provider.Settings
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class ControlsSettingsRepositoryImplTest : SysuiTestCase() {
+
+ companion object {
+ private const val LOCKSCREEN_SHOW = Settings.Secure.LOCKSCREEN_SHOW_CONTROLS
+ private const val LOCKSCREEN_ACTION = Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS
+
+ private fun createUser(id: Int): UserInfo {
+ return UserInfo(id, "user_$id", 0)
+ }
+
+ private val ALL_USERS = (0..1).map { it to createUser(it) }.toMap()
+ }
+
+ private lateinit var underTest: ControlsSettingsRepository
+
+ private lateinit var testScope: TestScope
+ private lateinit var secureSettings: FakeSettings
+ private lateinit var userRepository: FakeUserRepository
+
+ @Before
+ fun setUp() {
+ secureSettings = FakeSettings()
+ userRepository = FakeUserRepository()
+ userRepository.setUserInfos(ALL_USERS.values.toList())
+
+ val coroutineDispatcher = UnconfinedTestDispatcher()
+ testScope = TestScope(coroutineDispatcher)
+
+ underTest =
+ ControlsSettingsRepositoryImpl(
+ scope = testScope.backgroundScope,
+ backgroundDispatcher = coroutineDispatcher,
+ userRepository = userRepository,
+ secureSettings = secureSettings,
+ )
+ }
+
+ @Test
+ fun showInLockScreen() =
+ testScope.runTest {
+ setUser(0)
+ val values = mutableListOf<Boolean>()
+ val job =
+ launch(UnconfinedTestDispatcher()) {
+ underTest.canShowControlsInLockscreen.toList(values)
+ }
+ assertThat(values.last()).isFalse()
+
+ secureSettings.putBool(LOCKSCREEN_SHOW, true)
+ assertThat(values.last()).isTrue()
+
+ secureSettings.putBool(LOCKSCREEN_SHOW, false)
+ assertThat(values.last()).isFalse()
+
+ secureSettings.putBoolForUser(LOCKSCREEN_SHOW, true, 1)
+ assertThat(values.last()).isFalse()
+
+ setUser(1)
+ assertThat(values.last()).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun showInLockScreen_changesInOtherUsersAreNotQueued() =
+ testScope.runTest {
+ setUser(0)
+
+ val values = mutableListOf<Boolean>()
+ val job =
+ launch(UnconfinedTestDispatcher()) {
+ underTest.canShowControlsInLockscreen.toList(values)
+ }
+
+ secureSettings.putBoolForUser(LOCKSCREEN_SHOW, true, 1)
+ secureSettings.putBoolForUser(LOCKSCREEN_SHOW, false, 1)
+
+ setUser(1)
+ assertThat(values.last()).isFalse()
+ assertThat(values).containsNoneIn(listOf(true))
+
+ job.cancel()
+ }
+
+ @Test
+ fun actionInLockScreen() =
+ testScope.runTest {
+ setUser(0)
+ val values = mutableListOf<Boolean>()
+ val job =
+ launch(UnconfinedTestDispatcher()) {
+ underTest.allowActionOnTrivialControlsInLockscreen.toList(values)
+ }
+ assertThat(values.last()).isFalse()
+
+ secureSettings.putBool(LOCKSCREEN_ACTION, true)
+ assertThat(values.last()).isTrue()
+
+ secureSettings.putBool(LOCKSCREEN_ACTION, false)
+ assertThat(values.last()).isFalse()
+
+ secureSettings.putBoolForUser(LOCKSCREEN_ACTION, true, 1)
+ assertThat(values.last()).isFalse()
+
+ setUser(1)
+ assertThat(values.last()).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun actionInLockScreen_changesInOtherUsersAreNotQueued() =
+ testScope.runTest {
+ setUser(0)
+
+ val values = mutableListOf<Boolean>()
+ val job =
+ launch(UnconfinedTestDispatcher()) {
+ underTest.allowActionOnTrivialControlsInLockscreen.toList(values)
+ }
+
+ secureSettings.putBoolForUser(LOCKSCREEN_ACTION, true, 1)
+ secureSettings.putBoolForUser(LOCKSCREEN_ACTION, false, 1)
+
+ setUser(1)
+ assertThat(values.last()).isFalse()
+ assertThat(values).containsNoneIn(listOf(true))
+
+ job.cancel()
+ }
+
+ @Test
+ fun valueIsUpdatedWhenNotSubscribed() =
+ testScope.runTest {
+ setUser(0)
+ assertThat(underTest.canShowControlsInLockscreen.value).isFalse()
+
+ secureSettings.putBool(LOCKSCREEN_SHOW, true)
+
+ assertThat(underTest.canShowControlsInLockscreen.value).isTrue()
+ }
+
+ private suspend fun setUser(id: Int) {
+ secureSettings.userId = id
+ userRepository.setSelectedUserInfo(ALL_USERS[id]!!)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt
new file mode 100644
index 000000000000..8a1bed20e700
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.controls
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeControlsSettingsRepository : ControlsSettingsRepository {
+ private val _canShowControlsInLockscreen = MutableStateFlow(false)
+ override val canShowControlsInLockscreen = _canShowControlsInLockscreen.asStateFlow()
+ private val _allowActionOnTrivialControlsInLockscreen = MutableStateFlow(false)
+ override val allowActionOnTrivialControlsInLockscreen =
+ _allowActionOnTrivialControlsInLockscreen.asStateFlow()
+
+ fun setCanShowControlsInLockscreen(value: Boolean) {
+ _canShowControlsInLockscreen.value = value
+ }
+
+ fun setAllowActionOnTrivialControlsInLockscreen(value: Boolean) {
+ _allowActionOnTrivialControlsInLockscreen.value = value
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 4ed5649c9c50..1d00d6b05568 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -18,30 +18,24 @@ package com.android.systemui.controls.ui
import android.content.Context
import android.content.SharedPreferences
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.os.UserHandle
-import android.provider.Settings
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.controls.ControlsMetricsLogger
+import com.android.systemui.controls.FakeControlsSettingsRepository
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.settings.SecureSettings
import com.android.wm.shell.TaskViewFactory
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
-import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when`
@@ -79,8 +73,6 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
@Mock
private lateinit var secureSettings: SecureSettings
@Mock
- private lateinit var mainHandler: Handler
- @Mock
private lateinit var userContextProvider: UserContextProvider
companion object {
@@ -91,17 +83,15 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
private lateinit var coordinator: ControlActionCoordinatorImpl
private lateinit var action: ControlActionCoordinatorImpl.Action
+ private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(secureSettings.getUriFor(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS))
- .thenReturn(Settings.Secure
- .getUriFor(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS))
- `when`(secureSettings.getIntForUser(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
- 0, UserHandle.USER_CURRENT))
- .thenReturn(1)
+ controlsSettingsRepository = FakeControlsSettingsRepository()
+ controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
+ controlsSettingsRepository.setCanShowControlsInLockscreen(true)
coordinator = spy(ControlActionCoordinatorImpl(
mContext,
@@ -115,7 +105,7 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
vibratorHelper,
secureSettings,
userContextProvider,
- mainHandler
+ controlsSettingsRepository
))
val userContext = mock(Context::class.java)
@@ -128,9 +118,6 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
`when`(pref.getInt(DeviceControlsControllerImpl.PREFS_SETTINGS_DIALOG_ATTEMPTS, 0))
.thenReturn(2)
- verify(secureSettings).registerContentObserverForUser(any(Uri::class.java),
- anyBoolean(), any(ContentObserver::class.java), anyInt())
-
`when`(cvh.cws.ci.controlId).thenReturn(ID)
`when`(cvh.cws.control?.isAuthRequired()).thenReturn(true)
action = spy(coordinator.Action(ID, {}, false, true))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index c31fd828c730..1b34706bd220 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.controls.controller
import android.app.PendingIntent
-import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.ContextWrapper
@@ -31,7 +30,6 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.backup.BackupHelper
-import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.management.ControlsListingController
@@ -85,10 +83,8 @@ class ControlsControllerImplTest : SysuiTestCase() {
@Mock
private lateinit var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper
@Mock
- private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock
private lateinit var listingController: ControlsListingController
- @Mock(stubOnly = true)
+ @Mock
private lateinit var userTracker: UserTracker
@Mock
private lateinit var userFileManager: UserFileManager
@@ -104,7 +100,7 @@ class ControlsControllerImplTest : SysuiTestCase() {
ArgumentCaptor<ControlsBindingController.LoadCallback>
@Captor
- private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
+ private lateinit var userTrackerCallbackCaptor: ArgumentCaptor<UserTracker.Callback>
@Captor
private lateinit var listingCallbackCaptor:
ArgumentCaptor<ControlsListingController.ControlsListingCallback>
@@ -170,16 +166,15 @@ class ControlsControllerImplTest : SysuiTestCase() {
uiController,
bindingController,
listingController,
- broadcastDispatcher,
userFileManager,
+ userTracker,
Optional.of(persistenceWrapper),
- mock(DumpManager::class.java),
- userTracker
+ mock(DumpManager::class.java)
)
controller.auxiliaryPersistenceWrapper = auxiliaryPersistenceWrapper
- verify(broadcastDispatcher).registerReceiver(
- capture(broadcastReceiverCaptor), any(), any(), eq(UserHandle.ALL), anyInt(), any()
+ verify(userTracker).addCallback(
+ capture(userTrackerCallbackCaptor), any()
)
verify(listingController).addCallback(capture(listingCallbackCaptor))
@@ -227,11 +222,10 @@ class ControlsControllerImplTest : SysuiTestCase() {
uiController,
bindingController,
listingController,
- broadcastDispatcher,
userFileManager,
+ userTracker,
Optional.of(persistenceWrapper),
- mock(DumpManager::class.java),
- userTracker
+ mock(DumpManager::class.java)
)
assertEquals(listOf(TEST_STRUCTURE_INFO), controller_other.getFavorites())
}
@@ -518,14 +512,8 @@ class ControlsControllerImplTest : SysuiTestCase() {
delayableExecutor.runAllReady()
reset(persistenceWrapper)
- val intent = Intent(Intent.ACTION_USER_SWITCHED).apply {
- putExtra(Intent.EXTRA_USER_HANDLE, otherUser)
- }
- val pendingResult = mock(BroadcastReceiver.PendingResult::class.java)
- `when`(pendingResult.sendingUserId).thenReturn(otherUser)
- broadcastReceiverCaptor.value.pendingResult = pendingResult
- broadcastReceiverCaptor.value.onReceive(mContext, intent)
+ userTrackerCallbackCaptor.value.onUserChanged(otherUser, mContext)
verify(persistenceWrapper).changeFileAndBackupManager(any(), any())
verify(persistenceWrapper).readFavorites()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
index 77f451f61c3c..48fc46b7e730 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
@@ -17,19 +17,18 @@
package com.android.systemui.controls.dagger
import android.testing.AndroidTestingRunner
-import android.provider.Settings
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.FakeControlsSettingsRepository
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.ControlsTileResourceConfiguration
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.settings.SecureSettings
import dagger.Lazy
import java.util.Optional
import org.junit.Assert.assertEquals
@@ -63,13 +62,13 @@ class ControlsComponentTest : SysuiTestCase() {
@Mock
private lateinit var lockPatternUtils: LockPatternUtils
@Mock
- private lateinit var secureSettings: SecureSettings
- @Mock
private lateinit var optionalControlsTileResourceConfiguration:
Optional<ControlsTileResourceConfiguration>
@Mock
private lateinit var controlsTileResourceConfiguration: ControlsTileResourceConfiguration
+ private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
+
companion object {
fun <T> eq(value: T): T = Mockito.eq(value) ?: value
}
@@ -78,6 +77,8 @@ class ControlsComponentTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
+ controlsSettingsRepository = FakeControlsSettingsRepository()
+
`when`(userTracker.userHandle.identifier).thenReturn(0)
`when`(optionalControlsTileResourceConfiguration.orElse(any()))
.thenReturn(controlsTileResourceConfiguration)
@@ -125,8 +126,7 @@ class ControlsComponentTest : SysuiTestCase() {
`when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(STRONG_AUTH_NOT_REQUIRED)
`when`(keyguardStateController.isUnlocked()).thenReturn(false)
- `when`(secureSettings.getInt(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS), anyInt()))
- .thenReturn(0)
+ controlsSettingsRepository.setCanShowControlsInLockscreen(false)
val component = setupComponent(true)
assertEquals(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK, component.getVisibility())
@@ -137,9 +137,7 @@ class ControlsComponentTest : SysuiTestCase() {
`when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(STRONG_AUTH_NOT_REQUIRED)
`when`(keyguardStateController.isUnlocked()).thenReturn(false)
- `when`(secureSettings.getIntForUser(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS),
- anyInt(), anyInt()))
- .thenReturn(1)
+ controlsSettingsRepository.setCanShowControlsInLockscreen(true)
val component = setupComponent(true)
assertEquals(ControlsComponent.Visibility.AVAILABLE, component.getVisibility())
@@ -147,8 +145,7 @@ class ControlsComponentTest : SysuiTestCase() {
@Test
fun testFeatureEnabledAndCanShowWhileUnlockedVisibility() {
- `when`(secureSettings.getInt(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS), anyInt()))
- .thenReturn(0)
+ controlsSettingsRepository.setCanShowControlsInLockscreen(false)
`when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(STRONG_AUTH_NOT_REQUIRED)
`when`(keyguardStateController.isUnlocked()).thenReturn(true)
@@ -187,7 +184,7 @@ class ControlsComponentTest : SysuiTestCase() {
lockPatternUtils,
keyguardStateController,
userTracker,
- secureSettings,
+ controlsSettingsRepository,
optionalControlsTileResourceConfiguration
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index 98ff8d1d8845..c677f19f93e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -31,6 +31,7 @@ import android.service.controls.ControlsProviderService
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.settingslib.applications.ServiceListing
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.dump.DumpManager
@@ -110,6 +111,12 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED)
mContext.setMockPackageManager(packageManager)
+ mContext.orCreateTestableResources
+ .addOverride(
+ R.array.config_controlsPreferredPackages,
+ arrayOf(componentName.packageName)
+ )
+
// Return true by default, we'll test the false path
`when`(featureFlags.isEnabled(USE_APP_PANELS)).thenReturn(true)
@@ -482,6 +489,35 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
}
@Test
+ fun testPackageNotPreferred_nullPanel() {
+ mContext.orCreateTestableResources
+ .addOverride(R.array.config_controlsPreferredPackages, arrayOf<String>())
+
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
fun testListingsNotModifiedByCallback() {
// This test checks that if the list passed to the callback is modified, it has no effect
// in the resulting services
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index 7a2ba95f74a0..06a944e7a6d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -361,8 +361,7 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
assertThat(lp.getMarginEnd()).isEqualTo(margin);
});
- // The third view should be at the top end corner. No margin should be applied if not
- // specified.
+ // The third view should be at the top end corner. No margin should be applied.
verifyChange(thirdViewInfo, true, lp -> {
assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
@@ -442,65 +441,129 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
}
/**
- * Ensures the root complication applies margin if specified.
+ * Ensures layout sets correct max width constraint.
*/
@Test
- public void testRootComplicationSpecifiedMargin() {
- final int defaultMargin = 5;
- final int complicationMargin = 10;
+ public void testWidthConstraint() {
+ final int maxWidth = 20;
final ComplicationLayoutEngine engine =
- new ComplicationLayoutEngine(mLayout, defaultMargin, mTouchSession, 0, 0);
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession, 0, 0);
- final ViewInfo firstViewInfo = new ViewInfo(
+ final ViewInfo viewStartDirection = new ViewInfo(
new ComplicationLayoutParams(
100,
100,
ComplicationLayoutParams.POSITION_TOP
| ComplicationLayoutParams.POSITION_END,
- ComplicationLayoutParams.DIRECTION_DOWN,
- 0),
+ ComplicationLayoutParams.DIRECTION_START,
+ 0,
+ 5,
+ maxWidth),
+ Complication.CATEGORY_STANDARD,
+ mLayout);
+ final ViewInfo viewEndDirection = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_END,
+ 0,
+ 5,
+ maxWidth),
Complication.CATEGORY_STANDARD,
mLayout);
- addComplication(engine, firstViewInfo);
+ addComplication(engine, viewStartDirection);
+ addComplication(engine, viewEndDirection);
- final ViewInfo secondViewInfo = new ViewInfo(
+ // Verify both horizontal direction views have max width set correctly, and max height is
+ // not set.
+ verifyChange(viewStartDirection, false, lp -> {
+ assertThat(lp.matchConstraintMaxWidth).isEqualTo(maxWidth);
+ assertThat(lp.matchConstraintMaxHeight).isEqualTo(0);
+ });
+ verifyChange(viewEndDirection, false, lp -> {
+ assertThat(lp.matchConstraintMaxWidth).isEqualTo(maxWidth);
+ assertThat(lp.matchConstraintMaxHeight).isEqualTo(0);
+ });
+ }
+
+ /**
+ * Ensures layout sets correct max height constraint.
+ */
+ @Test
+ public void testHeightConstraint() {
+ final int maxHeight = 20;
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession, 0, 0);
+
+ final ViewInfo viewUpDirection = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_BOTTOM
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_UP,
+ 0,
+ 5,
+ maxHeight),
+ Complication.CATEGORY_STANDARD,
+ mLayout);
+ final ViewInfo viewDownDirection = new ViewInfo(
new ComplicationLayoutParams(
100,
100,
ComplicationLayoutParams.POSITION_TOP
| ComplicationLayoutParams.POSITION_END,
- ComplicationLayoutParams.DIRECTION_START,
- 0),
- Complication.CATEGORY_SYSTEM,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0,
+ 5,
+ maxHeight),
+ Complication.CATEGORY_STANDARD,
mLayout);
- addComplication(engine, secondViewInfo);
+ addComplication(engine, viewUpDirection);
+ addComplication(engine, viewDownDirection);
- firstViewInfo.clearInvocations();
- secondViewInfo.clearInvocations();
+ // Verify both vertical direction views have max height set correctly, and max width is
+ // not set.
+ verifyChange(viewUpDirection, false, lp -> {
+ assertThat(lp.matchConstraintMaxHeight).isEqualTo(maxHeight);
+ assertThat(lp.matchConstraintMaxWidth).isEqualTo(0);
+ });
+ verifyChange(viewDownDirection, false, lp -> {
+ assertThat(lp.matchConstraintMaxHeight).isEqualTo(maxHeight);
+ assertThat(lp.matchConstraintMaxWidth).isEqualTo(0);
+ });
+ }
- final ViewInfo thirdViewInfo = new ViewInfo(
+ /**
+ * Ensures layout does not set any constraint if not specified.
+ */
+ @Test
+ public void testConstraintNotSetWhenNotSpecified() {
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession, 0, 0);
+
+ final ViewInfo view = new ViewInfo(
new ComplicationLayoutParams(
100,
100,
ComplicationLayoutParams.POSITION_TOP
| ComplicationLayoutParams.POSITION_END,
- ComplicationLayoutParams.DIRECTION_START,
- 1,
- complicationMargin),
- Complication.CATEGORY_SYSTEM,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0,
+ 5),
+ Complication.CATEGORY_STANDARD,
mLayout);
- addComplication(engine, thirdViewInfo);
+ addComplication(engine, view);
- // The third view is the root view and has specified margin, which should be applied based
- // on its direction.
- verifyChange(thirdViewInfo, true, lp -> {
- assertThat(lp.getMarginStart()).isEqualTo(0);
- assertThat(lp.getMarginEnd()).isEqualTo(complicationMargin);
- assertThat(lp.topMargin).isEqualTo(0);
- assertThat(lp.bottomMargin).isEqualTo(0);
+ // Verify neither max height nor max width set.
+ verifyChange(view, false, lp -> {
+ assertThat(lp.matchConstraintMaxHeight).isEqualTo(0);
+ assertThat(lp.matchConstraintMaxWidth).isEqualTo(0);
});
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
index ce7561e95f1e..fdb4cc4480da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
@@ -97,35 +97,10 @@ public class ComplicationLayoutParamsTest extends SysuiTestCase {
}
/**
- * Ensures ComplicationLayoutParams correctly returns whether the complication specified margin.
- */
- @Test
- public void testIsMarginSpecified() {
- final ComplicationLayoutParams paramsNoMargin = new ComplicationLayoutParams(
- 100,
- 100,
- ComplicationLayoutParams.POSITION_TOP
- | ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_DOWN,
- 0);
- assertThat(paramsNoMargin.isMarginSpecified()).isFalse();
-
- final ComplicationLayoutParams paramsWithMargin = new ComplicationLayoutParams(
- 100,
- 100,
- ComplicationLayoutParams.POSITION_TOP
- | ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_DOWN,
- 0,
- 20 /*margin*/);
- assertThat(paramsWithMargin.isMarginSpecified()).isTrue();
- }
-
- /**
* Ensures unspecified margin uses default.
*/
@Test
- public void testUnspecifiedMarginUsesDefault() {
+ public void testDefaultMargin() {
final ComplicationLayoutParams params = new ComplicationLayoutParams(
100,
100,
@@ -161,13 +136,15 @@ public class ComplicationLayoutParamsTest extends SysuiTestCase {
ComplicationLayoutParams.POSITION_TOP,
ComplicationLayoutParams.DIRECTION_DOWN,
3,
- 10);
+ 10,
+ 20);
final ComplicationLayoutParams copy = new ComplicationLayoutParams(params);
assertThat(copy.getDirection() == params.getDirection()).isTrue();
assertThat(copy.getPosition() == params.getPosition()).isTrue();
assertThat(copy.getWeight() == params.getWeight()).isTrue();
assertThat(copy.getMargin(0) == params.getMargin(1)).isTrue();
+ assertThat(copy.getConstraint() == params.getConstraint()).isTrue();
assertThat(copy.height == params.height).isTrue();
assertThat(copy.width == params.width).isTrue();
}
@@ -193,4 +170,31 @@ public class ComplicationLayoutParamsTest extends SysuiTestCase {
assertThat(copy.height == params.height).isTrue();
assertThat(copy.width == params.width).isTrue();
}
+
+ /**
+ * Ensures that constraint is set correctly.
+ */
+ @Test
+ public void testConstraint() {
+ final ComplicationLayoutParams paramsWithoutConstraint = new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 3,
+ 10);
+ assertThat(paramsWithoutConstraint.constraintSpecified()).isFalse();
+
+ final int constraint = 10;
+ final ComplicationLayoutParams paramsWithConstraint = new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 3,
+ 10,
+ constraint);
+ assertThat(paramsWithConstraint.constraintSpecified()).isTrue();
+ assertThat(paramsWithConstraint.getConstraint()).isEqualTo(constraint);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index 30ad485d7ac3..e6d3a69593cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -35,6 +35,7 @@ import android.widget.ImageView;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.controls.ControlsServiceInfo;
import com.android.systemui.controls.controller.ControlsController;
@@ -84,7 +85,10 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
private ArgumentCaptor<ControlsListingController.ControlsListingCallback> mCallbackCaptor;
@Mock
- private ImageView mView;
+ private View mView;
+
+ @Mock
+ private ImageView mHomeControlsView;
@Mock
private ActivityStarter mActivityStarter;
@@ -105,6 +109,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
when(mControlsComponent.getControlsListingController()).thenReturn(
Optional.of(mControlsListingController));
when(mControlsComponent.getVisibility()).thenReturn(AVAILABLE);
+ when(mView.findViewById(R.id.home_controls_chip)).thenReturn(mHomeControlsView);
}
@Test
@@ -206,9 +211,9 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
ArgumentCaptor.forClass(View.OnClickListener.class);
- verify(mView).setOnClickListener(clickListenerCaptor.capture());
+ verify(mHomeControlsView).setOnClickListener(clickListenerCaptor.capture());
- clickListenerCaptor.getValue().onClick(mView);
+ clickListenerCaptor.getValue().onClick(mHomeControlsView);
verify(mUiEventLogger).log(
DreamHomeControlsComplication.DreamHomeControlsChipViewController
.DreamOverlayEvent.DREAM_HOME_CONTROLS_TAPPED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
index cedde58746d2..cef452b8ec22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
@@ -20,6 +20,7 @@ package com.android.systemui.keyguard
import android.content.ContentValues
import android.content.pm.PackageManager
import android.content.pm.ProviderInfo
+import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SystemUIAppComponentFactoryBase
@@ -27,8 +28,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -36,8 +39,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceIn
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
@@ -74,8 +77,8 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
underTest = KeyguardQuickAffordanceProvider()
val scope = CoroutineScope(IMMEDIATE)
- val selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ val localUserSelectionManager =
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock<UserFileManager>().apply {
@@ -89,12 +92,22 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ )
+ val remoteUserSelectionManager =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = scope,
+ userTracker = userTracker,
+ clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+ userHandle = UserHandle.SYSTEM,
)
val quickAffordanceRepository =
KeyguardQuickAffordanceRepository(
appContext = context,
scope = scope,
- selectionManager = selectionManager,
+ localUserSelectionManager = localUserSelectionManager,
+ remoteUserSelectionManager = remoteUserSelectionManager,
+ userTracker = userTracker,
configs =
setOf(
FakeKeyguardQuickAffordanceConfig(
@@ -113,9 +126,10 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
scope = scope,
backgroundDispatcher = IMMEDIATE,
secureSettings = FakeSettings(),
- selectionsManager = selectionManager,
+ selectionsManager = localUserSelectionManager,
),
dumpManager = mock(),
+ userHandle = UserHandle.SYSTEM,
)
underTest.interactor =
KeyguardQuickAffordanceInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index 623becf166d3..7205f3068abb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -37,25 +37,29 @@ class CameraQuickAffordanceConfigTest : SysuiTestCase() {
@Mock private lateinit var cameraGestureHelper: CameraGestureHelper
@Mock private lateinit var context: Context
+
private lateinit var underTest: CameraQuickAffordanceConfig
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- underTest = CameraQuickAffordanceConfig(
+
+ underTest =
+ CameraQuickAffordanceConfig(
context,
- cameraGestureHelper,
- )
+ ) {
+ cameraGestureHelper
+ }
}
@Test
fun `affordance triggered -- camera launch called`() {
- //when
+ // When
val result = underTest.onTriggered(null)
- //then
+ // Then
verify(cameraGestureHelper)
- .launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
+ .launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
new file mode 100644
index 000000000000..9fa7db127e1f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
@@ -0,0 +1,215 @@
+/*
+ * 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.keyguard.data.quickaffordance
+
+import android.content.Context
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.statusbar.policy.FlashlightController
+import com.android.systemui.utils.leaks.FakeFlashlightController
+import com.android.systemui.utils.leaks.LeakCheckedTest
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {
+
+ @Mock private lateinit var context: Context
+ private lateinit var flashlightController: FakeFlashlightController
+ private lateinit var underTest: FlashlightQuickAffordanceConfig
+
+ @Before
+ fun setUp() {
+ injectLeakCheckedDependency(FlashlightController::class.java)
+ MockitoAnnotations.initMocks(this)
+
+ flashlightController =
+ SysuiLeakCheck().getLeakChecker(FlashlightController::class.java)
+ as FakeFlashlightController
+ underTest = FlashlightQuickAffordanceConfig(context, flashlightController)
+ }
+
+ @Test
+ fun `flashlight is off -- triggered -- icon is on and active`() = runTest {
+ // given
+ flashlightController.isEnabled = false
+ flashlightController.isAvailable = true
+ val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
+
+ // when
+ underTest.onTriggered(null)
+ val lastValue = values.last()
+
+ // then
+ assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
+ assertEquals(
+ R.drawable.qs_flashlight_icon_on,
+ ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
+ as? Icon.Resource)
+ ?.res
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `flashlight is on -- triggered -- icon is off and inactive`() = runTest {
+ // given
+ flashlightController.isEnabled = true
+ flashlightController.isAvailable = true
+ val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
+
+ // when
+ underTest.onTriggered(null)
+ val lastValue = values.last()
+
+ // then
+ assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
+ assertEquals(
+ R.drawable.qs_flashlight_icon_off,
+ ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
+ as? Icon.Resource)
+ ?.res
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `flashlight is on -- receives error -- icon is off and inactive`() = runTest {
+ // given
+ flashlightController.isEnabled = true
+ flashlightController.isAvailable = false
+ val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
+
+ // when
+ flashlightController.onFlashlightError()
+ val lastValue = values.last()
+
+ // then
+ assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
+ assertEquals(
+ R.drawable.qs_flashlight_icon_off,
+ ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
+ as? Icon.Resource)
+ ?.res
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `flashlight availability now off -- hidden`() = runTest {
+ // given
+ flashlightController.isEnabled = true
+ flashlightController.isAvailable = false
+ val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
+
+ // when
+ flashlightController.onFlashlightAvailabilityChanged(false)
+ val lastValue = values.last()
+
+ // then
+ assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ job.cancel()
+ }
+
+ @Test
+ fun `flashlight availability now on -- flashlight on -- inactive and icon off`() = runTest {
+ // given
+ flashlightController.isEnabled = true
+ flashlightController.isAvailable = false
+ val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
+
+ // when
+ flashlightController.onFlashlightAvailabilityChanged(true)
+ val lastValue = values.last()
+
+ // then
+ assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
+ assertTrue(
+ (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState
+ is ActivationState.Active
+ )
+ assertEquals(R.drawable.qs_flashlight_icon_on, (lastValue.icon as? Icon.Resource)?.res)
+ job.cancel()
+ }
+
+ @Test
+ fun `flashlight availability now on -- flashlight off -- inactive and icon off`() = runTest {
+ // given
+ flashlightController.isEnabled = false
+ flashlightController.isAvailable = false
+ val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
+
+ // when
+ flashlightController.onFlashlightAvailabilityChanged(true)
+ val lastValue = values.last()
+
+ // then
+ assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
+ assertTrue(
+ (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState
+ is ActivationState.Inactive
+ )
+ assertEquals(R.drawable.qs_flashlight_icon_off, (lastValue.icon as? Icon.Resource)?.res)
+ job.cancel()
+ }
+
+ @Test
+ fun `flashlight available -- picker state default`() = runTest {
+ // given
+ flashlightController.isAvailable = true
+
+ // when
+ val result = underTest.getPickerScreenState()
+
+ // then
+ assertTrue(result is KeyguardQuickAffordanceConfig.PickerScreenState.Default)
+ }
+
+ @Test
+ fun `flashlight not available -- picker state unavailable`() = runTest {
+ // given
+ flashlightController.isAvailable = false
+
+ // when
+ val result = underTest.getPickerScreenState()
+
+ // then
+ assertTrue(result is KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 8ef921eaa50a..3b0169d77063 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -57,7 +57,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
private lateinit var testScope: TestScope
private lateinit var testDispatcher: TestDispatcher
- private lateinit var selectionManager: KeyguardQuickAffordanceSelectionManager
+ private lateinit var selectionManager: KeyguardQuickAffordanceLocalUserSelectionManager
private lateinit var settings: FakeSettings
@Before
@@ -75,7 +75,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
testDispatcher = UnconfinedTestDispatcher()
testScope = TestScope(testDispatcher)
selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock {
@@ -89,6 +89,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = FakeUserTracker(),
+ broadcastDispatcher = fakeBroadcastDispatcher,
)
settings = FakeSettings()
settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index d8ee9f113d33..67091a9f40c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.quickaffordance
+import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.UserInfo
import androidx.test.filters.SmallTest
@@ -27,10 +28,15 @@ import com.android.systemui.settings.UserFileManager
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,15 +44,19 @@ import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
-class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
+class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() {
@Mock private lateinit var userFileManager: UserFileManager
- private lateinit var underTest: KeyguardQuickAffordanceSelectionManager
+ private lateinit var underTest: KeyguardQuickAffordanceLocalUserSelectionManager
private lateinit var userTracker: FakeUserTracker
private lateinit var sharedPrefs: MutableMap<Int, SharedPreferences>
@@ -60,15 +70,23 @@ class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
sharedPrefs.getOrPut(userId) { FakeSharedPreferences() }
}
userTracker = FakeUserTracker()
+ val dispatcher = UnconfinedTestDispatcher()
+ Dispatchers.setMain(dispatcher)
underTest =
- KeyguardQuickAffordanceSelectionManager(
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager = userFileManager,
userTracker = userTracker,
+ broadcastDispatcher = fakeBroadcastDispatcher,
)
}
+ @After
+ fun tearDown() {
+ Dispatchers.resetMain()
+ }
+
@Test
fun setSelections() = runTest {
overrideResource(R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>())
@@ -318,6 +336,22 @@ class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun `responds to backup and restore by reloading the selections from disk`() = runTest {
+ overrideResource(R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>())
+ val affordanceIdsBySlotId = mutableListOf<Map<String, List<String>>>()
+ val job =
+ launch(UnconfinedTestDispatcher()) {
+ underTest.selections.toList(affordanceIdsBySlotId)
+ }
+ clearInvocations(userFileManager)
+
+ fakeBroadcastDispatcher.registeredReceivers.firstOrNull()?.onReceive(context, Intent())
+
+ verify(userFileManager, atLeastOnce()).getSharedPreferences(anyString(), anyInt(), anyInt())
+ job.cancel()
+ }
+
private fun assertSelections(
observed: Map<String, List<String>>?,
expected: Map<String, List<String>>,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
new file mode 100644
index 000000000000..d7e9cf144f88
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
@@ -0,0 +1,219 @@
+/*
+ * 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.keyguard.data.quickaffordance
+
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+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.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
+
+ @Mock private lateinit var userHandle: UserHandle
+
+ private lateinit var underTest: KeyguardQuickAffordanceRemoteUserSelectionManager
+
+ private lateinit var clientFactory: FakeKeyguardQuickAffordanceProviderClientFactory
+ private lateinit var testScope: TestScope
+ private lateinit var testDispatcher: TestDispatcher
+ private lateinit var userTracker: FakeUserTracker
+ private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
+ private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(userHandle.identifier).thenReturn(UserHandle.USER_SYSTEM)
+ whenever(userHandle.isSystem).thenReturn(true)
+ client1 = FakeKeyguardQuickAffordanceProviderClient()
+ client2 = FakeKeyguardQuickAffordanceProviderClient()
+
+ userTracker = FakeUserTracker()
+ userTracker.set(
+ userInfos =
+ listOf(
+ UserInfo(
+ UserHandle.USER_SYSTEM,
+ "Primary",
+ /* flags= */ 0,
+ ),
+ UserInfo(
+ OTHER_USER_ID_1,
+ "Secondary 1",
+ /* flags= */ 0,
+ ),
+ UserInfo(
+ OTHER_USER_ID_2,
+ "Secondary 2",
+ /* flags= */ 0,
+ ),
+ ),
+ selectedUserIndex = 0,
+ )
+
+ clientFactory =
+ FakeKeyguardQuickAffordanceProviderClientFactory(
+ userTracker,
+ ) { selectedUserId ->
+ when (selectedUserId) {
+ OTHER_USER_ID_1 -> client1
+ OTHER_USER_ID_2 -> client2
+ else -> error("No client set-up for user $selectedUserId!")
+ }
+ }
+
+ testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
+
+ underTest =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = testScope.backgroundScope,
+ userTracker = userTracker,
+ clientFactory = clientFactory,
+ userHandle = userHandle,
+ )
+ }
+
+ @Test
+ fun `selections - primary user process`() =
+ testScope.runTest {
+ val values = mutableListOf<Map<String, List<String>>>()
+ val job = launch { underTest.selections.toList(values) }
+
+ runCurrent()
+ assertThat(values.last()).isEmpty()
+
+ client1.insertSelection(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ )
+ client2.insertSelection(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
+ affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+ )
+
+ userTracker.set(
+ userInfos = userTracker.userProfiles,
+ selectedUserIndex = 1,
+ )
+ runCurrent()
+ assertThat(values.last())
+ .isEqualTo(
+ mapOf(
+ KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
+ listOf(
+ FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ ),
+ )
+ )
+
+ userTracker.set(
+ userInfos = userTracker.userProfiles,
+ selectedUserIndex = 2,
+ )
+ runCurrent()
+ assertThat(values.last())
+ .isEqualTo(
+ mapOf(
+ KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
+ listOf(
+ FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+ ),
+ )
+ )
+
+ job.cancel()
+ }
+
+ @Test
+ fun `selections - secondary user process - always empty`() =
+ testScope.runTest {
+ whenever(userHandle.isSystem).thenReturn(false)
+ val values = mutableListOf<Map<String, List<String>>>()
+ val job = launch { underTest.selections.toList(values) }
+
+ runCurrent()
+ assertThat(values.last()).isEmpty()
+
+ client1.insertSelection(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ )
+ userTracker.set(
+ userInfos = userTracker.userProfiles,
+ selectedUserIndex = 1,
+ )
+ runCurrent()
+ assertThat(values.last()).isEmpty()
+
+ job.cancel()
+ }
+
+ @Test
+ fun setSelections() =
+ testScope.runTest {
+ userTracker.set(
+ userInfos = userTracker.userProfiles,
+ selectedUserIndex = 1,
+ )
+ runCurrent()
+
+ underTest.setSelections(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceIds = listOf(FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1),
+ )
+ runCurrent()
+
+ assertThat(underTest.getSelections())
+ .isEqualTo(
+ mapOf(
+ KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
+ listOf(
+ FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ ),
+ )
+ )
+ }
+
+ companion object {
+ private const val OTHER_USER_ID_1 = UserHandle.MIN_SECONDARY_USER_ID + 1
+ private const val OTHER_USER_ID_2 = UserHandle.MIN_SECONDARY_USER_ID + 2
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 5c75417c3473..c40488adf029 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -17,17 +17,23 @@
package com.android.systemui.keyguard.data.repository
+import android.content.pm.UserInfo
+import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -39,6 +45,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,14 +62,24 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
private lateinit var config1: FakeKeyguardQuickAffordanceConfig
private lateinit var config2: FakeKeyguardQuickAffordanceConfig
+ private lateinit var userTracker: FakeUserTracker
+ private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
+ private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
@Before
fun setUp() {
- config1 = FakeKeyguardQuickAffordanceConfig("built_in:1")
- config2 = FakeKeyguardQuickAffordanceConfig("built_in:2")
+ config1 =
+ FakeKeyguardQuickAffordanceConfig(
+ FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1
+ )
+ config2 =
+ FakeKeyguardQuickAffordanceConfig(
+ FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2
+ )
val scope = CoroutineScope(IMMEDIATE)
- val selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ userTracker = FakeUserTracker()
+ val localUserSelectionManager =
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock<UserFileManager>().apply {
@@ -75,23 +92,45 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
)
.thenReturn(FakeSharedPreferences())
},
- userTracker = FakeUserTracker(),
+ userTracker = userTracker,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ )
+ client1 = FakeKeyguardQuickAffordanceProviderClient()
+ client2 = FakeKeyguardQuickAffordanceProviderClient()
+ val remoteUserSelectionManager =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = scope,
+ userTracker = userTracker,
+ clientFactory =
+ FakeKeyguardQuickAffordanceProviderClientFactory(
+ userTracker,
+ ) { selectedUserId ->
+ when (selectedUserId) {
+ SECONDARY_USER_1 -> client1
+ SECONDARY_USER_2 -> client2
+ else -> error("No set-up client for user $selectedUserId!")
+ }
+ },
+ userHandle = UserHandle.SYSTEM,
)
underTest =
KeyguardQuickAffordanceRepository(
appContext = context,
scope = scope,
- selectionManager = selectionManager,
+ localUserSelectionManager = localUserSelectionManager,
+ remoteUserSelectionManager = remoteUserSelectionManager,
+ userTracker = userTracker,
legacySettingSyncer =
KeyguardQuickAffordanceLegacySettingSyncer(
scope = scope,
backgroundDispatcher = IMMEDIATE,
secureSettings = FakeSettings(),
- selectionsManager = selectionManager,
+ selectionsManager = localUserSelectionManager,
),
configs = setOf(config1, config2),
dumpManager = mock(),
+ userHandle = UserHandle.SYSTEM,
)
}
@@ -186,7 +225,53 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
)
}
- private suspend fun assertSelections(
+ @Test
+ fun `selections for secondary user`() =
+ runBlocking(IMMEDIATE) {
+ userTracker.set(
+ userInfos =
+ listOf(
+ UserInfo(
+ UserHandle.USER_SYSTEM,
+ "Primary",
+ /* flags= */ 0,
+ ),
+ UserInfo(
+ SECONDARY_USER_1,
+ "Secondary 1",
+ /* flags= */ 0,
+ ),
+ UserInfo(
+ SECONDARY_USER_2,
+ "Secondary 2",
+ /* flags= */ 0,
+ ),
+ ),
+ selectedUserIndex = 2,
+ )
+ client2.insertSelection(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+ )
+ val observed = mutableListOf<Map<String, List<KeyguardQuickAffordanceConfig>>>()
+ val job = underTest.selections.onEach { observed.add(it) }.launchIn(this)
+ yield()
+
+ assertSelections(
+ observed = observed.last(),
+ expected =
+ mapOf(
+ KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
+ listOf(
+ config2,
+ ),
+ )
+ )
+
+ job.cancel()
+ }
+
+ private fun assertSelections(
observed: Map<String, List<KeyguardQuickAffordanceConfig>>?,
expected: Map<String, List<KeyguardQuickAffordanceConfig>>,
) {
@@ -200,5 +285,7 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
+ private const val SECONDARY_USER_1 = UserHandle.MIN_SECONDARY_USER_ID + 1
+ private const val SECONDARY_USER_2 = UserHandle.MIN_SECONDARY_USER_ID + 2
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 13fc9fcf834b..5deac1924ab7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -16,10 +16,13 @@
package com.android.systemui.keyguard.data.repository
+import android.graphics.Point
+import android.hardware.biometrics.BiometricSourceType
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost
import com.android.systemui.doze.DozeMachine
@@ -27,9 +30,11 @@ import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -57,9 +62,10 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
+ @Mock private lateinit var authController: AuthController
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private lateinit var underTest: KeyguardRepositoryImpl
@@ -76,6 +82,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
keyguardStateController,
keyguardUpdateMonitor,
dozeTransitionListener,
+ authController,
)
}
@@ -198,7 +205,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
fun dozeAmount() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
- val job = underTest.dozeAmount.onEach(values::add).launchIn(this)
+ val job = underTest.linearDozeAmount.onEach(values::add).launchIn(this)
val captor = argumentCaptor<StatusBarStateController.StateListener>()
verify(statusBarStateController).addCallback(captor.capture())
@@ -207,7 +214,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
captor.value.onDozeAmountChanged(0.498f, 0.5f)
captor.value.onDozeAmountChanged(0.661f, 0.65f)
- assertThat(values).isEqualTo(listOf(0f, 0.4f, 0.5f, 0.65f))
+ assertThat(values).isEqualTo(listOf(0f, 0.433f, 0.498f, 0.661f))
job.cancel()
verify(statusBarStateController).removeCallback(captor.value)
@@ -217,25 +224,36 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
fun wakefulness() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<WakefulnessModel>()
- val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
+ val job = underTest.wakefulness.onEach(values::add).launchIn(this)
val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
verify(wakefulnessLifecycle).addObserver(captor.capture())
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_WAKING)
captor.value.onStartedWakingUp()
+
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE)
captor.value.onFinishedWakingUp()
+
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP)
captor.value.onStartedGoingToSleep()
+
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
captor.value.onFinishedGoingToSleep()
- assertThat(values)
+ assertThat(values.map { it.state })
.isEqualTo(
listOf(
// Initial value will be ASLEEP
- WakefulnessModel.ASLEEP,
- WakefulnessModel.STARTING_TO_WAKE,
- WakefulnessModel.AWAKE,
- WakefulnessModel.STARTING_TO_SLEEP,
- WakefulnessModel.ASLEEP,
+ WakefulnessState.ASLEEP,
+ WakefulnessState.STARTING_TO_WAKE,
+ WakefulnessState.AWAKE,
+ WakefulnessState.STARTING_TO_SLEEP,
+ WakefulnessState.ASLEEP,
)
)
@@ -329,14 +347,20 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
verify(biometricUnlockController).addBiometricModeListener(captor.capture())
- captor.value.onModeChanged(BiometricUnlockController.MODE_NONE)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
- captor.value.onModeChanged(BiometricUnlockController.MODE_SHOW_BOUNCER)
- captor.value.onModeChanged(BiometricUnlockController.MODE_ONLY_WAKE)
- captor.value.onModeChanged(BiometricUnlockController.MODE_UNLOCK_COLLAPSING)
- captor.value.onModeChanged(BiometricUnlockController.MODE_DISMISS_BOUNCER)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+ listOf(
+ BiometricUnlockController.MODE_NONE,
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK,
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockController.MODE_SHOW_BOUNCER,
+ BiometricUnlockController.MODE_ONLY_WAKE,
+ BiometricUnlockController.MODE_UNLOCK_COLLAPSING,
+ BiometricUnlockController.MODE_DISMISS_BOUNCER,
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM,
+ )
+ .forEach {
+ whenever(biometricUnlockController.mode).thenReturn(it)
+ captor.value.onModeChanged(it)
+ }
assertThat(values)
.isEqualTo(
@@ -420,4 +444,104 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
job.cancel()
verify(dozeTransitionListener).removeCallback(listener)
}
+
+ @Test
+ fun fingerprintSensorLocation() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Point?>()
+ val job = underTest.fingerprintSensorLocation.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<AuthController.Callback>()
+ verify(authController).addCallback(captor.capture())
+
+ // An initial, null value should be initially emitted so that flows combined with this
+ // one
+ // emit values immediately. The sensor location is expected to be nullable, so anyone
+ // consuming it should handle that properly.
+ assertThat(values).isEqualTo(listOf(null))
+
+ listOf(Point(500, 500), Point(0, 0), null, Point(250, 250))
+ .onEach {
+ whenever(authController.fingerprintSensorLocation).thenReturn(it)
+ captor.value.onFingerprintLocationChanged()
+ }
+ .also { dispatchedSensorLocations ->
+ assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
+ }
+
+ job.cancel()
+ }
+
+ @Test
+ fun faceSensorLocation() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Point?>()
+ val job = underTest.faceSensorLocation.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<AuthController.Callback>()
+ verify(authController).addCallback(captor.capture())
+
+ // An initial, null value should be initially emitted so that flows combined with this
+ // one
+ // emit values immediately. The sensor location is expected to be nullable, so anyone
+ // consuming it should handle that properly.
+ assertThat(values).isEqualTo(listOf(null))
+
+ listOf(
+ Point(500, 500),
+ Point(0, 0),
+ null,
+ Point(250, 250),
+ )
+ .onEach {
+ whenever(authController.faceSensorLocation).thenReturn(it)
+ captor.value.onFaceSensorLocationChanged()
+ }
+ .also { dispatchedSensorLocations ->
+ assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
+ }
+
+ job.cancel()
+ }
+
+ @Test
+ fun biometricUnlockSource() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<BiometricUnlockSource?>()
+ val job = underTest.biometricUnlockSource.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+
+ // An initial, null value should be initially emitted so that flows combined with this
+ // one
+ // emit values immediately. The biometric unlock source is expected to be nullable, so
+ // anyone consuming it should handle that properly.
+ assertThat(values).isEqualTo(listOf(null))
+
+ listOf(
+ BiometricSourceType.FINGERPRINT,
+ BiometricSourceType.IRIS,
+ null,
+ BiometricSourceType.FACE,
+ BiometricSourceType.FINGERPRINT,
+ )
+ .onEach { biometricSourceType ->
+ captor.value.onBiometricAuthenticated(0, biometricSourceType, false)
+ }
+
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ null,
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
+ BiometricUnlockSource.FACE_SENSOR,
+ null,
+ BiometricUnlockSource.FACE_SENSOR,
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
+ )
+ )
+
+ job.cancel()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
new file mode 100644
index 000000000000..d2db910ad443
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.graphics.Point
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LightRevealEffect
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import kotlinx.coroutines.launch
+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.junit.runners.JUnit4
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class LightRevealScrimRepositoryTest : SysuiTestCase() {
+ private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
+ private lateinit var underTest: LightRevealScrimRepositoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ fakeKeyguardRepository = FakeKeyguardRepository()
+ underTest = LightRevealScrimRepositoryImpl(fakeKeyguardRepository, context)
+ }
+
+ @Test
+ fun `nextRevealEffect - effect switches between default and biometric with no dupes`() =
+ runTest {
+ val values = mutableListOf<LightRevealEffect>()
+ val job = launch { underTest.revealEffect.collect { values.add(it) } }
+
+ // We should initially emit the default reveal effect.
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT })
+
+ // The source and sensor locations are still null, so we should still be using the
+ // default reveal despite a biometric unlock.
+ fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+ // We got a source but still have no sensor locations, so should be sticking with
+ // the default effect.
+ fakeKeyguardRepository.setBiometricUnlockSource(
+ BiometricUnlockSource.FINGERPRINT_SENSOR
+ )
+
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+ // We got a location for the face sensor, but we unlocked with fingerprint.
+ val faceLocation = Point(250, 0)
+ fakeKeyguardRepository.setFaceSensorLocation(faceLocation)
+
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+ // Now we have fingerprint sensor locations, and wake and unlock via fingerprint.
+ val fingerprintLocation = Point(500, 500)
+ fakeKeyguardRepository.setFingerprintSensorLocation(fingerprintLocation)
+ fakeKeyguardRepository.setBiometricUnlockSource(
+ BiometricUnlockSource.FINGERPRINT_SENSOR
+ )
+ fakeKeyguardRepository.setBiometricUnlockState(
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+ )
+
+ // We should now have switched to the circle reveal, at the fingerprint location.
+ runCurrent()
+ values.assertEffectsMatchPredicates(
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == fingerprintLocation.x &&
+ it.centerY == fingerprintLocation.y
+ },
+ )
+
+ // Subsequent wake and unlocks should not emit duplicate, identical CircleReveals.
+ val valuesPrevSize = values.size
+ fakeKeyguardRepository.setBiometricUnlockState(
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+ )
+ fakeKeyguardRepository.setBiometricUnlockState(
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+ )
+ assertEquals(valuesPrevSize, values.size)
+
+ // Non-biometric unlock, we should return to the default reveal.
+ fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.NONE)
+
+ runCurrent()
+ values.assertEffectsMatchPredicates(
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == fingerprintLocation.x &&
+ it.centerY == fingerprintLocation.y
+ },
+ { it == DEFAULT_REVEAL_EFFECT },
+ )
+
+ // We already have a face location, so switching to face source should update the
+ // CircleReveal.
+ fakeKeyguardRepository.setBiometricUnlockSource(BiometricUnlockSource.FACE_SENSOR)
+ runCurrent()
+ fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+ runCurrent()
+
+ values.assertEffectsMatchPredicates(
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == fingerprintLocation.x &&
+ it.centerY == fingerprintLocation.y
+ },
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == faceLocation.x &&
+ it.centerY == faceLocation.y
+ },
+ )
+
+ job.cancel()
+ }
+
+ /**
+ * Asserts that the list of LightRevealEffects satisfies the list of predicates, in order, with
+ * no leftover elements.
+ */
+ private fun List<LightRevealEffect>.assertEffectsMatchPredicates(
+ vararg predicates: (LightRevealEffect) -> Boolean
+ ) {
+ println(this)
+ assertEquals(predicates.size, this.size)
+
+ assertFalse(
+ zip(predicates) { effect, predicate -> predicate(effect) }.any { matched -> !matched }
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index c2650ec455d8..1c1f0399bb06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.content.Intent
+import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
@@ -29,9 +30,11 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
@@ -237,8 +240,8 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
val qrCodeScanner =
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
val scope = CoroutineScope(IMMEDIATE)
- val selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ val localUserSelectionManager =
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock<UserFileManager>().apply {
@@ -252,21 +255,32 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ )
+ val remoteUserSelectionManager =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = scope,
+ userTracker = userTracker,
+ clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+ userHandle = UserHandle.SYSTEM,
)
val quickAffordanceRepository =
KeyguardQuickAffordanceRepository(
appContext = context,
scope = scope,
- selectionManager = selectionManager,
+ localUserSelectionManager = localUserSelectionManager,
+ remoteUserSelectionManager = remoteUserSelectionManager,
+ userTracker = userTracker,
legacySettingSyncer =
KeyguardQuickAffordanceLegacySettingSyncer(
scope = scope,
backgroundDispatcher = IMMEDIATE,
secureSettings = FakeSettings(),
- selectionsManager = selectionManager,
+ selectionsManager = localUserSelectionManager,
),
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
dumpManager = mock(),
+ userHandle = UserHandle.SYSTEM,
)
underTest =
KeyguardQuickAffordanceInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index b79030602368..11fe905b1d1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
+import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
@@ -26,9 +27,11 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
@@ -98,8 +101,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
val scope = CoroutineScope(IMMEDIATE)
- val selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ val localUserSelectionManager =
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock<UserFileManager>().apply {
@@ -113,21 +116,32 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ )
+ val remoteUserSelectionManager =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = scope,
+ userTracker = userTracker,
+ clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+ userHandle = UserHandle.SYSTEM,
)
val quickAffordanceRepository =
KeyguardQuickAffordanceRepository(
appContext = context,
scope = scope,
- selectionManager = selectionManager,
+ localUserSelectionManager = localUserSelectionManager,
+ remoteUserSelectionManager = remoteUserSelectionManager,
+ userTracker = userTracker,
legacySettingSyncer =
KeyguardQuickAffordanceLegacySettingSyncer(
scope = scope,
backgroundDispatcher = IMMEDIATE,
secureSettings = FakeSettings(),
- selectionsManager = selectionManager,
+ selectionsManager = localUserSelectionManager,
),
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
dumpManager = mock(),
+ userHandle = UserHandle.SYSTEM,
)
featureFlags =
FakeFeatureFlags().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
new file mode 100644
index 000000000000..31662145dfbe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
+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.statusbar.LightRevealEffect
+import com.android.systemui.statusbar.LightRevealScrim
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class LightRevealScrimInteractorTest : SysuiTestCase() {
+ private val fakeKeyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ private val fakeLightRevealScrimRepository = FakeLightRevealScrimRepository()
+
+ private val keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(fakeKeyguardTransitionRepository)
+
+ private lateinit var underTest: LightRevealScrimInteractor
+
+ private val reveal1 =
+ object : LightRevealEffect {
+ override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {}
+ }
+
+ private val reveal2 =
+ object : LightRevealEffect {
+ override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {}
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ LightRevealScrimInteractor(
+ fakeKeyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ fakeLightRevealScrimRepository
+ )
+ }
+
+ @Test
+ fun `lightRevealEffect - does not change during keyguard transition`() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<LightRevealEffect>()
+ val job = underTest.lightRevealEffect.onEach(values::add).launchIn(this)
+
+ fakeLightRevealScrimRepository.setRevealEffect(reveal1)
+
+ // The reveal effect shouldn't emit anything until a keyguard transition starts.
+ assertEquals(values.size, 0)
+
+ // Once it starts, it should emit reveal1.
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.STARTED)
+ )
+ assertEquals(values, listOf(reveal1))
+
+ // Until the next transition starts, reveal2 should not be emitted.
+ fakeLightRevealScrimRepository.setRevealEffect(reveal2)
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.RUNNING)
+ )
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.FINISHED)
+ )
+ assertEquals(values, listOf(reveal1))
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.STARTED)
+ )
+ assertEquals(values, listOf(reveal1, reveal2))
+
+ job.cancel()
+ }
+
+ @Test
+ fun `revealAmount - inverted when appropriate`() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+ val job = underTest.revealAmount.onEach(values::add).launchIn(this)
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 0.3f
+ )
+ )
+
+ assertEquals(values, listOf(0.3f))
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 0.3f
+ )
+ )
+
+ assertEquals(values, listOf(0.3f, 0.7f))
+
+ job.cancel()
+ }
+
+ @Test
+ fun `revealAmount - ignores transitions that do not affect reveal amount`() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+ val job = underTest.revealAmount.onEach(values::add).launchIn(this)
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from = KeyguardState.DOZING, to = KeyguardState.AOD, value = 0.3f)
+ )
+
+ assertEquals(values, emptyList<Float>())
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from = KeyguardState.AOD, to = KeyguardState.DOZING, value = 0.3f)
+ )
+
+ assertEquals(values, emptyList<Float>())
+
+ job.cancel()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 8b166bd89426..83a5d0e90c84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.Intent
+import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
@@ -27,9 +28,11 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
@@ -121,8 +124,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
val scope = CoroutineScope(IMMEDIATE)
- val selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ val localUserSelectionManager =
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock<UserFileManager>().apply {
@@ -136,18 +139,28 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ )
+ val remoteUserSelectionManager =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = scope,
+ userTracker = userTracker,
+ clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+ userHandle = UserHandle.SYSTEM,
)
val quickAffordanceRepository =
KeyguardQuickAffordanceRepository(
appContext = context,
scope = scope,
- selectionManager = selectionManager,
+ localUserSelectionManager = localUserSelectionManager,
+ remoteUserSelectionManager = remoteUserSelectionManager,
+ userTracker = userTracker,
legacySettingSyncer =
KeyguardQuickAffordanceLegacySettingSyncer(
scope = scope,
backgroundDispatcher = IMMEDIATE,
secureSettings = FakeSettings(),
- selectionsManager = selectionManager,
+ selectionsManager = localUserSelectionManager,
),
configs =
setOf(
@@ -156,6 +169,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
qrCodeScannerAffordanceConfig,
),
dumpManager = mock(),
+ userHandle = UserHandle.SYSTEM,
)
underTest =
KeyguardBottomAreaViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
new file mode 100644
index 000000000000..432764a7de8f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.table
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SmallTest
+class TableChangeTest : SysuiTestCase() {
+
+ @Test
+ fun setString_isString() {
+ val underTest = TableChange()
+
+ underTest.reset(timestamp = 100, columnPrefix = "", columnName = "fakeName")
+ underTest.set("fakeValue")
+
+ assertThat(underTest.hasData()).isTrue()
+ assertThat(underTest.getVal()).isEqualTo("fakeValue")
+ }
+
+ @Test
+ fun setBoolean_isBoolean() {
+ val underTest = TableChange()
+
+ underTest.reset(timestamp = 100, columnPrefix = "", columnName = "fakeName")
+ underTest.set(true)
+
+ assertThat(underTest.hasData()).isTrue()
+ assertThat(underTest.getVal()).isEqualTo("true")
+ }
+
+ @Test
+ fun setInt_isInt() {
+ val underTest = TableChange()
+
+ underTest.reset(timestamp = 100, columnPrefix = "", columnName = "fakeName")
+ underTest.set(8900)
+
+ assertThat(underTest.hasData()).isTrue()
+ assertThat(underTest.getVal()).isEqualTo("8900")
+ }
+
+ @Test
+ fun setThenReset_isEmpty() {
+ val underTest = TableChange()
+
+ underTest.reset(timestamp = 100, columnPrefix = "", columnName = "fakeName")
+ underTest.set(8900)
+ underTest.reset(timestamp = 0, columnPrefix = "prefix", columnName = "name")
+
+ assertThat(underTest.hasData()).isFalse()
+ assertThat(underTest.getVal()).isEqualTo("null")
+ }
+
+ @Test
+ fun getName_hasPrefix() {
+ val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+
+ assertThat(underTest.getName()).contains("fakePrefix")
+ assertThat(underTest.getName()).contains("fakeName")
+ }
+
+ @Test
+ fun getName_noPrefix() {
+ val underTest = TableChange(columnPrefix = "", columnName = "fakeName")
+
+ assertThat(underTest.getName()).contains("fakeName")
+ }
+
+ @Test
+ fun resetThenSet_hasNewValue() {
+ val underTest = TableChange()
+
+ underTest.reset(timestamp = 100, columnPrefix = "prefix", columnName = "original")
+ underTest.set("fakeValue")
+ underTest.reset(timestamp = 0, columnPrefix = "", columnName = "updated")
+ underTest.set(8900)
+
+ assertThat(underTest.hasData()).isTrue()
+ assertThat(underTest.getName()).contains("updated")
+ assertThat(underTest.getName()).doesNotContain("prefix")
+ assertThat(underTest.getName()).doesNotContain("original")
+ assertThat(underTest.getVal()).isEqualTo("8900")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
new file mode 100644
index 000000000000..2c8d7abd4f4a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.table
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class TableLogBufferTest : SysuiTestCase() {
+ private lateinit var underTest: TableLogBuffer
+
+ private lateinit var systemClock: FakeSystemClock
+ private lateinit var outputWriter: StringWriter
+
+ @Before
+ fun setup() {
+ systemClock = FakeSystemClock()
+ outputWriter = StringWriter()
+
+ underTest = TableLogBuffer(MAX_SIZE, NAME, systemClock)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun maxSizeZero_throwsException() {
+ TableLogBuffer(maxSize = 0, "name", systemClock)
+ }
+
+ @Test
+ fun dumpChanges_hasHeader() {
+ val dumpedString = dumpChanges()
+
+ assertThat(logLines(dumpedString)[0]).isEqualTo(HEADER_PREFIX + NAME)
+ }
+
+ @Test
+ fun dumpChanges_hasVersion() {
+ val dumpedString = dumpChanges()
+
+ assertThat(logLines(dumpedString)[1]).isEqualTo("version $VERSION")
+ }
+
+ @Test
+ fun dumpChanges_hasFooter() {
+ val dumpedString = dumpChanges()
+
+ assertThat(logLines(dumpedString).last()).isEqualTo(FOOTER_PREFIX + NAME)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_str_separatorNotAllowedInPrefix() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("columnName", "stringValue")
+ }
+ }
+ underTest.logDiffs("some${SEPARATOR}thing", TestDiffable(), next)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_bool_separatorNotAllowedInPrefix() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("columnName", true)
+ }
+ }
+ underTest.logDiffs("some${SEPARATOR}thing", TestDiffable(), next)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_int_separatorNotAllowedInPrefix() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("columnName", 567)
+ }
+ }
+ underTest.logDiffs("some${SEPARATOR}thing", TestDiffable(), next)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_str_separatorNotAllowedInColumnName() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("column${SEPARATOR}Name", "stringValue")
+ }
+ }
+ underTest.logDiffs("prefix", TestDiffable(), next)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_bool_separatorNotAllowedInColumnName() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("column${SEPARATOR}Name", true)
+ }
+ }
+ underTest.logDiffs("prefix", TestDiffable(), next)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_int_separatorNotAllowedInColumnName() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("column${SEPARATOR}Name", 456)
+ }
+ }
+ underTest.logDiffs("prefix", TestDiffable(), next)
+ }
+
+ @Test
+ fun logChange_bool_dumpsCorrectly() {
+ systemClock.setCurrentTimeMillis(4000L)
+
+ underTest.logChange("prefix", "columnName", true)
+
+ val dumpedString = dumpChanges()
+ val expected =
+ TABLE_LOG_DATE_FORMAT.format(4000L) +
+ SEPARATOR +
+ "prefix.columnName" +
+ SEPARATOR +
+ "true"
+ assertThat(dumpedString).contains(expected)
+ }
+
+ @Test
+ fun dumpChanges_strChange_logsFromNext() {
+ systemClock.setCurrentTimeMillis(100L)
+
+ val prevDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("stringValChange", "prevStringVal")
+ }
+ }
+ val nextDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("stringValChange", "newStringVal")
+ }
+ }
+
+ underTest.logDiffs("prefix", prevDiffable, nextDiffable)
+
+ val dumpedString = dumpChanges()
+
+ val expected =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "prefix.stringValChange" +
+ SEPARATOR +
+ "newStringVal"
+ assertThat(dumpedString).contains(expected)
+ assertThat(dumpedString).doesNotContain("prevStringVal")
+ }
+
+ @Test
+ fun dumpChanges_boolChange_logsFromNext() {
+ systemClock.setCurrentTimeMillis(100L)
+
+ val prevDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("booleanValChange", false)
+ }
+ }
+ val nextDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("booleanValChange", true)
+ }
+ }
+
+ underTest.logDiffs("prefix", prevDiffable, nextDiffable)
+
+ val dumpedString = dumpChanges()
+
+ val expected =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "prefix.booleanValChange" +
+ SEPARATOR +
+ "true"
+ assertThat(dumpedString).contains(expected)
+ assertThat(dumpedString).doesNotContain("false")
+ }
+
+ @Test
+ fun dumpChanges_intChange_logsFromNext() {
+ systemClock.setCurrentTimeMillis(100L)
+
+ val prevDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("intValChange", 12345)
+ }
+ }
+ val nextDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("intValChange", 67890)
+ }
+ }
+
+ underTest.logDiffs("prefix", prevDiffable, nextDiffable)
+
+ val dumpedString = dumpChanges()
+
+ val expected =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "prefix.intValChange" +
+ SEPARATOR +
+ "67890"
+ assertThat(dumpedString).contains(expected)
+ assertThat(dumpedString).doesNotContain("12345")
+ }
+
+ @Test
+ fun dumpChanges_noPrefix() {
+ systemClock.setCurrentTimeMillis(100L)
+
+ val prevDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("booleanValChange", false)
+ }
+ }
+ val nextDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("booleanValChange", true)
+ }
+ }
+
+ // WHEN there's a blank prefix
+ underTest.logDiffs("", prevDiffable, nextDiffable)
+
+ val dumpedString = dumpChanges()
+
+ // THEN the dump still works
+ val expected =
+ TABLE_LOG_DATE_FORMAT.format(100L) + SEPARATOR + "booleanValChange" + SEPARATOR + "true"
+ assertThat(dumpedString).contains(expected)
+ }
+
+ @Test
+ fun dumpChanges_multipleChangesForSameColumn_logs() {
+ lateinit var valToDump: String
+
+ val diffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("valChange", valToDump)
+ }
+ }
+
+ systemClock.setCurrentTimeMillis(12000L)
+ valToDump = "stateValue12"
+ underTest.logDiffs(columnPrefix = "", diffable, diffable)
+
+ systemClock.setCurrentTimeMillis(20000L)
+ valToDump = "stateValue20"
+ underTest.logDiffs(columnPrefix = "", diffable, diffable)
+
+ systemClock.setCurrentTimeMillis(40000L)
+ valToDump = "stateValue40"
+ underTest.logDiffs(columnPrefix = "", diffable, diffable)
+
+ systemClock.setCurrentTimeMillis(45000L)
+ valToDump = "stateValue45"
+ underTest.logDiffs(columnPrefix = "", diffable, diffable)
+
+ val dumpedString = dumpChanges()
+
+ val expected1 =
+ TABLE_LOG_DATE_FORMAT.format(12000L) +
+ SEPARATOR +
+ "valChange" +
+ SEPARATOR +
+ "stateValue12"
+ val expected2 =
+ TABLE_LOG_DATE_FORMAT.format(20000L) +
+ SEPARATOR +
+ "valChange" +
+ SEPARATOR +
+ "stateValue20"
+ val expected3 =
+ TABLE_LOG_DATE_FORMAT.format(40000L) +
+ SEPARATOR +
+ "valChange" +
+ SEPARATOR +
+ "stateValue40"
+ val expected4 =
+ TABLE_LOG_DATE_FORMAT.format(45000L) +
+ SEPARATOR +
+ "valChange" +
+ SEPARATOR +
+ "stateValue45"
+ assertThat(dumpedString).contains(expected1)
+ assertThat(dumpedString).contains(expected2)
+ assertThat(dumpedString).contains(expected3)
+ assertThat(dumpedString).contains(expected4)
+ }
+
+ @Test
+ fun dumpChanges_multipleChangesAtOnce_logs() {
+ systemClock.setCurrentTimeMillis(100L)
+
+ val prevDiffable = object : TestDiffable() {}
+ val nextDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("status", "in progress")
+ row.logChange("connected", false)
+ }
+ }
+
+ underTest.logDiffs(columnPrefix = "", prevDiffable, nextDiffable)
+
+ val dumpedString = dumpChanges()
+
+ val timestamp = TABLE_LOG_DATE_FORMAT.format(100L)
+ val expected1 = timestamp + SEPARATOR + "status" + SEPARATOR + "in progress"
+ val expected2 = timestamp + SEPARATOR + "connected" + SEPARATOR + "false"
+ assertThat(dumpedString).contains(expected1)
+ assertThat(dumpedString).contains(expected2)
+ }
+
+ @Test
+ fun logChange_rowInitializer_dumpsCorrectly() {
+ systemClock.setCurrentTimeMillis(100L)
+
+ underTest.logChange("") { row ->
+ row.logChange("column1", "val1")
+ row.logChange("column2", 2)
+ row.logChange("column3", true)
+ }
+
+ val dumpedString = dumpChanges()
+
+ val timestamp = TABLE_LOG_DATE_FORMAT.format(100L)
+ val expected1 = timestamp + SEPARATOR + "column1" + SEPARATOR + "val1"
+ val expected2 = timestamp + SEPARATOR + "column2" + SEPARATOR + "2"
+ val expected3 = timestamp + SEPARATOR + "column3" + SEPARATOR + "true"
+ assertThat(dumpedString).contains(expected1)
+ assertThat(dumpedString).contains(expected2)
+ assertThat(dumpedString).contains(expected3)
+ }
+
+ @Test
+ fun logChangeAndLogDiffs_bothLogged() {
+ systemClock.setCurrentTimeMillis(100L)
+
+ underTest.logChange("") { row ->
+ row.logChange("column1", "val1")
+ row.logChange("column2", 2)
+ row.logChange("column3", true)
+ }
+
+ systemClock.setCurrentTimeMillis(200L)
+ val prevDiffable = object : TestDiffable() {}
+ val nextDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("column1", "newVal1")
+ row.logChange("column2", 222)
+ row.logChange("column3", false)
+ }
+ }
+
+ underTest.logDiffs(columnPrefix = "", prevDiffable, nextDiffable)
+
+ val dumpedString = dumpChanges()
+
+ val timestamp1 = TABLE_LOG_DATE_FORMAT.format(100L)
+ val expected1 = timestamp1 + SEPARATOR + "column1" + SEPARATOR + "val1"
+ val expected2 = timestamp1 + SEPARATOR + "column2" + SEPARATOR + "2"
+ val expected3 = timestamp1 + SEPARATOR + "column3" + SEPARATOR + "true"
+ val timestamp2 = TABLE_LOG_DATE_FORMAT.format(200L)
+ val expected4 = timestamp2 + SEPARATOR + "column1" + SEPARATOR + "newVal1"
+ val expected5 = timestamp2 + SEPARATOR + "column2" + SEPARATOR + "222"
+ val expected6 = timestamp2 + SEPARATOR + "column3" + SEPARATOR + "false"
+ assertThat(dumpedString).contains(expected1)
+ assertThat(dumpedString).contains(expected2)
+ assertThat(dumpedString).contains(expected3)
+ assertThat(dumpedString).contains(expected4)
+ assertThat(dumpedString).contains(expected5)
+ assertThat(dumpedString).contains(expected6)
+ }
+
+ @Test
+ fun dumpChanges_rotatesIfBufferIsFull() {
+ lateinit var valToDump: String
+
+ val prevDiffable = object : TestDiffable() {}
+ val nextDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("status", valToDump)
+ }
+ }
+
+ for (i in 0 until MAX_SIZE + 3) {
+ valToDump = "testString[$i]"
+ underTest.logDiffs(columnPrefix = "", prevDiffable, nextDiffable)
+ }
+
+ val dumpedString = dumpChanges()
+
+ assertThat(dumpedString).doesNotContain("testString[0]")
+ assertThat(dumpedString).doesNotContain("testString[1]")
+ assertThat(dumpedString).doesNotContain("testString[2]")
+ assertThat(dumpedString).contains("testString[3]")
+ assertThat(dumpedString).contains("testString[${MAX_SIZE + 2}]")
+ }
+
+ private fun dumpChanges(): String {
+ underTest.dump(PrintWriter(outputWriter), arrayOf())
+ return outputWriter.toString()
+ }
+
+ private fun logLines(string: String): List<String> {
+ return string.split("\n").filter { it.isNotBlank() }
+ }
+
+ private open class TestDiffable : Diffable<TestDiffable> {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {}
+ }
+}
+
+private const val NAME = "TestTableBuffer"
+private const val MAX_SIZE = 10
+
+// Copying these here from [TableLogBuffer] so that we catch any accidental versioning change
+private const val HEADER_PREFIX = "SystemUI StateChangeTableSection START: "
+private const val FOOTER_PREFIX = "SystemUI StateChangeTableSection END: "
+private const val SEPARATOR = "|" // TBD
+private const val VERSION = "1"
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/resume/MediaResumeListenerTest.kt
index 84fdfd78e9fc..136ace173795 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
@@ -38,6 +38,7 @@ 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.settings.UserTracker
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -79,6 +80,7 @@ private fun <T> any(): T = Mockito.any<T>()
class MediaResumeListenerTest : SysuiTestCase() {
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var device: MediaDeviceData
@Mock private lateinit var token: MediaSession.Token
@@ -131,12 +133,15 @@ class MediaResumeListenerTest : SysuiTestCase() {
whenever(sharedPrefsEditor.putString(any(), any())).thenReturn(sharedPrefsEditor)
whenever(mockContext.packageManager).thenReturn(context.packageManager)
whenever(mockContext.contentResolver).thenReturn(context.contentResolver)
+ whenever(mockContext.userId).thenReturn(context.userId)
executor = FakeExecutor(clock)
resumeListener =
MediaResumeListener(
mockContext,
broadcastDispatcher,
+ userTracker,
+ executor,
executor,
tunerService,
resumeBrowserFactory,
@@ -177,6 +182,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
MediaResumeListener(
context,
broadcastDispatcher,
+ userTracker,
+ executor,
executor,
tunerService,
resumeBrowserFactory,
@@ -185,7 +192,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
)
listener.setManager(mediaDataManager)
verify(broadcastDispatcher, never())
- .registerReceiver(eq(listener.userChangeReceiver), any(), any(), any(), anyInt(), any())
+ .registerReceiver(eq(listener.userUnlockReceiver), any(), any(), any(), anyInt(), any())
// When data is loaded, we do NOT execute or update anything
listener.onMediaDataLoaded(KEY, OLD_KEY, data)
@@ -289,7 +296,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
resumeListener.setManager(mediaDataManager)
verify(broadcastDispatcher)
.registerReceiver(
- eq(resumeListener.userChangeReceiver),
+ eq(resumeListener.userUnlockReceiver),
any(),
any(),
any(),
@@ -299,7 +306,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
// When we get an unlock event
val intent = Intent(Intent.ACTION_USER_UNLOCKED)
- resumeListener.userChangeReceiver.onReceive(context, intent)
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+ resumeListener.userUnlockReceiver.onReceive(context, intent)
// Then we should attempt to find recent media for each saved component
verify(resumeBrowser, times(3)).findRecentMedia()
@@ -375,6 +383,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
MediaResumeListener(
mockContext,
broadcastDispatcher,
+ userTracker,
+ executor,
executor,
tunerService,
resumeBrowserFactory,
@@ -386,7 +396,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
// When we load a component that was played recently
val intent = Intent(Intent.ACTION_USER_UNLOCKED)
- resumeListener.userChangeReceiver.onReceive(mockContext, intent)
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+ resumeListener.userUnlockReceiver.onReceive(mockContext, intent)
// We add its resume controls
verify(resumeBrowser, times(1)).findRecentMedia()
@@ -404,6 +415,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
MediaResumeListener(
mockContext,
broadcastDispatcher,
+ userTracker,
+ executor,
executor,
tunerService,
resumeBrowserFactory,
@@ -415,7 +428,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
// When we load a component that is not recent
val intent = Intent(Intent.ACTION_USER_UNLOCKED)
- resumeListener.userChangeReceiver.onReceive(mockContext, intent)
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+ resumeListener.userUnlockReceiver.onReceive(mockContext, intent)
// We do not try to add resume controls
verify(resumeBrowser, times(0)).findRecentMedia()
@@ -443,6 +457,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
MediaResumeListener(
mockContext,
broadcastDispatcher,
+ userTracker,
+ executor,
executor,
tunerService,
resumeBrowserFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index 6c03730e056e..1865ef64c5d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.navigationbar;
+import static android.app.StatusBarManager.WINDOW_NAVIGATION_BAR;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
@@ -47,6 +48,7 @@ import com.android.systemui.assist.AssistManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -69,6 +71,10 @@ import dagger.Lazy;
@SmallTest
public class NavBarHelperTest extends SysuiTestCase {
+ private static final int DISPLAY_ID = 0;
+ private static final int WINDOW = WINDOW_NAVIGATION_BAR;
+ private static final int STATE_ID = 0;
+
@Mock
AccessibilityManager mAccessibilityManager;
@Mock
@@ -93,6 +99,8 @@ public class NavBarHelperTest extends SysuiTestCase {
DumpManager mDumpManager;
@Mock
NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater;
+ @Mock
+ CommandQueue mCommandQueue;
private AccessibilityManager.AccessibilityServicesStateChangeListener
mAccessibilityServicesStateChangeListener;
@@ -114,7 +122,7 @@ public class NavBarHelperTest extends SysuiTestCase {
mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
mSystemActions, mOverviewProxyService, mAssistManagerLazy,
() -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
- mNavigationModeController, mUserTracker, mDumpManager);
+ mNavigationModeController, mUserTracker, mDumpManager, mCommandQueue);
}
@@ -241,6 +249,45 @@ public class NavBarHelperTest extends SysuiTestCase {
ACCESSIBILITY_BUTTON_CLICKABLE_STATE);
}
+ @Test
+ public void registerCommandQueueCallbacks() {
+ mNavBarHelper.init();
+ verify(mCommandQueue, times(1)).addCallback(any());
+ }
+
+ @Test
+ public void saveMostRecentSysuiState() {
+ mNavBarHelper.init();
+ mNavBarHelper.setWindowState(DISPLAY_ID, WINDOW, STATE_ID);
+ NavBarHelper.CurrentSysuiState state1 = mNavBarHelper.getCurrentSysuiState();
+
+ // Update window state
+ int newState = STATE_ID + 1;
+ mNavBarHelper.setWindowState(DISPLAY_ID, WINDOW, newState);
+ NavBarHelper.CurrentSysuiState state2 = mNavBarHelper.getCurrentSysuiState();
+
+ // Ensure we get most recent state back
+ assertThat(state1.mWindowState).isNotEqualTo(state2.mWindowState);
+ assertThat(state1.mWindowStateDisplayId).isEqualTo(state2.mWindowStateDisplayId);
+ assertThat(state2.mWindowState).isEqualTo(newState);
+ }
+
+ @Test
+ public void ignoreNonNavbarSysuiState() {
+ mNavBarHelper.init();
+ mNavBarHelper.setWindowState(DISPLAY_ID, WINDOW, STATE_ID);
+ NavBarHelper.CurrentSysuiState state1 = mNavBarHelper.getCurrentSysuiState();
+
+ // Update window state for other window type
+ int newState = STATE_ID + 1;
+ mNavBarHelper.setWindowState(DISPLAY_ID, WINDOW + 1, newState);
+ NavBarHelper.CurrentSysuiState state2 = mNavBarHelper.getCurrentSysuiState();
+
+ // Ensure we get first state back
+ assertThat(state2.mWindowState).isEqualTo(state1.mWindowState);
+ assertThat(state2.mWindowState).isNotEqualTo(newState);
+ }
+
private List<String> createFakeShortcutTargets() {
return new ArrayList<>(List.of("a", "b", "c", "d"));
}
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 c1fa9b39f50b..80adbf025e0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -44,14 +44,11 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.IntentFilter;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.telecom.TelecomManager;
import android.testing.AndroidTestingRunner;
@@ -79,7 +76,6 @@ import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
@@ -119,6 +115,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Optional;
+import java.util.concurrent.Executor;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -166,7 +163,7 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private Handler mHandler;
@Mock
- private BroadcastDispatcher mBroadcastDispatcher;
+ private UserTracker mUserTracker;
@Mock
private UiEventLogger mUiEventLogger;
@Mock
@@ -247,7 +244,7 @@ public class NavigationBarTest extends SysuiTestCase {
mSystemActions, mOverviewProxyService,
() -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
mKeyguardStateController, mock(NavigationModeController.class),
- mock(UserTracker.class), mock(DumpManager.class)));
+ mock(UserTracker.class), mock(DumpManager.class), mock(CommandQueue.class)));
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
@@ -315,14 +312,10 @@ public class NavigationBarTest extends SysuiTestCase {
}
@Test
- public void testRegisteredWithDispatcher() {
+ public void testRegisteredWithUserTracker() {
mNavigationBar.init();
mNavigationBar.onViewAttached();
- verify(mBroadcastDispatcher).registerReceiverWithHandler(
- any(BroadcastReceiver.class),
- any(IntentFilter.class),
- any(Handler.class),
- any(UserHandle.class));
+ verify(mUserTracker).addCallback(any(UserTracker.Callback.class), any(Executor.class));
}
@Test
@@ -438,6 +431,12 @@ public class NavigationBarTest extends SysuiTestCase {
verify(mNavigationBarView).setVisibility(View.INVISIBLE);
}
+ @Test
+ public void testOnInit_readCurrentSysuiState() {
+ mNavigationBar.init();
+ verify(mNavBarHelper, times(1)).getCurrentSysuiState();
+ }
+
private NavigationBar createNavBar(Context context) {
DeviceProvisionedController deviceProvisionedController =
mock(DeviceProvisionedController.class);
@@ -457,7 +456,7 @@ public class NavigationBarTest extends SysuiTestCase {
mStatusBarStateController,
mStatusBarKeyguardViewManager,
mMockSysUiState,
- mBroadcastDispatcher,
+ mUserTracker,
mCommandQueue,
Optional.of(mock(Pip.class)),
Optional.of(mock(Recents.class)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index c377c374148f..338182a3e304 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -48,6 +48,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.power.PowerUI.WarningsUI;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -85,6 +86,7 @@ public class PowerUITest extends SysuiTestCase {
private PowerUI mPowerUI;
@Mock private EnhancedEstimates mEnhancedEstimates;
@Mock private PowerManager mPowerManager;
+ @Mock private UserTracker mUserTracker;
@Mock private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock private IThermalService mThermalServiceMock;
private IThermalEventListener mUsbThermalEventListener;
@@ -682,7 +684,8 @@ public class PowerUITest extends SysuiTestCase {
private void createPowerUi() {
mPowerUI = new PowerUI(
mContext, mBroadcastDispatcher, mCommandQueue, mCentralSurfacesOptionalLazy,
- mMockWarnings, mEnhancedEstimates, mWakefulnessLifecycle, mPowerManager);
+ mMockWarnings, mEnhancedEstimates, mWakefulnessLifecycle, mPowerManager,
+ mUserTracker);
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
index 645b1cde632f..23466cc20f44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
@@ -41,6 +41,7 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestCoroutineScheduler
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,7 +56,7 @@ class FooterActionsInteractorTest : SysuiTestCase() {
@Before
fun setUp() {
- utils = FooterActionsTestUtils(context, TestableLooper.get(this))
+ utils = FooterActionsTestUtils(context, TestableLooper.get(this), TestCoroutineScheduler())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 081a2181cfe5..47afa70fa84b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -29,6 +29,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.FakeFgsManagerController
import com.android.systemui.qs.QSSecurityFooterUtils
import com.android.systemui.qs.footer.FooterActionsTestUtils
@@ -44,12 +45,9 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -62,16 +60,20 @@ import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class FooterActionsViewModelTest : SysuiTestCase() {
+ private val testScope = TestScope()
private lateinit var utils: FooterActionsTestUtils
- private val testDispatcher = UnconfinedTestDispatcher(TestCoroutineScheduler())
@Before
fun setUp() {
- utils = FooterActionsTestUtils(context, TestableLooper.get(this))
+ utils = FooterActionsTestUtils(context, TestableLooper.get(this), testScope.testScheduler)
+ }
+
+ private fun runTest(block: suspend TestScope.() -> Unit) {
+ testScope.runTest(testBody = block)
}
@Test
- fun settingsButton() = runBlockingTest {
+ fun settingsButton() = runTest {
val underTest = utils.footerActionsViewModel(showPowerButton = false)
val settings = underTest.settings
@@ -87,7 +89,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
}
@Test
- fun powerButton() = runBlockingTest {
+ fun powerButton() = runTest {
// Without power button.
val underTestWithoutPower = utils.footerActionsViewModel(showPowerButton = false)
assertThat(underTestWithoutPower.power).isNull()
@@ -114,7 +116,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
}
@Test
- fun userSwitcher() = runBlockingTest {
+ fun userSwitcher() = runTest {
val picture: Drawable = mock()
val userInfoController = FakeUserInfoController(FakeInfo(picture = picture))
val settings = FakeSettings()
@@ -135,7 +137,6 @@ class FooterActionsViewModelTest : SysuiTestCase() {
showPowerButton = false,
footerActionsInteractor =
utils.footerActionsInteractor(
- bgDispatcher = testDispatcher,
userSwitcherRepository =
utils.userSwitcherRepository(
userTracker = userTracker,
@@ -143,22 +144,12 @@ class FooterActionsViewModelTest : SysuiTestCase() {
userManager = userManager,
userInfoController = userInfoController,
userSwitcherController = userSwitcherControllerWrapper.controller,
- bgDispatcher = testDispatcher,
),
)
)
// Collect the user switcher into currentUserSwitcher.
- var currentUserSwitcher: FooterActionsButtonViewModel? = null
- val job = launch { underTest.userSwitcher.collect { currentUserSwitcher = it } }
- fun currentUserSwitcher(): FooterActionsButtonViewModel? {
- // Make sure we finish collecting the current user switcher. This is necessary because
- // combined flows launch multiple coroutines in the current scope so we need to make
- // sure we process all coroutines triggered by our flow collection before we make
- // assertions on the current buttons.
- advanceUntilIdle()
- return currentUserSwitcher
- }
+ val currentUserSwitcher = collectLastValue(underTest.userSwitcher)
// The user switcher is disabled.
assertThat(currentUserSwitcher()).isNull()
@@ -203,12 +194,10 @@ class FooterActionsViewModelTest : SysuiTestCase() {
// in guest mode.
userInfoController.updateInfo { this.picture = mock<UserIconDrawable>() }
assertThat(iconTint()).isNull()
-
- job.cancel()
}
@Test
- fun security() = runBlockingTest {
+ fun security() = runTest {
val securityController = FakeSecurityController()
val qsSecurityFooterUtils = mock<QSSecurityFooterUtils>()
@@ -224,22 +213,15 @@ class FooterActionsViewModelTest : SysuiTestCase() {
footerActionsInteractor =
utils.footerActionsInteractor(
qsSecurityFooterUtils = qsSecurityFooterUtils,
- bgDispatcher = testDispatcher,
securityRepository =
utils.securityRepository(
securityController = securityController,
- bgDispatcher = testDispatcher,
),
),
)
// Collect the security model into currentSecurity.
- var currentSecurity: FooterActionsSecurityButtonViewModel? = null
- val job = launch { underTest.security.collect { currentSecurity = it } }
- fun currentSecurity(): FooterActionsSecurityButtonViewModel? {
- advanceUntilIdle()
- return currentSecurity
- }
+ val currentSecurity = collectLastValue(underTest.security)
// By default, we always return a null SecurityButtonConfig.
assertThat(currentSecurity()).isNull()
@@ -270,12 +252,10 @@ class FooterActionsViewModelTest : SysuiTestCase() {
security = currentSecurity()
assertThat(security).isNotNull()
assertThat(security!!.onClick).isNull()
-
- job.cancel()
}
@Test
- fun foregroundServices() = runBlockingTest {
+ fun foregroundServices() = runTest {
val securityController = FakeSecurityController()
val fgsManagerController =
FakeFgsManagerController(
@@ -300,21 +280,14 @@ class FooterActionsViewModelTest : SysuiTestCase() {
securityRepository =
utils.securityRepository(
securityController,
- bgDispatcher = testDispatcher,
),
foregroundServicesRepository =
utils.foregroundServicesRepository(fgsManagerController),
- bgDispatcher = testDispatcher,
),
)
// Collect the security model into currentSecurity.
- var currentForegroundServices: FooterActionsForegroundServicesButtonViewModel? = null
- val job = launch { underTest.foregroundServices.collect { currentForegroundServices = it } }
- fun currentForegroundServices(): FooterActionsForegroundServicesButtonViewModel? {
- advanceUntilIdle()
- return currentForegroundServices
- }
+ val currentForegroundServices = collectLastValue(underTest.foregroundServices)
// We don't show the foreground services button if the number of running packages is not
// > 1.
@@ -356,12 +329,10 @@ class FooterActionsViewModelTest : SysuiTestCase() {
}
securityController.updateState {}
assertThat(currentForegroundServices()?.displayText).isFalse()
-
- job.cancel()
}
@Test
- fun observeDeviceMonitoringDialogRequests() = runBlockingTest {
+ fun observeDeviceMonitoringDialogRequests() = runTest {
val qsSecurityFooterUtils = mock<QSSecurityFooterUtils>()
val broadcastDispatcher = mock<BroadcastDispatcher>()
@@ -390,7 +361,6 @@ class FooterActionsViewModelTest : SysuiTestCase() {
utils.footerActionsInteractor(
qsSecurityFooterUtils = qsSecurityFooterUtils,
broadcastDispatcher = broadcastDispatcher,
- bgDispatcher = testDispatcher,
),
)
@@ -415,7 +385,4 @@ class FooterActionsViewModelTest : SysuiTestCase() {
underTest.onVisibilityChangeRequested(visible = true)
assertThat(underTest.isVisible.value).isTrue()
}
-
- private fun runBlockingTest(block: suspend TestScope.() -> Unit) =
- runTest(testDispatcher) { block() }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
new file mode 100644
index 000000000000..ea0e454f0b5d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.reardisplay;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.hardware.devicestate.DeviceStateManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class RearDisplayDialogControllerTest extends SysuiTestCase {
+
+ @Mock
+ private CommandQueue mCommandQueue;
+
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+
+ private static final int CLOSED_BASE_STATE = 0;
+ private static final int OPEN_BASE_STATE = 1;
+
+ @Test
+ public void testClosedDialogIsShown() {
+ RearDisplayDialogController controller = new RearDisplayDialogController(mContext,
+ mCommandQueue, mFakeExecutor);
+ controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback());
+ controller.setFoldedStates(new int[]{0});
+ controller.setAnimationRepeatCount(0);
+
+ controller.showRearDisplayDialog(CLOSED_BASE_STATE);
+ assertTrue(controller.mRearDisplayEducationDialog.isShowing());
+ View deviceOpenedWarningTextView = controller.mRearDisplayEducationDialog.findViewById(
+ R.id.rear_display_warning_text_view);
+ assertNull(deviceOpenedWarningTextView);
+ }
+
+ @Test
+ public void testOpenDialogIsShown() {
+ RearDisplayDialogController controller = new RearDisplayDialogController(mContext,
+ mCommandQueue, mFakeExecutor);
+ controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback());
+ controller.setFoldedStates(new int[]{0});
+ controller.setAnimationRepeatCount(0);
+
+ controller.showRearDisplayDialog(OPEN_BASE_STATE);
+
+ assertTrue(controller.mRearDisplayEducationDialog.isShowing());
+ View deviceOpenedWarningTextView = controller.mRearDisplayEducationDialog.findViewById(
+ R.id.rear_display_warning_text_view);
+ assertNotNull(deviceOpenedWarningTextView);
+ }
+
+ /**
+ * Empty device state manager callbacks, so we can verify that the correct
+ * dialogs are being created regardless of device state of the test device.
+ */
+ private static class TestDeviceStateManagerCallback implements
+ DeviceStateManager.DeviceStateCallback {
+
+ @Override
+ public void onStateChanged(int state) { }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 013e58ed99d7..69f3e987ec1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -33,6 +33,9 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -49,12 +52,16 @@ import org.mockito.MockitoAnnotations;
*/
public class RecordingControllerTest extends SysuiTestCase {
+ private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@Mock
private RecordingController.RecordingStateChangeCallback mCallback;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
private UserContextProvider mUserContextProvider;
+ @Mock
+ private UserTracker mUserTracker;
private RecordingController mController;
@@ -63,7 +70,8 @@ public class RecordingControllerTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mController = new RecordingController(mBroadcastDispatcher, mUserContextProvider);
+ mController = new RecordingController(mMainExecutor, mBroadcastDispatcher,
+ mUserContextProvider, mUserTracker);
mController.addCallback(mCallback);
}
@@ -176,9 +184,7 @@ public class RecordingControllerTest extends SysuiTestCase {
mController.updateState(true);
// and user is changed
- Intent intent = new Intent(Intent.ACTION_USER_SWITCHED)
- .putExtra(Intent.EXTRA_USER_HANDLE, USER_ID);
- mController.mUserChangeReceiver.onReceive(mContext, intent);
+ mController.mUserChangedCallback.onUserChanged(USER_ID, mContext);
// Ensure that the recording was stopped
verify(mCallback).onRecordingEnd();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
index 6d9b01e28aa4..020a86611552 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
@@ -50,24 +50,20 @@ class UserFileManagerImplTest : SysuiTestCase() {
lateinit var userFileManager: UserFileManagerImpl
lateinit var backgroundExecutor: FakeExecutor
- @Mock
- lateinit var userManager: UserManager
- @Mock
- lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock lateinit var userManager: UserManager
+ @Mock lateinit var broadcastDispatcher: BroadcastDispatcher
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
backgroundExecutor = FakeExecutor(FakeSystemClock())
- userFileManager = UserFileManagerImpl(context, userManager,
- broadcastDispatcher, backgroundExecutor)
+ userFileManager =
+ UserFileManagerImpl(context, userManager, broadcastDispatcher, backgroundExecutor)
}
@After
fun end() {
- val dir = Environment.buildPath(
- context.filesDir,
- UserFileManagerImpl.ID)
+ val dir = Environment.buildPath(context.filesDir, UserFileManagerImpl.ID)
dir.deleteRecursively()
}
@@ -82,13 +78,14 @@ class UserFileManagerImplTest : SysuiTestCase() {
@Test
fun testGetSharedPreferences() {
val secondarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11)
- val secondaryUserDir = Environment.buildPath(
- context.filesDir,
- UserFileManagerImpl.ID,
- "11",
- UserFileManagerImpl.SHARED_PREFS,
- TEST_FILE_NAME
- )
+ val secondaryUserDir =
+ Environment.buildPath(
+ context.filesDir,
+ UserFileManagerImpl.ID,
+ "11",
+ UserFileManagerImpl.SHARED_PREFS,
+ TEST_FILE_NAME
+ )
assertThat(secondarySharedPref).isNotNull()
assertThat(secondaryUserDir.exists())
@@ -101,32 +98,35 @@ class UserFileManagerImplTest : SysuiTestCase() {
val userFileManager = spy(userFileManager)
userFileManager.start()
verify(userFileManager).clearDeletedUserData()
- verify(broadcastDispatcher).registerReceiver(any(BroadcastReceiver::class.java),
- any(IntentFilter::class.java),
- any(Executor::class.java), isNull(), eq(Context.RECEIVER_EXPORTED), isNull())
+ verify(broadcastDispatcher)
+ .registerReceiver(
+ any(BroadcastReceiver::class.java),
+ any(IntentFilter::class.java),
+ any(Executor::class.java),
+ isNull(),
+ eq(Context.RECEIVER_EXPORTED),
+ isNull()
+ )
}
@Test
fun testClearDeletedUserData() {
- val dir = Environment.buildPath(
- context.filesDir,
- UserFileManagerImpl.ID,
- "11",
- "files"
- )
+ val dir = Environment.buildPath(context.filesDir, UserFileManagerImpl.ID, "11", "files")
dir.mkdirs()
- val file = Environment.buildPath(
- context.filesDir,
- UserFileManagerImpl.ID,
- "11",
- "files",
- TEST_FILE_NAME
- )
- val secondaryUserDir = Environment.buildPath(
- context.filesDir,
- UserFileManagerImpl.ID,
- "11",
- )
+ val file =
+ Environment.buildPath(
+ context.filesDir,
+ UserFileManagerImpl.ID,
+ "11",
+ "files",
+ TEST_FILE_NAME
+ )
+ val secondaryUserDir =
+ Environment.buildPath(
+ context.filesDir,
+ UserFileManagerImpl.ID,
+ "11",
+ )
file.createNewFile()
assertThat(secondaryUserDir.exists()).isTrue()
assertThat(file.exists()).isTrue()
@@ -139,15 +139,16 @@ class UserFileManagerImplTest : SysuiTestCase() {
@Test
fun testEnsureParentDirExists() {
- val file = Environment.buildPath(
- context.filesDir,
- UserFileManagerImpl.ID,
- "11",
- "files",
- TEST_FILE_NAME
- )
+ val file =
+ Environment.buildPath(
+ context.filesDir,
+ UserFileManagerImpl.ID,
+ "11",
+ "files",
+ TEST_FILE_NAME
+ )
assertThat(file.parentFile.exists()).isFalse()
- userFileManager.ensureParentDirExists(file)
+ UserFileManagerImpl.ensureParentDirExists(file)
assertThat(file.parentFile.exists()).isTrue()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index e1007faae88b..858d0e7b94d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -35,6 +35,8 @@ import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.demomode.DemoMode
+import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -50,10 +52,12 @@ import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.VariableDateView
import com.android.systemui.statusbar.policy.VariableDateViewController
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -104,7 +108,7 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
@Mock
private lateinit var featureFlags: FeatureFlags
@Mock
- private lateinit var clock: TextView
+ private lateinit var clock: Clock
@Mock
private lateinit var date: VariableDateView
@Mock
@@ -138,6 +142,7 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
private lateinit var qsConstraints: ConstraintSet
@Mock
private lateinit var largeScreenConstraints: ConstraintSet
+ @Mock private lateinit var demoModeController: DemoModeController
@JvmField @Rule
val mockitoRule = MockitoJUnit.rule()
@@ -146,10 +151,12 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
private lateinit var controller: LargeScreenShadeHeaderController
private lateinit var carrierIconSlots: List<String>
private val configurationController = FakeConfigurationController()
+ private lateinit var demoModeControllerCapture: ArgumentCaptor<DemoMode>
@Before
fun setUp() {
- whenever<TextView>(view.findViewById(R.id.clock)).thenReturn(clock)
+ demoModeControllerCapture = argumentCaptor<DemoMode>()
+ whenever<Clock>(view.findViewById(R.id.clock)).thenReturn(clock)
whenever(clock.context).thenReturn(mockedContext)
whenever<TextView>(view.findViewById(R.id.date)).thenReturn(date)
@@ -195,7 +202,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
dumpManager,
featureFlags,
qsCarrierGroupControllerBuilder,
- combinedShadeHeadersConstraintManager
+ combinedShadeHeadersConstraintManager,
+ demoModeController
)
whenever(view.isAttachedToWindow).thenReturn(true)
controller.init()
@@ -617,6 +625,21 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
}
@Test
+ fun demoMode_attachDemoMode() {
+ verify(demoModeController).addCallback(capture(demoModeControllerCapture))
+ demoModeControllerCapture.value.onDemoModeStarted()
+ verify(clock).onDemoModeStarted()
+ }
+
+ @Test
+ fun demoMode_detachDemoMode() {
+ controller.simulateViewDetached()
+ verify(demoModeController).removeCallback(capture(demoModeControllerCapture))
+ demoModeControllerCapture.value.onDemoModeFinished()
+ verify(clock).onDemoModeFinished()
+ }
+
+ @Test
fun animateOutOnStartCustomizing() {
val animator = Mockito.mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
val duration = 1000L
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index 90ae693db955..b4c8f981b760 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -13,6 +13,8 @@ import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.demomode.DemoMode
+import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -22,9 +24,12 @@ import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.VariableDateViewController
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -52,7 +57,7 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
@Mock private lateinit var qsCarrierGroupController: QSCarrierGroupController
@Mock private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
@Mock private lateinit var featureFlags: FeatureFlags
- @Mock private lateinit var clock: TextView
+ @Mock private lateinit var clock: Clock
@Mock private lateinit var date: TextView
@Mock private lateinit var carrierGroup: QSCarrierGroup
@Mock private lateinit var batteryMeterView: BatteryMeterView
@@ -66,6 +71,7 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
CombinedShadeHeadersConstraintManager
@Mock private lateinit var mockedContext: Context
+ @Mock private lateinit var demoModeController: DemoModeController
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
@@ -76,7 +82,7 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
@Before
fun setup() {
- whenever<TextView>(view.findViewById(R.id.clock)).thenReturn(clock)
+ whenever<Clock>(view.findViewById(R.id.clock)).thenReturn(clock)
whenever(clock.context).thenReturn(mockedContext)
whenever<TextView>(view.findViewById(R.id.date)).thenReturn(date)
whenever(date.context).thenReturn(mockedContext)
@@ -111,8 +117,9 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
dumpManager,
featureFlags,
qsCarrierGroupControllerBuilder,
- combinedShadeHeadersConstraintManager
- )
+ combinedShadeHeadersConstraintManager,
+ demoModeController
+ )
whenever(view.isAttachedToWindow).thenReturn(true)
mLargeScreenShadeHeaderController.init()
carrierIconSlots = listOf(
@@ -230,4 +237,21 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
verify(animator).setInterpolator(Interpolators.ALPHA_IN)
verify(animator).start()
}
+
+ @Test
+ fun demoMode_attachDemoMode() {
+ val cb = argumentCaptor<DemoMode>()
+ verify(demoModeController).addCallback(capture(cb))
+ cb.value.onDemoModeStarted()
+ verify(clock).onDemoModeStarted()
+ }
+
+ @Test
+ fun demoMode_detachDemoMode() {
+ mLargeScreenShadeHeaderController.simulateViewDetached()
+ val cb = argumentCaptor<DemoMode>()
+ verify(demoModeController).removeCallback(capture(cb))
+ cb.value.onDemoModeFinished()
+ verify(clock).onDemoModeFinished()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 69a45599668b..b6f74f0a13ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -131,6 +131,7 @@ import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -383,7 +384,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mInteractionJankMonitor, mShadeExpansionStateManager),
mKeyguardBypassController,
mDozeParameters,
- mScreenOffAnimationController);
+ mScreenOffAnimationController,
+ mock(NotificationWakeUpCoordinatorLogger.class));
mConfigurationController = new ConfigurationControllerImpl(mContext);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
mContext,
@@ -499,8 +501,18 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mDumpManager);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
+ null,
() -> {},
mNotificationShelfController);
+ mNotificationPanelViewController.setTrackingStartedListener(() -> {});
+ mNotificationPanelViewController.setOpenCloseListener(
+ new NotificationPanelViewController.OpenCloseListener() {
+ @Override
+ public void onClosingFinished() {}
+
+ @Override
+ public void onOpenStarted() {}
+ });
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
@@ -831,7 +843,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
public void handleTouchEventFromStatusBar_panelAndViewEnabled_viewReceivesEvent() {
when(mCommandQueue.panelsEnabled()).thenReturn(true);
when(mView.isEnabled()).thenReturn(true);
- MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0);
+ MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0);
mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event);
@@ -839,6 +851,17 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
+ public void handleTouchEventFromStatusBar_topEdgeTouch_viewNeverReceivesEvent() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+ when(mView.isEnabled()).thenReturn(true);
+ MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0);
+
+ mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event);
+
+ verify(mView, never()).dispatchTouchEvent(event);
+ }
+
+ @Test
public void testA11y_initializeNode() {
AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
mAccessibilityDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 42bc7948caf4..8aaa18129834 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -117,13 +117,6 @@ public class CommandQueueTest extends SysuiTestCase {
}
@Test
- public void testCollapsePanels() {
- mCommandQueue.animateCollapsePanels();
- waitForIdleSync();
- verify(mCallbacks).animateCollapsePanels(eq(0), eq(false));
- }
-
- @Test
public void testExpandSettings() {
String panel = "some_panel";
mCommandQueue.animateExpandSettingsPanel(panel);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 15a687d2adc7..452606dfcca4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar;
-import static android.content.Intent.ACTION_USER_SWITCHED;
-
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -34,7 +32,6 @@ import android.app.Notification;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Handler;
@@ -293,11 +290,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
}
@Test
- public void testActionUserSwitchedCallsOnUserSwitched() {
- Intent intent = new Intent()
- .setAction(ACTION_USER_SWITCHED)
- .putExtra(Intent.EXTRA_USER_HANDLE, mSecondaryUser.id);
- mLockscreenUserManager.getBaseBroadcastReceiverForTest().onReceive(mContext, intent);
+ public void testUserSwitchedCallsOnUserSwitched() {
+ mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanged(mSecondaryUser.id,
+ mContext);
verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
}
@@ -366,6 +361,10 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
return mBaseBroadcastReceiver;
}
+ public UserTracker.Callback getUserTrackerCallbackForTest() {
+ return mUserChangedCallback;
+ }
+
public ContentObserver getLockscreenSettingsObserverForTest() {
return mLockscreenSettingsObserver;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index bdedd244abfa..7f73856bff89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -30,6 +30,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
+import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider
+import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -103,6 +105,31 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
}
@Test
+ fun unseenFilterUpdatesSeenProviderWhenSuppressing() {
+ whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
+
+ // GIVEN: Keyguard is not showing, and a notification is present
+ keyguardRepository.setKeyguardShowing(false)
+ runKeyguardCoordinatorTest {
+ val fakeEntry = NotificationEntryBuilder().build()
+ collectionListener.onEntryAdded(fakeEntry)
+
+ // WHEN: The keyguard is now showing
+ keyguardRepository.setKeyguardShowing(true)
+ testScheduler.runCurrent()
+
+ // THEN: The notification is recognized as "seen" and is filtered out.
+ assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isTrue()
+
+ // WHEN: The filter is cleaned up
+ unseenFilter.onCleanup()
+
+ // THEN: The SeenNotificationProvider has been updated to reflect the suppression
+ assertThat(seenNotificationsProvider.hasFilteredOutSeenNotifications).isTrue()
+ }
+ }
+
+ @Test
fun unseenFilterAllowsNewNotif() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
@@ -204,6 +231,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
testBlock: suspend KeyguardCoordinatorTestScope.() -> Unit
) {
val testScope = TestScope(UnconfinedTestDispatcher())
+ val seenNotificationsProvider = SeenNotificationsProviderImpl()
val keyguardCoordinator =
KeyguardCoordinator(
keyguardNotifVisibilityProvider,
@@ -211,18 +239,20 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
notifPipelineFlags,
testScope.backgroundScope,
sectionHeaderVisibilityProvider,
+ seenNotificationsProvider,
statusBarStateController,
)
keyguardCoordinator.attach(notifPipeline)
- KeyguardCoordinatorTestScope(keyguardCoordinator, testScope).run {
- testScheduler.advanceUntilIdle()
- testScope.runTest(dispatchTimeoutMs = 1.seconds.inWholeMilliseconds) { testBlock() }
+ testScope.runTest(dispatchTimeoutMs = 1.seconds.inWholeMilliseconds) {
+ KeyguardCoordinatorTestScope(keyguardCoordinator, testScope, seenNotificationsProvider)
+ .testBlock()
}
}
private inner class KeyguardCoordinatorTestScope(
private val keyguardCoordinator: KeyguardCoordinator,
private val scope: TestScope,
+ val seenNotificationsProvider: SeenNotificationsProvider,
) : CoroutineScope by scope {
val testScheduler: TestCoroutineScheduler
get() = scope.testScheduler
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index 8b7b4dea155f..6bd3f7a27413 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -26,22 +26,17 @@ import static android.app.NotificationManager.IMPORTANCE_MIN;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.collection.EntryUtilKt.modifyEntry;
-import static com.android.systemui.util.mockito.KotlinMockitoHelpersKt.argThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
@@ -54,10 +49,10 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.CoreStartable;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -97,7 +92,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
- @Mock private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock private UserTracker mUserTracker;
private final FakeSettings mFakeSettings = new FakeSettings();
private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
@@ -117,7 +112,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
mKeyguardUpdateMonitor,
mHighPriorityProvider,
mStatusBarStateController,
- mBroadcastDispatcher,
+ mUserTracker,
mFakeSettings,
mFakeSettings);
mKeyguardNotificationVisibilityProvider = component.getProvider();
@@ -205,23 +200,19 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
}
@Test
- public void notifyListeners_onReceiveUserSwitchBroadcast() {
- ArgumentCaptor<BroadcastReceiver> callbackCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mBroadcastDispatcher).registerReceiver(
+ public void notifyListeners_onReceiveUserSwitchCallback() {
+ ArgumentCaptor<UserTracker.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(UserTracker.Callback.class);
+ verify(mUserTracker).addCallback(
callbackCaptor.capture(),
- argThat(intentFilter -> intentFilter.hasAction(Intent.ACTION_USER_SWITCHED)),
- isNull(),
- isNull(),
- eq(Context.RECEIVER_EXPORTED),
- isNull());
- BroadcastReceiver callback = callbackCaptor.getValue();
+ any());
+ UserTracker.Callback callback = callbackCaptor.getValue();
Consumer<String> listener = mock(Consumer.class);
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
- callback.onReceive(mContext, new Intent(Intent.ACTION_USER_SWITCHED));
+ callback.onUserChanged(CURR_USER_ID, mContext);
verify(listener).accept(anyString());
}
@@ -619,7 +610,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
@BindsInstance KeyguardUpdateMonitor keyguardUpdateMonitor,
@BindsInstance HighPriorityProvider highPriorityProvider,
@BindsInstance SysuiStatusBarStateController statusBarStateController,
- @BindsInstance BroadcastDispatcher broadcastDispatcher,
+ @BindsInstance UserTracker userTracker,
@BindsInstance SecureSettings secureSettings,
@BindsInstance GlobalSettings globalSettings
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index ea311da3e20b..21aae00f12ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.interruption;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -33,6 +34,8 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -390,6 +393,127 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
+ private long makeWhenHoursAgo(long hoursAgo) {
+ return System.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo);
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_flagDisabled() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(false);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = makeWhenHoursAgo(25);
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_whenNow() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_whenRecent() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = makeWhenHoursAgo(13);
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_whenZero() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = 0L;
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(0L), anyLong(),
+ eq("when <= 0"));
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_whenNegative() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = -1L;
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(-1L), anyLong(),
+ eq("when <= 0"));
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_hasFullScreenIntent() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+ long when = makeWhenHoursAgo(25);
+
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silent= */ false);
+ entry.getSbn().getNotification().when = when;
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(when), anyLong(),
+ eq("full-screen intent"));
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_isForegroundService() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+ long when = makeWhenHoursAgo(25);
+
+ NotificationEntry entry = createFgsNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = when;
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(when), anyLong(),
+ eq("foreground service"));
+ }
+
+ @Test
+ public void testShouldNotHeadsUp_oldWhen() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+ long when = makeWhenHoursAgo(25);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = when;
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
+
+ verify(mLogger).logNoHeadsUpOldWhen(eq(entry), eq(when), anyLong());
+ verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+ }
+
@Test
public void testShouldNotFullScreen_notPendingIntent_withStrictFlag() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
@@ -763,6 +887,16 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
return createNotification(importance, n);
}
+ private NotificationEntry createFgsNotification(int importance) {
+ Notification n = new Notification.Builder(getContext(), "a")
+ .setContentTitle("title")
+ .setContentText("content text")
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .build();
+
+ return createNotification(importance, n);
+ }
+
private final NotificationInterruptSuppressor
mSuppressAwakeHeadsUp =
new NotificationInterruptSuppressor() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
new file mode 100644
index 000000000000..2d23f3c4aea8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.PluginManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.SmartReplyController
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
+import com.android.systemui.statusbar.notification.logging.NotificationLogger
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.SmartReplyConstants
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import junit.framework.Assert
+import org.junit.After
+import org.junit.Before
+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.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ExpandableNotificationRowControllerTest : SysuiTestCase() {
+
+ private val appName = "MyApp"
+ private val notifKey = "MyNotifKey"
+
+ private val view: ExpandableNotificationRow = mock()
+ private val activableNotificationViewController: ActivatableNotificationViewController = mock()
+ private val rivSubComponentFactory: RemoteInputViewSubcomponent.Factory = mock()
+ private val metricsLogger: MetricsLogger = mock()
+ private val logBufferLogger: NotificationRowLogger = mock()
+ private val listContainer: NotificationListContainer = mock()
+ private val mediaManager: NotificationMediaManager = mock()
+ private val smartReplyConstants: SmartReplyConstants = mock()
+ private val smartReplyController: SmartReplyController = mock()
+ private val pluginManager: PluginManager = mock()
+ private val systemClock: SystemClock = mock()
+ private val keyguardBypassController: KeyguardBypassController = mock()
+ private val groupMembershipManager: GroupMembershipManager = mock()
+ private val groupExpansionManager: GroupExpansionManager = mock()
+ private val rowContentBindStage: RowContentBindStage = mock()
+ private val notifLogger: NotificationLogger = mock()
+ private val headsUpManager: HeadsUpManager = mock()
+ private val onExpandClickListener: ExpandableNotificationRow.OnExpandClickListener = mock()
+ private val statusBarStateController: StatusBarStateController = mock()
+ private val gutsManager: NotificationGutsManager = mock()
+ private val onUserInteractionCallback: OnUserInteractionCallback = mock()
+ private val falsingManager: FalsingManager = mock()
+ private val falsingCollector: FalsingCollector = mock()
+ private val featureFlags: FeatureFlags = mock()
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier = mock()
+ private val bubblesManager: BubblesManager = mock()
+ private val dragController: ExpandableNotificationRowDragController = mock()
+ private lateinit var controller: ExpandableNotificationRowController
+
+ @Before
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ controller =
+ ExpandableNotificationRowController(
+ view,
+ activableNotificationViewController,
+ rivSubComponentFactory,
+ metricsLogger,
+ logBufferLogger,
+ listContainer,
+ mediaManager,
+ smartReplyConstants,
+ smartReplyController,
+ pluginManager,
+ systemClock,
+ appName,
+ notifKey,
+ keyguardBypassController,
+ groupMembershipManager,
+ groupExpansionManager,
+ rowContentBindStage,
+ notifLogger,
+ headsUpManager,
+ onExpandClickListener,
+ statusBarStateController,
+ gutsManager,
+ /*allowLongPress=*/ false,
+ onUserInteractionCallback,
+ falsingManager,
+ falsingCollector,
+ featureFlags,
+ peopleNotificationIdentifier,
+ Optional.of(bubblesManager),
+ dragController
+ )
+ }
+
+ @After
+ fun tearDown() {
+ disallowTestableLooperAsMainThread()
+ }
+
+ @Test
+ fun offerKeepInParent_parentDismissed() {
+ whenever(featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION))
+ .thenReturn(true)
+ whenever(view.isParentDismissed).thenReturn(true)
+
+ Assert.assertTrue(controller.offerToKeepInParentForAnimation())
+ Mockito.verify(view).setKeepInParentForDismissAnimation(true)
+ }
+
+ @Test
+ fun offerKeepInParent_parentNotDismissed() {
+ whenever(featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION))
+ .thenReturn(true)
+
+ Assert.assertFalse(controller.offerToKeepInParentForAnimation())
+ Mockito.verify(view, never()).setKeepInParentForDismissAnimation(anyBoolean())
+ }
+
+ @Test
+ fun removeFromParent_keptForAnimation() {
+ val parentView: ExpandableNotificationRow = mock()
+ whenever(view.notificationParent).thenReturn(parentView)
+ whenever(view.keepInParentForDismissAnimation()).thenReturn(true)
+
+ Assert.assertTrue(controller.removeFromParentIfKeptForAnimation())
+ Mockito.verify(parentView).removeChildNotification(view)
+ }
+
+ @Test
+ fun removeFromParent_notKeptForAnimation() {
+ val parentView: ExpandableNotificationRow = mock()
+ whenever(view.notificationParent).thenReturn(parentView)
+
+ Assert.assertFalse(controller.removeFromParentIfKeptForAnimation())
+ Mockito.verifyNoMoreInteractions(parentView)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
index ed2afe753a5e..915924f13197 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
@@ -41,7 +41,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
-import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerFake;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 5fd9448b129d..fb31bef909e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -301,7 +301,8 @@ public class NotificationTestHelper {
public ExpandableNotificationRow createBubble()
throws Exception {
Notification n = createNotification(false /* isGroupSummary */,
- null /* groupKey */, makeBubbleMetadata(null));
+ null /* groupKey */,
+ makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */));
n.flags |= FLAG_BUBBLE;
ExpandableNotificationRow row = generateRow(n, PKG, UID, USER_HANDLE,
mDefaultInflationFlags, IMPORTANCE_HIGH);
@@ -334,7 +335,8 @@ public class NotificationTestHelper {
public ExpandableNotificationRow createBubbleInGroup()
throws Exception {
Notification n = createNotification(false /* isGroupSummary */,
- GROUP_KEY /* groupKey */, makeBubbleMetadata(null));
+ GROUP_KEY /* groupKey */,
+ makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */));
n.flags |= FLAG_BUBBLE;
ExpandableNotificationRow row = generateRow(n, PKG, UID, USER_HANDLE,
mDefaultInflationFlags, IMPORTANCE_HIGH);
@@ -350,7 +352,7 @@ public class NotificationTestHelper {
* @param deleteIntent the intent to assign to {@link BubbleMetadata#deleteIntent}
*/
public NotificationEntry createBubble(@Nullable PendingIntent deleteIntent) {
- return createBubble(makeBubbleMetadata(deleteIntent), USER_HANDLE);
+ return createBubble(makeBubbleMetadata(deleteIntent, false /* autoExpand */), USER_HANDLE);
}
/**
@@ -359,7 +361,16 @@ public class NotificationTestHelper {
* @param handle the user to associate with this bubble.
*/
public NotificationEntry createBubble(UserHandle handle) {
- return createBubble(makeBubbleMetadata(null), handle);
+ return createBubble(makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */),
+ handle);
+ }
+
+ /**
+ * Returns an {@link NotificationEntry} that should be shown as a auto-expanded bubble.
+ */
+ public NotificationEntry createAutoExpandedBubble() {
+ return createBubble(makeBubbleMetadata(null /* deleteIntent */, true /* autoExpand */),
+ USER_HANDLE);
}
/**
@@ -567,7 +578,7 @@ public class NotificationTestHelper {
assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
}
- private BubbleMetadata makeBubbleMetadata(PendingIntent deleteIntent) {
+ private BubbleMetadata makeBubbleMetadata(PendingIntent deleteIntent, boolean autoExpand) {
Intent target = new Intent(mContext, BubblesTestActivity.class);
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target,
PendingIntent.FLAG_MUTABLE);
@@ -576,6 +587,7 @@ public class NotificationTestHelper {
Icon.createWithResource(mContext, R.drawable.android))
.setDeleteIntent(deleteIntent)
.setDesiredHeight(314)
+ .setAutoExpandBubble(autoExpand)
.build();
}
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 026c82eda42d..645052feee94 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
@@ -59,8 +59,10 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -119,6 +121,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private GroupExpansionManager mGroupExpansionManager;
@Mock private SectionHeaderController mSilentHeaderController;
@Mock private NotifPipeline mNotifPipeline;
+ @Mock private NotifPipelineFlags mNotifPipelineFlags;
@Mock private NotifCollection mNotifCollection;
@Mock private UiEventLogger mUiEventLogger;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@@ -170,12 +173,14 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mGroupExpansionManager,
mSilentHeaderController,
mNotifPipeline,
+ mNotifPipelineFlags,
mNotifCollection,
mLockscreenShadeTransitionController,
mShadeTransitionController,
mUiEventLogger,
mRemoteInputManager,
mVisibilityLocationProviderDelegator,
+ new SeenNotificationsProviderImpl(),
mShadeController,
mJankMonitor,
mStackLogger,
@@ -228,14 +233,16 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
- /* notifVisibleInShade= */ true);
+ /* notifVisibleInShade= */ true,
+ /* areSeenNotifsFiltered= */false);
setupShowEmptyShadeViewState(false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ false,
- /* notifVisibleInShade= */ true);
+ /* notifVisibleInShade= */ true,
+ /* areSeenNotifsFiltered= */false);
}
@Test
@@ -248,14 +255,16 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
- /* notifVisibleInShade= */ false);
+ /* notifVisibleInShade= */ false,
+ /* areSeenNotifsFiltered= */false);
setupShowEmptyShadeViewState(false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ false,
- /* notifVisibleInShade= */ false);
+ /* notifVisibleInShade= */ false,
+ /* areSeenNotifsFiltered= */false);
}
@Test
@@ -274,14 +283,16 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
- /* notifVisibleInShade= */ false);
+ /* notifVisibleInShade= */ false,
+ /* areSeenNotifsFiltered= */false);
mController.setQsFullScreen(true);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
- /* notifVisibleInShade= */ false);
+ /* notifVisibleInShade= */ false,
+ /* areSeenNotifsFiltered= */false);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index dceb4ff48125..07ea6304f7cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -324,7 +324,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
public void updateEmptyView_dndSuppressing() {
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, true);
+ mStackScroller.updateEmptyShadeView(true, true, false);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
}
@@ -334,7 +334,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, false);
+ mStackScroller.updateEmptyShadeView(true, false, false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
}
@@ -343,10 +343,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
public void updateEmptyView_noNotificationsToDndSuppressing() {
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, false);
+ mStackScroller.updateEmptyShadeView(true, false, false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
- mStackScroller.updateEmptyShadeView(true, true);
+ mStackScroller.updateEmptyShadeView(true, true, false);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index bf31eb287579..3fccd37d9d7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -136,7 +136,7 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
verify(mCentralSurfaces).updateQsExpansionEnabled();
- verify(mShadeController).animateCollapsePanels();
+ verify(mShadeController).animateCollapseShade();
// Trying to open it does nothing.
mSbcqCallbacks.animateExpandNotificationsPanel();
@@ -154,7 +154,7 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NONE, false);
verify(mCentralSurfaces).updateQsExpansionEnabled();
- verify(mShadeController, never()).animateCollapsePanels();
+ verify(mShadeController, never()).animateCollapseShade();
// Can now be opened.
mSbcqCallbacks.animateExpandNotificationsPanel();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 013e7278753d..521e51846834 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -106,6 +106,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.PluginDependencyProvider;
@@ -212,6 +213,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private NotificationPanelView mNotificationPanelView;
@Mock private IStatusBarService mBarService;
@Mock private IDreamManager mDreamManager;
+ @Mock private LightRevealScrimViewModel mLightRevealScrimViewModel;
@Mock private ScrimController mScrimController;
@Mock private DozeScrimController mDozeScrimController;
@Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@@ -392,10 +394,21 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
return null;
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
- mShadeController = spy(new ShadeControllerImpl(mCommandQueue,
- mStatusBarStateController, mNotificationShadeWindowController,
- mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> Optional.of(mCentralSurfaces), () -> mAssistManager));
+ mShadeController = spy(new ShadeControllerImpl(
+ mCommandQueue,
+ mKeyguardStateController,
+ mStatusBarStateController,
+ mStatusBarKeyguardViewManager,
+ mStatusBarWindowController,
+ mNotificationShadeWindowController,
+ mContext.getSystemService(WindowManager.class),
+ () -> mAssistManager,
+ () -> mNotificationGutsManager
+ ));
+ mShadeController.setNotificationPanelViewController(mNotificationPanelViewController);
+ mShadeController.setNotificationShadeWindowViewController(
+ mNotificationShadeWindowViewController);
+ mShadeController.setNotificationPresenter(mNotificationPresenter);
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
@@ -486,12 +499,14 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mDeviceStateManager,
mWiredChargingRippleController,
mDreamManager,
- mCameraLauncherLazy) {
+ mCameraLauncherLazy,
+ () -> mLightRevealScrimViewModel) {
@Override
protected ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
}
};
+ mCentralSurfaces.initShadeVisibilityListener();
when(mViewRootImpl.getOnBackInvokedDispatcher())
.thenReturn(mOnBackInvokedDispatcher);
when(mKeyguardViewMediator.registerCentralSurfaces(
@@ -807,7 +822,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true);
mOnBackInvokedCallback.getValue().onBackInvoked();
- verify(mShadeController).animateCollapsePanels();
+ verify(mShadeController).animateCollapseShade();
}
@Test
@@ -1030,7 +1045,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
- public void collapseShade_callsAnimateCollapsePanels_whenExpanded() {
+ public void collapseShade_callsanimateCollapseShade_whenExpanded() {
// GIVEN the shade is expanded
mCentralSurfaces.onShadeExpansionFullyChanged(true);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
@@ -1038,12 +1053,12 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
// WHEN collapseShade is called
mCentralSurfaces.collapseShade();
- // VERIFY that animateCollapsePanels is called
- verify(mShadeController).animateCollapsePanels();
+ // VERIFY that animateCollapseShade is called
+ verify(mShadeController).animateCollapseShade();
}
@Test
- public void collapseShade_doesNotCallAnimateCollapsePanels_whenCollapsed() {
+ public void collapseShade_doesNotCallanimateCollapseShade_whenCollapsed() {
// GIVEN the shade is collapsed
mCentralSurfaces.onShadeExpansionFullyChanged(false);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
@@ -1051,12 +1066,12 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
// WHEN collapseShade is called
mCentralSurfaces.collapseShade();
- // VERIFY that animateCollapsePanels is NOT called
- verify(mShadeController, never()).animateCollapsePanels();
+ // VERIFY that animateCollapseShade is NOT called
+ verify(mShadeController, never()).animateCollapseShade();
}
@Test
- public void collapseShadeForBugReport_callsAnimateCollapsePanels_whenFlagDisabled() {
+ public void collapseShadeForBugReport_callsanimateCollapseShade_whenFlagDisabled() {
// GIVEN the shade is expanded & flag enabled
mCentralSurfaces.onShadeExpansionFullyChanged(true);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
@@ -1065,12 +1080,12 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
// WHEN collapseShadeForBugreport is called
mCentralSurfaces.collapseShadeForBugreport();
- // VERIFY that animateCollapsePanels is called
- verify(mShadeController).animateCollapsePanels();
+ // VERIFY that animateCollapseShade is called
+ verify(mShadeController).animateCollapseShade();
}
@Test
- public void collapseShadeForBugReport_doesNotCallAnimateCollapsePanels_whenFlagEnabled() {
+ public void collapseShadeForBugReport_doesNotCallanimateCollapseShade_whenFlagEnabled() {
// GIVEN the shade is expanded & flag enabled
mCentralSurfaces.onShadeExpansionFullyChanged(true);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
@@ -1079,8 +1094,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
// WHEN collapseShadeForBugreport is called
mCentralSurfaces.collapseShadeForBugreport();
- // VERIFY that animateCollapsePanels is called
- verify(mShadeController, never()).animateCollapsePanels();
+ // VERIFY that animateCollapseShade is called
+ verify(mShadeController, never()).animateCollapseShade();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index d3b541899635..df7ee432e79e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -39,6 +39,7 @@ import static org.mockito.Mockito.when;
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -398,6 +399,8 @@ public class KeyguardBouncerTest extends SysuiTestCase {
@Test
public void testShow_delaysIfFaceAuthIsRunning() {
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
+ .thenReturn(true);
when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
mBouncer.show(true /* reset */);
@@ -410,9 +413,10 @@ public class KeyguardBouncerTest extends SysuiTestCase {
}
@Test
- public void testShow_doesNotDelaysIfFaceAuthIsLockedOut() {
+ public void testShow_doesNotDelaysIfFaceAuthIsNotAllowed() {
when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
- when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE))
+ .thenReturn(false);
mBouncer.show(true /* reset */);
verify(mHandler, never()).postDelayed(any(), anyLong());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index de71e2c250c4..e4759057a59c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1442,16 +1442,30 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void testNotificationTransparency_followsTransitionToFullShade() {
+ mScrimController.setClipsQsScrim(true);
+
mScrimController.transitionTo(SHADE_LOCKED);
mScrimController.setRawPanelExpansionFraction(1.0f);
finishAnimationsImmediately();
+
+ assertScrimTinted(Map.of(
+ mScrimInFront, false,
+ mScrimBehind, true,
+ mNotificationsScrim, false
+ ));
+
float shadeLockedAlpha = mNotificationsScrim.getViewAlpha();
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.setRawPanelExpansionFraction(1.0f);
finishAnimationsImmediately();
float keyguardAlpha = mNotificationsScrim.getViewAlpha();
- mScrimController.setClipsQsScrim(true);
+ assertScrimTinted(Map.of(
+ mScrimInFront, true,
+ mScrimBehind, true,
+ mNotificationsScrim, true
+ ));
+
float progress = 0.5f;
float lsNotifProgress = 0.3f;
mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index bf5186b6324d..e467d9399059 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -307,6 +307,17 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
+ public void onPanelExpansionChanged_neverTranslatesBouncerWhenOccluded() {
+ when(mKeyguardStateController.isOccluded()).thenReturn(true);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ expansionEvent(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false));
+ verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
+ }
+
+ @Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenShowBouncer() {
// Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
// the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index ce54d784520c..cae414a3dc67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -263,7 +263,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
while (!runnables.isEmpty()) runnables.remove(0).run();
// Then
- verify(mShadeController, atLeastOnce()).collapsePanel();
+ verify(mShadeController, atLeastOnce()).collapseShade();
verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(any(),
eq(false) /* animate */, any(), any());
@@ -296,7 +296,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
verify(mBubblesManager).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry()));
// This is called regardless, and simply short circuits when there is nothing to do.
- verify(mShadeController, atLeastOnce()).collapsePanel();
+ verify(mShadeController, atLeastOnce()).collapseShade();
verify(mAssistManager).hideAssist();
@@ -329,7 +329,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
// Then
verify(mBubblesManager).expandStackAndSelectBubble(eq(mBubbleNotificationRow.getEntry()));
- verify(mShadeController, atLeastOnce()).collapsePanel();
+ verify(mShadeController, atLeastOnce()).collapseShade();
verify(mAssistManager).hideAssist();
@@ -357,7 +357,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
// Then
verify(mBubblesManager).expandStackAndSelectBubble(mBubbleNotificationRow.getEntry());
- verify(mShadeController, atLeastOnce()).collapsePanel();
+ verify(mShadeController, atLeastOnce()).collapseShade();
verify(mAssistManager).hideAssist();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
index b7a6c0125cfa..d35ce76d7a9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
@@ -22,7 +22,7 @@ import android.os.UserHandle
import android.provider.Settings.Global
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
@@ -45,7 +45,7 @@ class AirplaneModeRepositoryImplTest : SysuiTestCase() {
private lateinit var underTest: AirplaneModeRepositoryImpl
- @Mock private lateinit var logger: ConnectivityPipelineLogger
+ @Mock private lateinit var logger: TableLogBuffer
private lateinit var bgHandler: Handler
private lateinit var scope: CoroutineScope
private lateinit var settings: FakeSettings
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
index 76016a121e68..5a6bb301743a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
@@ -38,9 +38,9 @@ import org.mockito.MockitoAnnotations
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-class AirplaneModeViewModelTest : SysuiTestCase() {
+class AirplaneModeViewModelImplTest : SysuiTestCase() {
- private lateinit var underTest: AirplaneModeViewModel
+ private lateinit var underTest: AirplaneModeViewModelImpl
@Mock private lateinit var logger: ConnectivityPipelineLogger
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -57,7 +57,7 @@ class AirplaneModeViewModelTest : SysuiTestCase() {
scope = CoroutineScope(IMMEDIATE)
underTest =
- AirplaneModeViewModel(
+ AirplaneModeViewModelImpl(
interactor,
logger,
scope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt
index 3d29d2b2c3c8..30fd308433e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt
@@ -18,8 +18,10 @@ package com.android.systemui.statusbar.pipeline.wifi.data.model
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Active.Companion.MIN_VALID_LEVEL
+import com.google.common.truth.Truth.assertThat
import org.junit.Test
@SmallTest
@@ -48,6 +50,125 @@ class WifiNetworkModelTest : SysuiTestCase() {
WifiNetworkModel.Active(NETWORK_ID, level = MAX_VALID_LEVEL + 1)
}
+ // Non-exhaustive logDiffs test -- just want to make sure the logging logic isn't totally broken
+
+ @Test
+ fun logDiffs_inactiveToActive_logsAllActiveFields() {
+ val logger = TestLogger()
+ val activeNetwork =
+ WifiNetworkModel.Active(
+ networkId = 5,
+ isValidated = true,
+ level = 3,
+ ssid = "Test SSID"
+ )
+
+ activeNetwork.logDiffs(prevVal = WifiNetworkModel.Inactive, logger)
+
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_ACTIVE))
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "5"))
+ assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
+ assertThat(logger.changes).contains(Pair(COL_LEVEL, "3"))
+ assertThat(logger.changes).contains(Pair(COL_SSID, "Test SSID"))
+ }
+ @Test
+ fun logDiffs_activeToInactive_resetsAllActiveFields() {
+ val logger = TestLogger()
+ val activeNetwork =
+ WifiNetworkModel.Active(
+ networkId = 5,
+ isValidated = true,
+ level = 3,
+ ssid = "Test SSID"
+ )
+
+ WifiNetworkModel.Inactive.logDiffs(prevVal = activeNetwork, logger)
+
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_INACTIVE))
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, NETWORK_ID_DEFAULT.toString()))
+ assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
+ assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
+ assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
+ }
+
+ @Test
+ fun logDiffs_carrierMergedToActive_logsAllActiveFields() {
+ val logger = TestLogger()
+ val activeNetwork =
+ WifiNetworkModel.Active(
+ networkId = 5,
+ isValidated = true,
+ level = 3,
+ ssid = "Test SSID"
+ )
+
+ activeNetwork.logDiffs(prevVal = WifiNetworkModel.CarrierMerged, logger)
+
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_ACTIVE))
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "5"))
+ assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
+ assertThat(logger.changes).contains(Pair(COL_LEVEL, "3"))
+ assertThat(logger.changes).contains(Pair(COL_SSID, "Test SSID"))
+ }
+ @Test
+ fun logDiffs_activeToCarrierMerged_resetsAllActiveFields() {
+ val logger = TestLogger()
+ val activeNetwork =
+ WifiNetworkModel.Active(
+ networkId = 5,
+ isValidated = true,
+ level = 3,
+ ssid = "Test SSID"
+ )
+
+ WifiNetworkModel.CarrierMerged.logDiffs(prevVal = activeNetwork, logger)
+
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED))
+ assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, NETWORK_ID_DEFAULT.toString()))
+ assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
+ assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
+ assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
+ }
+
+ @Test
+ fun logDiffs_activeChangesLevel_onlyLevelLogged() {
+ val logger = TestLogger()
+ val prevActiveNetwork =
+ WifiNetworkModel.Active(
+ networkId = 5,
+ isValidated = true,
+ level = 3,
+ ssid = "Test SSID"
+ )
+ val newActiveNetwork =
+ WifiNetworkModel.Active(
+ networkId = 5,
+ isValidated = true,
+ level = 2,
+ ssid = "Test SSID"
+ )
+
+ newActiveNetwork.logDiffs(prevActiveNetwork, logger)
+
+ assertThat(logger.changes).isEqualTo(listOf(Pair(COL_LEVEL, "2")))
+ }
+
+ private class TestLogger : TableRowLogger {
+ val changes = mutableListOf<Pair<String, String>>()
+
+ override fun logChange(columnName: String, value: String?) {
+ changes.add(Pair(columnName, value.toString()))
+ }
+
+ override fun logChange(columnName: String, value: Int) {
+ changes.add(Pair(columnName, value.toString()))
+ }
+
+ override fun logChange(columnName: String, value: Boolean) {
+ changes.add(Pair(columnName, value.toString()))
+ }
+ }
+
companion object {
private const val NETWORK_ID = 2
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
index a64a4bd2e57a..800f3c01c874 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
@@ -29,6 +29,7 @@ import android.net.wifi.WifiManager.TrafficStateCallback
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
@@ -69,6 +70,7 @@ class WifiRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock private lateinit var logger: ConnectivityPipelineLogger
+ @Mock private lateinit var tableLogger: TableLogBuffer
@Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var wifiManager: WifiManager
private lateinit var executor: Executor
@@ -804,6 +806,7 @@ class WifiRepositoryImplTest : SysuiTestCase() {
broadcastDispatcher,
connectivityManager,
logger,
+ tableLogger,
executor,
scope,
wifiManagerToUse,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 71b8bab87d19..b38497a7bbdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -35,8 +35,9 @@ import org.junit.Before
import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class)
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@SmallTest
-class WifiInteractorTest : SysuiTestCase() {
+class WifiInteractorImplTest : SysuiTestCase() {
private lateinit var underTest: WifiInteractor
@@ -47,7 +48,7 @@ class WifiInteractorTest : SysuiTestCase() {
fun setUp() {
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
- underTest = WifiInteractor(connectivityRepository, wifiRepository)
+ underTest = WifiInteractorImpl(connectivityRepository, wifiRepository)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 37457b308597..5c16e1295b65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -32,12 +32,14 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
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.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
@@ -86,9 +88,9 @@ class ModernStatusBarWifiViewTest : SysuiTestCase() {
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractor(connectivityRepository, wifiRepository)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(Dispatchers.Unconfined)
- airplaneModeViewModel = AirplaneModeViewModel(
+ airplaneModeViewModel = AirplaneModeViewModelImpl(
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index a1afcd71e3c3..3001b8162185 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
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.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -37,6 +38,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
@@ -81,10 +83,10 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractor(connectivityRepository, wifiRepository)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(IMMEDIATE)
airplaneModeViewModel =
- AirplaneModeViewModel(
+ AirplaneModeViewModelImpl(
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 7d2c56098584..6a6b2a801ab0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -23,6 +23,7 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
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.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -30,6 +31,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
import com.google.common.truth.Truth.assertThat
@@ -73,9 +75,9 @@ class WifiViewModelTest : SysuiTestCase() {
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractor(connectivityRepository, wifiRepository)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(IMMEDIATE)
- airplaneModeViewModel = AirplaneModeViewModel(
+ airplaneModeViewModel = AirplaneModeViewModelImpl(
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
index 5509a6cac1dd..03fd624a8c1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
@@ -124,8 +124,11 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
{ foldStateProvider.sendHingeAngleUpdate(90f) },
- { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) },
- { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_CLOSING) },
+ {
+ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+ // Start closing immediately after we opened, before the animation ended
+ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ },
{ foldStateProvider.sendHingeAngleUpdate(60f) },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 2e527be1af89..034c618e55d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -95,6 +95,24 @@ class UserRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun userSwitcherSettings_isUserSwitcherEnabled_notInitialized() = runSelfCancelingTest {
+ underTest = create(this)
+
+ var value: UserSwitcherSettingsModel? = null
+ underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
+
+ assertUserSwitcherSettings(
+ model = value,
+ expectedSimpleUserSwitcher = false,
+ expectedAddUsersFromLockscreen = false,
+ expectedUserSwitcherEnabled =
+ context.resources.getBoolean(
+ com.android.internal.R.bool.config_showUserSwitcherByDefault
+ ),
+ )
+ }
+
+ @Test
fun refreshUsers() = runSelfCancelingTest {
underTest = create(this)
val initialExpectedValue =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 50d239d25607..78b0cbe8c718 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -761,7 +761,7 @@ class UserInteractorTest : SysuiTestCase() {
}
@Test
- fun `users - secondary user - no guest user`() =
+ fun `users - secondary user - guest user can be switched to`() =
runBlocking(IMMEDIATE) {
val userInfos = createUserInfos(count = 3, includeGuest = true)
userRepository.setUserInfos(userInfos)
@@ -770,8 +770,8 @@ class UserInteractorTest : SysuiTestCase() {
var res: List<UserModel>? = null
val job = underTest.users.onEach { res = it }.launchIn(this)
- assertThat(res?.size == 2).isTrue()
- assertThat(res?.find { it.isGuest }).isNull()
+ assertThat(res?.size == 3).isTrue()
+ assertThat(res?.find { it.isGuest }).isNotNull()
job.cancel()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 4b6bdac46a3d..784a26bb371b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -47,18 +47,14 @@ import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -89,7 +85,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
- private lateinit var injectedScope: CoroutineScope
@Before
fun setUp() {
@@ -104,7 +99,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
testDispatcher = UnconfinedTestDispatcher()
testScope = TestScope(testDispatcher)
- injectedScope = CoroutineScope(testScope.coroutineContext + SupervisorJob())
userRepository = FakeUserRepository()
runBlocking {
userRepository.setSettings(
@@ -118,14 +112,14 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
powerRepository = FakePowerRepository()
val refreshUsersScheduler =
RefreshUsersScheduler(
- applicationScope = injectedScope,
+ applicationScope = testScope.backgroundScope,
mainDispatcher = testDispatcher,
repository = userRepository,
)
val guestUserInteractor =
GuestUserInteractor(
applicationContext = context,
- applicationScope = injectedScope,
+ applicationScope = testScope.backgroundScope,
mainDispatcher = testDispatcher,
backgroundDispatcher = testDispatcher,
manager = manager,
@@ -154,7 +148,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
},
manager = manager,
- applicationScope = injectedScope,
+ applicationScope = testScope.backgroundScope,
telephonyInteractor =
TelephonyInteractor(
repository = FakeTelephonyRepository(),
@@ -175,7 +169,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun users() = selfCancelingTest {
+ fun users() = testScope.runTest {
val userInfos =
listOf(
UserInfo(
@@ -210,26 +204,26 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
assertUserViewModel(
viewModel = userViewModels.last()[0],
viewKey = 0,
- name = "zero",
+ name = Text.Loaded("zero"),
isSelectionMarkerVisible = true,
)
assertUserViewModel(
viewModel = userViewModels.last()[1],
viewKey = 1,
- name = "one",
+ name = Text.Loaded("one"),
isSelectionMarkerVisible = false,
)
assertUserViewModel(
viewModel = userViewModels.last()[2],
viewKey = 2,
- name = "two",
+ name = Text.Loaded("two"),
isSelectionMarkerVisible = false,
)
job.cancel()
}
@Test
- fun `maximumUserColumns - few users`() = selfCancelingTest {
+ fun `maximumUserColumns - few users`() = testScope.runTest {
setUsers(count = 2)
val values = mutableListOf<Int>()
val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) }
@@ -240,7 +234,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `maximumUserColumns - many users`() = selfCancelingTest {
+ fun `maximumUserColumns - many users`() = testScope.runTest {
setUsers(count = 5)
val values = mutableListOf<Int>()
val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) }
@@ -250,7 +244,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `isOpenMenuButtonVisible - has actions - true`() = selfCancelingTest {
+ fun `isOpenMenuButtonVisible - has actions - true`() = testScope.runTest {
setUsers(2)
val isVisible = mutableListOf<Boolean>()
@@ -261,7 +255,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `isOpenMenuButtonVisible - no actions - false`() = selfCancelingTest {
+ fun `isOpenMenuButtonVisible - no actions - false`() = testScope.runTest {
val userInfos = setUsers(2)
userRepository.setSelectedUserInfo(userInfos[1])
keyguardRepository.setKeyguardShowing(true)
@@ -275,7 +269,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun menu() = selfCancelingTest {
+ fun menu() = testScope.runTest {
val isMenuVisible = mutableListOf<Boolean>()
val job = launch(testDispatcher) { underTest.isMenuVisible.toList(isMenuVisible) }
assertThat(isMenuVisible.last()).isFalse()
@@ -290,7 +284,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `menu actions`() = selfCancelingTest {
+ fun `menu actions`() = testScope.runTest {
setUsers(2)
val actions = mutableListOf<List<UserActionViewModel>>()
val job = launch(testDispatcher) { underTest.menu.toList(actions) }
@@ -309,7 +303,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `isFinishRequested - finishes when user is switched`() = selfCancelingTest {
+ fun `isFinishRequested - finishes when user is switched`() = testScope.runTest {
val userInfos = setUsers(count = 2)
val isFinishRequested = mutableListOf<Boolean>()
val job = launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
@@ -323,7 +317,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `isFinishRequested - finishes when the screen turns off`() = selfCancelingTest {
+ fun `isFinishRequested - finishes when the screen turns off`() = testScope.runTest {
setUsers(count = 2)
powerRepository.setInteractive(true)
val isFinishRequested = mutableListOf<Boolean>()
@@ -338,7 +332,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `isFinishRequested - finishes when cancel button is clicked`() = selfCancelingTest {
+ fun `isFinishRequested - finishes when cancel button is clicked`() = testScope.runTest {
setUsers(count = 2)
powerRepository.setInteractive(true)
val isFinishRequested = mutableListOf<Boolean>()
@@ -356,6 +350,93 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun `guest selected -- name is exit guest`() = testScope.runTest {
+ val userInfos =
+ listOf(
+ UserInfo(
+ /* id= */ 0,
+ /* name= */ "zero",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM,
+ ),
+ UserInfo(
+ /* id= */ 1,
+ /* name= */ "one",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_GUEST,
+ ),
+ )
+
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[1])
+
+ val userViewModels = mutableListOf<List<UserViewModel>>()
+ val job = launch(testDispatcher) { underTest.users.toList(userViewModels) }
+
+ assertThat(userViewModels.last()).hasSize(2)
+ assertUserViewModel(
+ viewModel = userViewModels.last()[0],
+ viewKey = 0,
+ name = Text.Loaded("zero"),
+ isSelectionMarkerVisible = false,
+ )
+ assertUserViewModel(
+ viewModel = userViewModels.last()[1],
+ viewKey = 1,
+ name = Text.Resource(
+ com.android.settingslib.R.string.guest_exit_quick_settings_button
+ ),
+ isSelectionMarkerVisible = true,
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `guest not selected -- name is guest`() = testScope.runTest {
+ val userInfos =
+ listOf(
+ UserInfo(
+ /* id= */ 0,
+ /* name= */ "zero",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM,
+ ),
+ UserInfo(
+ /* id= */ 1,
+ /* name= */ "one",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_GUEST,
+ ),
+ )
+
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ runCurrent()
+
+ val userViewModels = mutableListOf<List<UserViewModel>>()
+ val job = launch(testDispatcher) { underTest.users.toList(userViewModels) }
+
+ assertThat(userViewModels.last()).hasSize(2)
+ assertUserViewModel(
+ viewModel = userViewModels.last()[0],
+ viewKey = 0,
+ name = Text.Loaded("zero"),
+ isSelectionMarkerVisible = true,
+ )
+ assertUserViewModel(
+ viewModel = userViewModels.last()[1],
+ viewKey = 1,
+ name = Text.Loaded("one"),
+ isSelectionMarkerVisible = false,
+ )
+ job.cancel()
+ }
+
private suspend fun setUsers(count: Int): List<UserInfo> {
val userInfos =
(0 until count).map { index ->
@@ -384,26 +465,18 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
private fun assertUserViewModel(
viewModel: UserViewModel?,
viewKey: Int,
- name: String,
+ name: Text,
isSelectionMarkerVisible: Boolean,
) {
checkNotNull(viewModel)
assertThat(viewModel.viewKey).isEqualTo(viewKey)
- assertThat(viewModel.name).isEqualTo(Text.Loaded(name))
+ assertThat(viewModel.name).isEqualTo(name)
assertThat(viewModel.isSelectionMarkerVisible).isEqualTo(isSelectionMarkerVisible)
assertThat(viewModel.alpha)
.isEqualTo(LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA)
assertThat(viewModel.onClicked).isNotNull()
}
- private fun selfCancelingTest(
- block: suspend TestScope.() -> Unit,
- ): TestResult =
- testScope.runTest {
- block()
- injectedScope.coroutineContext[Job.Key]?.cancelAndJoin()
- }
-
companion object {
private const val SUPERVISED_USER_CREATION_PACKAGE = "com.some.package"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 7df707789290..6bfc2f1a8b8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -51,15 +51,11 @@ class PairwiseFlowTest : SysuiTestCase() {
)
}
- @Test
- fun notEnough() = runBlocking {
- assertThatFlow(flowOf(1).pairwise()).emitsNothing()
- }
+ @Test fun notEnough() = runBlocking { assertThatFlow(flowOf(1).pairwise()).emitsNothing() }
@Test
fun withInit() = runBlocking {
- assertThatFlow(flowOf(2).pairwise(initialValue = 1))
- .emitsExactly(WithPrev(1, 2))
+ assertThatFlow(flowOf(2).pairwise(initialValue = 1)).emitsExactly(WithPrev(1, 2))
}
@Test
@@ -68,25 +64,78 @@ class PairwiseFlowTest : SysuiTestCase() {
}
@Test
- fun withStateFlow() = runBlocking(Dispatchers.Main.immediate) {
- val state = MutableStateFlow(1)
- val stop = MutableSharedFlow<Unit>()
-
- val stoppable = merge(state, stop)
- .takeWhile { it is Int }
- .filterIsInstance<Int>()
+ fun withTransform() = runBlocking {
+ assertThatFlow(
+ flowOf("val1", "val2", "val3").pairwiseBy { prev: String, next: String ->
+ "$prev|$next"
+ }
+ )
+ .emitsExactly("val1|val2", "val2|val3")
+ }
- val job1 = launch {
- assertThatFlow(stoppable.pairwise()).emitsExactly(WithPrev(1, 2))
- }
- state.value = 2
- val job2 = launch { assertThatFlow(stoppable.pairwise()).emitsNothing() }
+ @Test
+ fun withGetInit() = runBlocking {
+ var initRun = false
+ assertThatFlow(
+ flowOf("val1", "val2").pairwiseBy(
+ getInitialValue = {
+ initRun = true
+ "initial"
+ }
+ ) { prev: String, next: String -> "$prev|$next" }
+ )
+ .emitsExactly("initial|val1", "val1|val2")
+ assertThat(initRun).isTrue()
+ }
- stop.emit(Unit)
+ @Test
+ fun notEnoughWithGetInit() = runBlocking {
+ var initRun = false
+ assertThatFlow(
+ emptyFlow<String>().pairwiseBy(
+ getInitialValue = {
+ initRun = true
+ "initial"
+ }
+ ) { prev: String, next: String -> "$prev|$next" }
+ )
+ .emitsNothing()
+ // Even though the flow will not emit anything, the initial value function should still get
+ // run.
+ assertThat(initRun).isTrue()
+ }
- assertThatJob(job1).isCompleted()
- assertThatJob(job2).isCompleted()
+ @Test
+ fun getInitNotRunWhenFlowNotCollected() = runBlocking {
+ var initRun = false
+ flowOf("val1", "val2").pairwiseBy(
+ getInitialValue = {
+ initRun = true
+ "initial"
+ }
+ ) { prev: String, next: String -> "$prev|$next" }
+
+ // Since the flow isn't collected, ensure [initialValueFun] isn't run.
+ assertThat(initRun).isFalse()
}
+
+ @Test
+ fun withStateFlow() =
+ runBlocking(Dispatchers.Main.immediate) {
+ val state = MutableStateFlow(1)
+ val stop = MutableSharedFlow<Unit>()
+
+ val stoppable = merge(state, stop).takeWhile { it is Int }.filterIsInstance<Int>()
+
+ val job1 = launch { assertThatFlow(stoppable.pairwise()).emitsExactly(WithPrev(1, 2)) }
+ state.value = 2
+ val job2 = launch { assertThatFlow(stoppable.pairwise()).emitsNothing() }
+
+ stop.emit(Unit)
+
+ assertThatJob(job1).isCompleted()
+ assertThatJob(job2).isCompleted()
+ }
}
@SmallTest
@@ -94,18 +143,17 @@ class PairwiseFlowTest : SysuiTestCase() {
class SetChangesFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
- assertThatFlow(
- flowOf(setOf(1, 2, 3), setOf(2, 3, 4)).setChanges()
- ).emitsExactly(
- SetChanges(
- added = setOf(1, 2, 3),
- removed = emptySet(),
- ),
- SetChanges(
- added = setOf(4),
- removed = setOf(1),
- ),
- )
+ assertThatFlow(flowOf(setOf(1, 2, 3), setOf(2, 3, 4)).setChanges())
+ .emitsExactly(
+ SetChanges(
+ added = setOf(1, 2, 3),
+ removed = emptySet(),
+ ),
+ SetChanges(
+ added = setOf(4),
+ removed = setOf(1),
+ ),
+ )
}
@Test
@@ -147,14 +195,19 @@ class SetChangesFlowTest : SysuiTestCase() {
class SampleFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
- assertThatFlow(flow { yield(); emit(1) }.sample(flowOf(2)) { a, b -> a to b })
+ assertThatFlow(
+ flow {
+ yield()
+ emit(1)
+ }
+ .sample(flowOf(2)) { a, b -> a to b }
+ )
.emitsExactly(1 to 2)
}
@Test
fun otherFlowNoValueYet() = runBlocking {
- assertThatFlow(flowOf(1).sample(emptyFlow<Unit>()))
- .emitsNothing()
+ assertThatFlow(flowOf(1).sample(emptyFlow<Unit>())).emitsNothing()
}
@Test
@@ -178,13 +231,14 @@ class SampleFlowTest : SysuiTestCase() {
}
}
-private fun <T> assertThatFlow(flow: Flow<T>) = object {
- suspend fun emitsExactly(vararg emissions: T) =
- assertThat(flow.toList()).containsExactly(*emissions).inOrder()
- suspend fun emitsNothing() =
- assertThat(flow.toList()).isEmpty()
-}
+private fun <T> assertThatFlow(flow: Flow<T>) =
+ object {
+ suspend fun emitsExactly(vararg emissions: T) =
+ assertThat(flow.toList()).containsExactly(*emissions).inOrder()
+ suspend fun emitsNothing() = assertThat(flow.toList()).isEmpty()
+ }
-private fun assertThatJob(job: Job) = object {
- fun isCompleted() = assertThat(job.isCompleted).isTrue()
-}
+private fun assertThatJob(job: Job) =
+ object {
+ fun isCompleted() = assertThat(job.isCompleted).isTrue()
+ }
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 d42f757e16bd..b9dfc27483b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -1423,13 +1423,43 @@ public class BubblesTest extends SysuiTestCase {
assertStackCollapsed();
// Post status bar state change update with the same value
mBubbleController.onStatusBarStateChanged(false);
- // Stack should remain collapsedb
+ // Stack should remain collapsed
assertStackCollapsed();
// Post status bar state change which should trigger bubble to expand
mBubbleController.onStatusBarStateChanged(true);
assertStackExpanded();
}
+ /**
+ * Test to verify behavior for the following scenario:
+ * <ol>
+ * <li>device is locked with keyguard on, status bar shade state updates to
+ * <code>false</code></li>
+ * <li>notification entry is marked to be a bubble and it is set to auto-expand</li>
+ * <li>device unlock starts, status bar shade state receives another update to
+ * <code>false</code></li>
+ * <li>device is unlocked and status bar shade state is set to <code>true</code></li>
+ * <li>bubble should be expanded</li>
+ * </ol>
+ */
+ @Test
+ public void testOnStatusBarStateChanged_newAutoExpandedBubbleRemainsExpanded() {
+ // Set device as locked
+ mBubbleController.onStatusBarStateChanged(false);
+
+ // Create a auto-expanded bubble
+ NotificationEntry entry = mNotificationTestHelper.createAutoExpandedBubble();
+ mEntryListener.onEntryAdded(entry);
+
+ // When unlocking, we may receive duplicate updates with shade=false, ensure they don't
+ // clear the expanded state
+ mBubbleController.onStatusBarStateChanged(false);
+ mBubbleController.onStatusBarStateChanged(true);
+
+ // After unlocking, stack should be expanded
+ assertStackExpanded();
+ }
+
@Test
public void testSetShouldAutoExpand_notifiesFlagChanged() {
mBubbleController.updateBubble(mBubbleEntry);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index fa3cc9905c3f..bf2235aa98a9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -136,6 +136,8 @@ public abstract class SysuiTestCase {
InstrumentationRegistry.getArguments());
if (TestableLooper.get(this) != null) {
TestableLooper.get(this).processAllMessages();
+ // Must remove static reference to this test object to prevent leak (b/261039202)
+ TestableLooper.remove(this);
}
disallowTestableLooperAsMainThread();
mContext.cleanUpReceivers(this.getClass().getSimpleName());
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt
new file mode 100644
index 000000000000..8176dd07b84a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+
+class FakeOverlapDetector : OverlapDetector {
+ var shouldReturn: Boolean = false
+
+ override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
+ return shouldReturn
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index 52e0c982cae0..ad086ff9c664 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -22,12 +22,12 @@ import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
import android.os.UserHandle
-import android.util.ArraySet
import android.util.Log
import com.android.systemui.SysuiTestableContext
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import com.android.systemui.settings.UserTracker
+import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executor
class FakeBroadcastDispatcher(
@@ -50,7 +50,7 @@ class FakeBroadcastDispatcher(
PendingRemovalStore(logger)
) {
- val registeredReceivers = ArraySet<BroadcastReceiver>()
+ val registeredReceivers: MutableSet<BroadcastReceiver> = ConcurrentHashMap.newKeySet()
override fun registerReceiverWithHandler(
receiver: BroadcastReceiver,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
new file mode 100644
index 000000000000..b7a8d2e9f684
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.coroutines
+
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+
+/** Collect [flow] in a new [Job] and return a getter for the last collected value. */
+fun <T> TestScope.collectLastValue(
+ flow: Flow<T>,
+ context: CoroutineContext = EmptyCoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+): () -> T? {
+ var lastValue: T? = null
+ backgroundScope.launch(context, start) { flow.collect { lastValue = it } }
+ return {
+ runCurrent()
+ lastValue
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
new file mode 100644
index 000000000000..d85dd2e7bc8a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+
+class FakeKeyguardQuickAffordanceProviderClientFactory(
+ private val userTracker: UserTracker,
+ private val callback: (Int) -> KeyguardQuickAffordanceProviderClient = {
+ FakeKeyguardQuickAffordanceProviderClient()
+ },
+) : KeyguardQuickAffordanceProviderClientFactory {
+
+ override fun create(): KeyguardQuickAffordanceProviderClient {
+ return callback(userTracker.userId)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 36016678fb91..5c2a915e81b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -17,11 +17,15 @@
package com.android.systemui.keyguard.data.repository
+import android.graphics.Point
import com.android.systemui.common.shared.model.Position
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -49,7 +53,7 @@ class FakeKeyguardRepository : KeyguardRepository {
override val isDreaming: Flow<Boolean> = _isDreaming
private val _dozeAmount = MutableStateFlow(0f)
- override val dozeAmount: Flow<Float> = _dozeAmount
+ override val linearDozeAmount: Flow<Float> = _dozeAmount
private val _statusBarState = MutableStateFlow(StatusBarState.SHADE)
override val statusBarState: Flow<StatusBarState> = _statusBarState
@@ -57,8 +61,16 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _dozeTransitionModel = MutableStateFlow(DozeTransitionModel())
override val dozeTransitionModel: Flow<DozeTransitionModel> = _dozeTransitionModel
- private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
- override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
+ private val _wakefulnessModel =
+ MutableStateFlow(
+ WakefulnessModel(
+ WakefulnessState.ASLEEP,
+ false,
+ WakeSleepReason.OTHER,
+ WakeSleepReason.OTHER
+ )
+ )
+ override val wakefulness: Flow<WakefulnessModel> = _wakefulnessModel
private val _isUdfpsSupported = MutableStateFlow(false)
@@ -71,6 +83,15 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE)
override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState
+ private val _fingerprintSensorLocation = MutableStateFlow<Point?>(null)
+ override val fingerprintSensorLocation: Flow<Point?> = _fingerprintSensorLocation
+
+ private val _faceSensorLocation = MutableStateFlow<Point?>(null)
+ override val faceSensorLocation: Flow<Point?> = _faceSensorLocation
+
+ private val _biometricUnlockSource = MutableStateFlow<BiometricUnlockSource?>(null)
+ override val biometricUnlockSource: Flow<BiometricUnlockSource?> = _biometricUnlockSource
+
override fun isKeyguardShowing(): Boolean {
return _isKeyguardShowing.value
}
@@ -99,6 +120,22 @@ class FakeKeyguardRepository : KeyguardRepository {
_dozeAmount.value = dozeAmount
}
+ fun setBiometricUnlockState(state: BiometricUnlockModel) {
+ _biometricUnlockState.tryEmit(state)
+ }
+
+ fun setBiometricUnlockSource(source: BiometricUnlockSource?) {
+ _biometricUnlockSource.tryEmit(source)
+ }
+
+ fun setFaceSensorLocation(location: Point?) {
+ _faceSensorLocation.tryEmit(location)
+ }
+
+ fun setFingerprintSensorLocation(location: Point?) {
+ _fingerprintSensorLocation.tryEmit(location)
+ }
+
override fun isUdfpsSupported(): Boolean {
return _isUdfpsSupported.value
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
new file mode 100644
index 000000000000..7c22604dc546
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import com.android.systemui.statusbar.LightRevealEffect
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of [LightRevealScrimRepository] */
+class FakeLightRevealScrimRepository : LightRevealScrimRepository {
+
+ private val _revealEffect: MutableStateFlow<LightRevealEffect> =
+ MutableStateFlow(DEFAULT_REVEAL_EFFECT)
+ override val revealEffect = _revealEffect
+
+ fun setRevealEffect(effect: LightRevealEffect) {
+ _revealEffect.tryEmit(effect)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
index 63448e236867..1a893f8c523c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
@@ -56,7 +56,8 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.GlobalSettings
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.TestCoroutineDispatcher
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
/**
* Util class to create real implementations of the FooterActions repositories, viewModel and
@@ -65,6 +66,7 @@ import kotlinx.coroutines.test.TestCoroutineDispatcher
class FooterActionsTestUtils(
private val context: Context,
private val testableLooper: TestableLooper,
+ private val scheduler: TestCoroutineScheduler,
) {
/** Enable or disable the user switcher in the settings. */
fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean, userId: Int) {
@@ -105,7 +107,7 @@ class FooterActionsTestUtils(
foregroundServicesRepository: ForegroundServicesRepository = foregroundServicesRepository(),
userSwitcherRepository: UserSwitcherRepository = userSwitcherRepository(),
broadcastDispatcher: BroadcastDispatcher = mock(),
- bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
+ bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
): FooterActionsInteractor {
return FooterActionsInteractorImpl(
activityStarter,
@@ -126,7 +128,7 @@ class FooterActionsTestUtils(
/** Create a [SecurityRepository] to be used in tests. */
fun securityRepository(
securityController: SecurityController = FakeSecurityController(),
- bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
+ bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
): SecurityRepository {
return SecurityRepositoryImpl(
securityController,
@@ -145,7 +147,7 @@ class FooterActionsTestUtils(
fun userSwitcherRepository(
@Application context: Context = this.context.applicationContext,
bgHandler: Handler = Handler(testableLooper.looper),
- bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
+ bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
userManager: UserManager = mock(),
userTracker: UserTracker = FakeUserTracker(),
userSwitcherController: UserSwitcherController = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index a7eadba60ddc..0dd1fc758a27 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -66,7 +66,8 @@ class FakeUserTracker(
_userId = _userInfo.id
_userHandle = UserHandle.of(_userId)
- callbacks.forEach { it.onUserChanged(_userId, userContext) }
+ val copy = callbacks.toList()
+ copy.forEach { it.onUserChanged(_userId, userContext) }
}
fun onProfileChanged() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeFlashlightController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
index f6fd2cb8f3b1..f68baf5546d7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
@@ -16,32 +16,71 @@ package com.android.systemui.utils.leaks;
import android.testing.LeakCheck;
+import androidx.annotation.VisibleForTesting;
+
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
+import java.util.ArrayList;
+import java.util.List;
+
public class FakeFlashlightController extends BaseLeakChecker<FlashlightListener>
implements FlashlightController {
+
+ private final List<FlashlightListener> callbacks = new ArrayList<>();
+
+ @VisibleForTesting
+ public boolean isAvailable;
+ @VisibleForTesting
+ public boolean isEnabled;
+ @VisibleForTesting
+ public boolean hasFlashlight;
+
public FakeFlashlightController(LeakCheck test) {
super(test, "flashlight");
}
+ @VisibleForTesting
+ public void onFlashlightAvailabilityChanged(boolean newValue) {
+ callbacks.forEach(
+ flashlightListener -> flashlightListener.onFlashlightAvailabilityChanged(newValue)
+ );
+ }
+
+ @VisibleForTesting
+ public void onFlashlightError() {
+ callbacks.forEach(FlashlightListener::onFlashlightError);
+ }
+
@Override
public boolean hasFlashlight() {
- return false;
+ return hasFlashlight;
}
@Override
public void setFlashlight(boolean newState) {
-
+ callbacks.forEach(flashlightListener -> flashlightListener.onFlashlightChanged(newState));
}
@Override
public boolean isAvailable() {
- return false;
+ return isAvailable;
}
@Override
public boolean isEnabled() {
- return false;
+ return isEnabled;
+ }
+
+ @Override
+ public void addCallback(FlashlightListener listener) {
+ super.addCallback(listener);
+ callbacks.add(listener);
+ }
+
+ @Override
+ public void removeCallback(FlashlightListener listener) {
+ super.removeCallback(listener);
+ callbacks.remove(listener);
}
}
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
index 0e4870af9930..a1e734dead77 100644
--- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -363,7 +363,9 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange
@Override
public void handleMessage(@NonNull Message msg) {
final int associationId = msg.what;
- onDeviceGone(mSimulated, associationId, /* sourceLoggingTag */ "simulated");
+ if (mSimulated.contains(associationId)) {
+ onDeviceGone(mSimulated, associationId, /* sourceLoggingTag */ "simulated");
+ }
}
}
}
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index 104d10de93c8..3487613d313c 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -19,6 +19,7 @@ package com.android.server;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.database.ContentObserver;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
@@ -73,6 +74,7 @@ final class DockObserver extends SystemService {
private final boolean mAllowTheaterModeWakeFromDock;
private final List<ExtconStateConfig> mExtconStateConfigs;
+ private DeviceProvisionedObserver mDeviceProvisionedObserver;
static final class ExtconStateProvider {
private final Map<String, String> mState;
@@ -110,7 +112,7 @@ final class DockObserver extends SystemService {
Slog.w(TAG, "No state file found at: " + stateFilePath);
return new ExtconStateProvider(new HashMap<>());
} catch (Exception e) {
- Slog.e(TAG, "" , e);
+ Slog.e(TAG, "", e);
return new ExtconStateProvider(new HashMap<>());
}
}
@@ -136,7 +138,7 @@ final class DockObserver extends SystemService {
private static List<ExtconStateConfig> loadExtconStateConfigs(Context context) {
String[] rows = context.getResources().getStringArray(
- com.android.internal.R.array.config_dockExtconStateMapping);
+ com.android.internal.R.array.config_dockExtconStateMapping);
try {
ArrayList<ExtconStateConfig> configs = new ArrayList<>();
for (String row : rows) {
@@ -167,6 +169,7 @@ final class DockObserver extends SystemService {
com.android.internal.R.bool.config_allowTheaterModeWakeFromDock);
mKeepDreamingWhenUndocking = context.getResources().getBoolean(
com.android.internal.R.bool.config_keepDreamingWhenUndocking);
+ mDeviceProvisionedObserver = new DeviceProvisionedObserver(mHandler);
mExtconStateConfigs = loadExtconStateConfigs(context);
@@ -199,15 +202,19 @@ final class DockObserver extends SystemService {
if (phase == PHASE_ACTIVITY_MANAGER_READY) {
synchronized (mLock) {
mSystemReady = true;
-
- // don't bother broadcasting undocked here
- if (mReportedDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
- updateLocked();
- }
+ mDeviceProvisionedObserver.onSystemReady();
+ updateIfDockedLocked();
}
}
}
+ private void updateIfDockedLocked() {
+ // don't bother broadcasting undocked here
+ if (mReportedDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ updateLocked();
+ }
+ }
+
private void setActualDockStateLocked(int newState) {
mActualDockState = newState;
if (!mUpdatesStopped) {
@@ -252,8 +259,7 @@ final class DockObserver extends SystemService {
// Skip the dock intent if not yet provisioned.
final ContentResolver cr = getContext().getContentResolver();
- if (Settings.Global.getInt(cr,
- Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
+ if (!mDeviceProvisionedObserver.isDeviceProvisioned()) {
Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
return;
}
@@ -302,6 +308,7 @@ final class DockObserver extends SystemService {
getContext(), soundUri);
if (sfx != null) {
sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+ sfx.preferBuiltinDevice(true);
sfx.play();
}
}
@@ -418,4 +425,48 @@ final class DockObserver extends SystemService {
}
}
}
+
+ private final class DeviceProvisionedObserver extends ContentObserver {
+ private boolean mRegistered;
+
+ public DeviceProvisionedObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ synchronized (mLock) {
+ updateRegistration();
+ if (isDeviceProvisioned()) {
+ // Send the dock broadcast if device is docked after provisioning.
+ updateIfDockedLocked();
+ }
+ }
+ }
+
+ void onSystemReady() {
+ updateRegistration();
+ }
+
+ private void updateRegistration() {
+ boolean register = !isDeviceProvisioned();
+ if (register == mRegistered) {
+ return;
+ }
+ final ContentResolver resolver = getContext().getContentResolver();
+ if (register) {
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, this);
+ } else {
+ resolver.unregisterContentObserver(this);
+ }
+ mRegistered = register;
+ }
+
+ boolean isDeviceProvisioned() {
+ return Settings.Global.getInt(getContext().getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 2ec744fd4bb8..c868f533bb7c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -248,6 +248,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runForceStop(pw);
case "stop-app":
return runStopApp(pw);
+ case "clear-recent-apps":
+ return runClearRecentApps(pw);
case "fgs-notification-rate-limit":
return runFgsNotificationRateLimit(pw);
case "crash":
@@ -1192,6 +1194,11 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
+ int runClearRecentApps(PrintWriter pw) throws RemoteException {
+ mTaskInterface.removeAllVisibleRecentTasks();
+ return 0;
+ }
+
int runFgsNotificationRateLimit(PrintWriter pw) throws RemoteException {
final String toggleValue = getNextArgRequired();
final boolean enable;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 4a01c61830d0..e8f25490e61a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3599,6 +3599,18 @@ public class AudioService extends IAudioService.Stub
}
}
+ // TODO enforce MODIFY_AUDIO_SYSTEM_SETTINGS when defined
+ private void enforceModifyAudioRoutingOrSystemSettingsPermission() {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ != PackageManager.PERMISSION_GRANTED
+ /*&& mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ != PackageManager.PERMISSION_DENIED*/) {
+ throw new SecurityException(
+ "Missing MODIFY_AUDIO_ROUTING or MODIFY_AUDIO_SYSTEM_SETTINGS permission");
+ }
+ }
+
private void enforceAccessUltrasoundPermission() {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3703,10 +3715,12 @@ public class AudioService extends IAudioService.Stub
}
/** @see AudioDeviceVolumeManager#setDeviceVolume(VolumeInfo, AudioDeviceAttributes)
- * Part of service interface, check permissions and parameters here */
+ * Part of service interface, check permissions and parameters here
+ * Note calling package is for logging purposes only, not to be trusted
+ */
public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada,
- @NonNull String callingPackage, @Nullable String attributionTag) {
- enforceModifyAudioRoutingPermission();
+ @NonNull String callingPackage) {
+ enforceModifyAudioRoutingOrSystemSettingsPermission();
Objects.requireNonNull(vi);
Objects.requireNonNull(ada);
Objects.requireNonNull(callingPackage);
@@ -3719,8 +3733,20 @@ public class AudioService extends IAudioService.Stub
return;
}
int index = vi.getVolumeIndex();
- if (index == VolumeInfo.INDEX_NOT_SET) {
- throw new IllegalArgumentException("changing device volume requires a volume index");
+ if (index == VolumeInfo.INDEX_NOT_SET && !vi.hasMuteCommand()) {
+ throw new IllegalArgumentException(
+ "changing device volume requires a volume index or mute command");
+ }
+
+ // TODO handle unmuting of current audio device
+ // if a stream is not muted but the VolumeInfo is for muting, set the volume index
+ // for the device to min volume
+ if (vi.hasMuteCommand() && vi.isMuted() && !isStreamMute(vi.getStreamType())) {
+ setStreamVolumeWithAttributionInt(vi.getStreamType(),
+ mStreamStates[vi.getStreamType()].getMinIndex(),
+ /*flags*/ 0,
+ ada, callingPackage, null);
+ return;
}
if (vi.getMinVolumeIndex() == VolumeInfo.INDEX_NOT_SET
@@ -3742,7 +3768,7 @@ public class AudioService extends IAudioService.Stub
}
}
setStreamVolumeWithAttributionInt(vi.getStreamType(), index, /*flags*/ 0,
- ada, callingPackage, attributionTag);
+ ada, callingPackage, null);
}
/** Retain API for unsupported app usage */
@@ -4648,6 +4674,40 @@ public class AudioService extends IAudioService.Stub
}
}
+ /**
+ * @see AudioDeviceVolumeManager#getDeviceVolume(VolumeInfo, AudioDeviceAttributes)
+ */
+ public @NonNull VolumeInfo getDeviceVolume(@NonNull VolumeInfo vi,
+ @NonNull AudioDeviceAttributes ada, @NonNull String callingPackage) {
+ enforceModifyAudioRoutingOrSystemSettingsPermission();
+ Objects.requireNonNull(vi);
+ Objects.requireNonNull(ada);
+ Objects.requireNonNull(callingPackage);
+ if (!vi.hasStreamType()) {
+ Log.e(TAG, "Unsupported non-stream type based VolumeInfo", new Exception());
+ return getDefaultVolumeInfo();
+ }
+
+ int streamType = vi.getStreamType();
+ final VolumeInfo.Builder vib = new VolumeInfo.Builder(vi);
+ vib.setMinVolumeIndex((mStreamStates[streamType].mIndexMin + 5) / 10);
+ vib.setMaxVolumeIndex((mStreamStates[streamType].mIndexMax + 5) / 10);
+ synchronized (VolumeStreamState.class) {
+ final int index;
+ if (isFixedVolumeDevice(ada.getInternalType())) {
+ index = (mStreamStates[streamType].mIndexMax + 5) / 10;
+ } else {
+ index = (mStreamStates[streamType].getIndex(ada.getInternalType()) + 5) / 10;
+ }
+ vib.setVolumeIndex(index);
+ // only set as a mute command if stream muted
+ if (mStreamStates[streamType].mIsMuted) {
+ vib.setMuted(true);
+ }
+ return vib.build();
+ }
+ }
+
/** @see AudioManager#getStreamMaxVolume(int) */
public int getStreamMaxVolume(int streamType) {
ensureValidStreamType(streamType);
@@ -4686,7 +4746,6 @@ public class AudioService extends IAudioService.Stub
sDefaultVolumeInfo = new VolumeInfo.Builder(AudioSystem.STREAM_MUSIC)
.setMinVolumeIndex(getStreamMinVolume(AudioSystem.STREAM_MUSIC))
.setMaxVolumeIndex(getStreamMaxVolume(AudioSystem.STREAM_MUSIC))
- .setMuted(false)
.build();
}
return sDefaultVolumeInfo;
@@ -6996,9 +7055,10 @@ public class AudioService extends IAudioService.Stub
private @AudioManager.DeviceVolumeBehavior
int getDeviceVolumeBehaviorInt(@NonNull AudioDeviceAttributes device) {
- // translate Java device type to native device type (for the devices masks for full / fixed)
- final int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice(
- device.getType());
+ // Get the internal type set by the AudioDeviceAttributes constructor which is always more
+ // exact (avoids double conversions) than a conversion from SDK type via
+ // AudioDeviceInfo.convertDeviceTypeToInternalDevice()
+ final int audioSystemDeviceOut = device.getInternalType();
int setDeviceVolumeBehavior = retrieveStoredDeviceVolumeBehavior(audioSystemDeviceOut);
if (setDeviceVolumeBehavior != AudioManager.DEVICE_VOLUME_BEHAVIOR_UNSET) {
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 94b67cedf86c..598e2b990ea5 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
@@ -452,13 +452,6 @@ public class FingerprintService extends SystemService {
return -1;
}
- if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
- // If this happens, something in KeyguardUpdateMonitor is wrong. This should only
- // ever be invoked when the user is encrypted or lockdown.
- Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown");
- return -1;
- }
-
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for detectFingerprint");
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a92b65ebeb6f..4d44c886fa22 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3796,13 +3796,13 @@ public class NotificationManagerService extends SystemService {
}
private void createNotificationChannelsImpl(String pkg, int uid,
- ParceledListSlice channelsList, boolean fromTargetApp) {
- createNotificationChannelsImpl(pkg, uid, channelsList, fromTargetApp,
+ ParceledListSlice channelsList) {
+ createNotificationChannelsImpl(pkg, uid, channelsList,
ActivityTaskManager.INVALID_TASK_ID);
}
private void createNotificationChannelsImpl(String pkg, int uid,
- ParceledListSlice channelsList, boolean fromTargetApp, int startingTaskId) {
+ ParceledListSlice channelsList, int startingTaskId) {
List<NotificationChannel> channels = channelsList.getList();
final int channelsSize = channels.size();
ParceledListSlice<NotificationChannel> oldChannels =
@@ -3814,7 +3814,7 @@ public class NotificationManagerService extends SystemService {
final NotificationChannel channel = channels.get(i);
Objects.requireNonNull(channel, "channel in list is null");
needsPolicyFileChange = mPreferencesHelper.createNotificationChannel(pkg, uid,
- channel, fromTargetApp,
+ channel, true /* fromTargetApp */,
mConditionProviders.isPackageOrComponentAllowed(
pkg, UserHandle.getUserId(uid)));
if (needsPolicyFileChange) {
@@ -3850,7 +3850,6 @@ public class NotificationManagerService extends SystemService {
@Override
public void createNotificationChannels(String pkg, ParceledListSlice channelsList) {
checkCallerIsSystemOrSameApp(pkg);
- boolean fromTargetApp = !isCallerSystemOrPhone(); // if not system, it's from the app
int taskId = ActivityTaskManager.INVALID_TASK_ID;
try {
int uid = mPackageManager.getPackageUid(pkg, 0,
@@ -3859,15 +3858,14 @@ public class NotificationManagerService extends SystemService {
} catch (RemoteException e) {
// Do nothing
}
- createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList, fromTargetApp,
- taskId);
+ createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList, taskId);
}
@Override
public void createNotificationChannelsForPackage(String pkg, int uid,
ParceledListSlice channelsList) {
enforceSystemOrSystemUI("only system can call this");
- createNotificationChannelsImpl(pkg, uid, channelsList, false /* fromTargetApp */);
+ createNotificationChannelsImpl(pkg, uid, channelsList);
}
@Override
@@ -3882,8 +3880,7 @@ public class NotificationManagerService extends SystemService {
CONVERSATION_CHANNEL_ID_FORMAT, parentId, conversationId));
conversationChannel.setConversationId(parentId, conversationId);
createNotificationChannelsImpl(
- pkg, uid, new ParceledListSlice(Arrays.asList(conversationChannel)),
- false /* fromTargetApp */);
+ pkg, uid, new ParceledListSlice(Arrays.asList(conversationChannel)));
mRankingHandler.requestSort();
handleSavePolicyFile();
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 97911581c59c..d8aa469bcd81 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -916,7 +916,7 @@ public class PreferencesHelper implements RankingConfig {
throw new IllegalArgumentException("Reserved id");
}
NotificationChannel existing = r.channels.get(channel.getId());
- if (existing != null) {
+ if (existing != null && fromTargetApp) {
// Actually modifying an existing channel - keep most of the existing settings
if (existing.isDeleted()) {
// The existing channel was deleted - undelete it.
@@ -1002,7 +1002,9 @@ public class PreferencesHelper implements RankingConfig {
}
if (fromTargetApp) {
channel.setLockscreenVisibility(r.visibility);
- channel.setAllowBubbles(NotificationChannel.DEFAULT_ALLOW_BUBBLE);
+ channel.setAllowBubbles(existing != null
+ ? existing.getAllowBubbles()
+ : NotificationChannel.DEFAULT_ALLOW_BUBBLE);
}
clearLockedFieldsLocked(channel);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 0d031333443e..b1c986e6558a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2197,6 +2197,15 @@ public final class PowerManagerService extends SystemService
if (sQuiescent) {
mDirty |= DIRTY_QUIESCENT;
}
+ PowerGroup defaultGroup = mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP);
+ if (defaultGroup.getWakefulnessLocked() == WAKEFULNESS_DOZING) {
+ // Workaround for b/187231320 where the AOD can get stuck in a "half on /
+ // half off" state when a non-default-group VirtualDisplay causes the global
+ // wakefulness to change to awake, even though the default display is
+ // dozing. We set sandman summoned to restart dreaming to get it unstuck.
+ // TODO(b/255688811) - fix this so that AOD never gets interrupted at all.
+ defaultGroup.setSandmanSummonedLocked(true);
+ }
break;
case WAKEFULNESS_ASLEEP:
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 4b8c7c176fda..36293d518f51 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -107,6 +107,7 @@ public class TrustAgentWrapper {
// Trust state
private boolean mTrusted;
private boolean mWaitingForTrustableDowngrade = false;
+ private boolean mWithinSecurityLockdownWindow = false;
private boolean mTrustable;
private CharSequence mMessage;
private boolean mDisplayTrustGrantedMessage;
@@ -160,6 +161,7 @@ public class TrustAgentWrapper {
mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0;
if ((flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) {
mWaitingForTrustableDowngrade = true;
+ setSecurityWindowTimer();
} else {
mWaitingForTrustableDowngrade = false;
}
@@ -452,6 +454,9 @@ public class TrustAgentWrapper {
if (mBound) {
scheduleRestart();
}
+ if (mWithinSecurityLockdownWindow) {
+ mTrustManagerService.lockUser(mUserId);
+ }
// mTrustDisabledByDpm maintains state
}
};
@@ -673,6 +678,22 @@ public class TrustAgentWrapper {
}
}
+ private void setSecurityWindowTimer() {
+ mWithinSecurityLockdownWindow = true;
+ long expiration = SystemClock.elapsedRealtime() + (15 * 1000); // timer for 15 seconds
+ mAlarmManager.setExact(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ expiration,
+ TAG,
+ new AlarmManager.OnAlarmListener() {
+ @Override
+ public void onAlarm() {
+ mWithinSecurityLockdownWindow = false;
+ }
+ },
+ Handler.getMain());
+ }
+
public boolean isManagingTrust() {
return mManagingTrust && !mTrustDisabledByDpm;
}
@@ -691,7 +712,6 @@ public class TrustAgentWrapper {
public void destroy() {
mHandler.removeMessages(MSG_RESTART_TIMEOUT);
-
if (!mBound) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 741141d9e463..da6e7e8436eb 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -580,17 +580,18 @@ class ActivityClientController extends IActivityClientController.Stub {
}
/**
- * Returns the windowing mode of the task that hosts the activity, or {@code -1} if task is not
- * found.
+ * Returns the {@link Configuration} of the task which hosts the Activity, or {@code null} if
+ * the task {@link Configuration} cannot be obtained.
*/
@Override
- public int getTaskWindowingMode(IBinder activityToken) {
+ @Nullable
+ public Configuration getTaskConfiguration(IBinder activityToken) {
synchronized (mGlobalLock) {
final ActivityRecord ar = ActivityRecord.isInAnyTask(activityToken);
if (ar == null) {
- return -1;
+ return null;
}
- return ar.getTask().getWindowingMode();
+ return ar.getTask().getConfiguration();
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d499858024f2..e776e68f108c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8928,9 +8928,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN)
- && getParent().getConfiguration().orientation == ORIENTATION_PORTRAIT
- && getParent().getWindowConfiguration().getWindowingMode()
- == WINDOWING_MODE_FULLSCREEN) {
+ && isParentFullscreenPortrait()) {
// We are using the parent configuration here as this is the most recent one that gets
// passed to onConfigurationChanged when a relevant change takes place
return info.getMinAspectRatio();
@@ -8953,6 +8951,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return info.getMinAspectRatio();
}
+ private boolean isParentFullscreenPortrait() {
+ final WindowContainer parent = getParent();
+ return parent != null
+ && parent.getConfiguration().orientation == ORIENTATION_PORTRAIT
+ && parent.getWindowConfiguration().getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+ }
+
/**
* Returns true if the activity has maximum or minimum aspect ratio.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 9f502ab6ca10..e1c3cbf94bd4 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -556,47 +556,52 @@ public class ActivityStartController {
final Task rootTask = mService.mRootWindowContainer.getDefaultTaskDisplayArea()
.getRootTask(WINDOWING_MODE_UNDEFINED, activityType);
if (rootTask == null) return false;
+ final RemoteTransition remote = options.getRemoteTransition();
final ActivityRecord r = rootTask.topRunningActivity();
- if (r == null || r.mVisibleRequested || !r.attachedToProcess()
+ if (r == null || r.mVisibleRequested || !r.attachedToProcess() || remote == null
|| !r.mActivityComponent.equals(intent.getComponent())
// Recents keeps invisible while device is locked.
|| r.mDisplayContent.isKeyguardLocked()) {
return false;
}
mService.mRootWindowContainer.startPowerModeLaunchIfNeeded(true /* forceSend */, r);
- final RemoteTransition remote = options.getRemoteTransition();
- if (remote != null && rootTask.mTransitionController.isCollecting()) {
- final Transition transition = new Transition(WindowManager.TRANSIT_TO_FRONT,
- 0 /* flags */, rootTask.mTransitionController,
- mService.mWindowManager.mSyncEngine);
+ final ActivityMetricsLogger.LaunchingState launchingState =
+ mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
+ final Transition transition = new Transition(WindowManager.TRANSIT_TO_FRONT,
+ 0 /* flags */, r.mTransitionController, mService.mWindowManager.mSyncEngine);
+ if (r.mTransitionController.isCollecting()) {
// Special case: we are entering recents while an existing transition is running. In
// this case, we know it's safe to "defer" the activity launch, so lets do so now so
// that it can get its own transition and thus update launcher correctly.
mService.mWindowManager.mSyncEngine.queueSyncSet(
- () -> rootTask.mTransitionController.moveToCollecting(transition),
() -> {
- final Task task = r.getTask();
- task.mTransitionController.requestStartTransition(transition,
- task, remote, null /* displayChange */);
- task.mTransitionController.collect(task);
- startExistingRecentsIfPossibleInner(intent, options, r, task, rootTask);
+ if (r.isAttached()) {
+ r.mTransitionController.moveToCollecting(transition);
+ }
+ },
+ () -> {
+ if (r.isAttached() && transition.isCollecting()) {
+ startExistingRecentsIfPossibleInner(options, r, rootTask,
+ launchingState, remote, transition);
+ }
});
} else {
- final Task task = r.getTask();
- task.mTransitionController.requestTransitionIfNeeded(WindowManager.TRANSIT_TO_FRONT,
- 0 /* flags */, task, task /* readyGroupRef */,
- options.getRemoteTransition(), null /* displayChange */);
- startExistingRecentsIfPossibleInner(intent, options, r, task, rootTask);
+ r.mTransitionController.moveToCollecting(transition);
+ startExistingRecentsIfPossibleInner(options, r, rootTask, launchingState, remote,
+ transition);
}
return true;
}
- void startExistingRecentsIfPossibleInner(Intent intent, ActivityOptions options,
- ActivityRecord r, Task task, Task rootTask) {
- final ActivityMetricsLogger.LaunchingState launchingState =
- mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
+ private void startExistingRecentsIfPossibleInner(ActivityOptions options, ActivityRecord r,
+ Task rootTask, ActivityMetricsLogger.LaunchingState launchingState,
+ RemoteTransition remoteTransition, Transition transition) {
+ final Task task = r.getTask();
mService.deferWindowLayout();
try {
+ r.mTransitionController.requestStartTransition(transition,
+ task, remoteTransition, null /* displayChange */);
+ r.mTransitionController.collect(task);
r.mTransitionController.setTransientLaunch(r,
TaskDisplayArea.getRootTaskAbove(rootTask));
task.moveToFront("startExistingRecents");
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d2422f378d81..27869c7cd44b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -55,7 +55,9 @@ import static android.content.pm.ActivityInfo.launchModeToString;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.INVALID_UID;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
@@ -1665,9 +1667,9 @@ class ActivityStarter {
&& transitionController.getTransitionPlayer() != null)
? transitionController.createTransition(TRANSIT_OPEN) : null;
RemoteTransition remoteTransition = r.takeRemoteTransition();
- transitionController.collect(r);
try {
mService.deferWindowLayout();
+ transitionController.collect(r);
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
@@ -2397,6 +2399,11 @@ class ActivityStarter {
if (actuallyMoved) {
// Only record if the activity actually moved.
mMovedToTopActivity = act;
+ if (mNoAnimation) {
+ act.mDisplayContent.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ act.mDisplayContent.prepareAppTransition(TRANSIT_TO_FRONT);
+ }
}
act.updateOptionsLocked(mOptions);
deliverNewIntent(act, intentGrants);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 7e93109dcb6d..abaa3630ff7b 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -268,6 +268,7 @@ public class AppTransitionController {
handleClosingApps();
handleOpeningApps();
handleChangingApps(transit);
+ handleClosingChangingContainers();
appTransition.setLastAppTransition(transit, topOpeningApp,
topClosingApp, topChangingApp);
@@ -287,6 +288,7 @@ public class AppTransitionController {
mDisplayContent.mClosingApps.clear();
mDisplayContent.mChangingContainers.clear();
mDisplayContent.mUnknownAppVisibilityController.clear();
+ mDisplayContent.mClosingChangingContainers.clear();
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
@@ -1171,6 +1173,24 @@ public class AppTransitionController {
}
}
+ private void handleClosingChangingContainers() {
+ final ArrayMap<WindowContainer, Rect> containers =
+ mDisplayContent.mClosingChangingContainers;
+ while (!containers.isEmpty()) {
+ final WindowContainer container = containers.keyAt(0);
+ containers.remove(container);
+
+ // For closing changing windows that are part of the transition, they should have been
+ // removed from mClosingChangingContainers in WindowContainer#getAnimationAdapter()
+ // If the closing changing TaskFragment is not part of the transition, update its
+ // surface after removing it from mClosingChangingContainers.
+ final TaskFragment taskFragment = container.asTaskFragment();
+ if (taskFragment != null) {
+ taskFragment.updateOrganizedTaskFragmentSurface();
+ }
+ }
+ }
+
private void handleChangingApps(@TransitionOldType int transit) {
final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers;
final int appsCount = apps.size();
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index d3452277a29f..cd26e2eb9c53 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -226,6 +226,9 @@ class BLASTSyncEngine {
}
private void setReady(boolean ready) {
+ if (mReady == ready) {
+ return;
+ }
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Set ready", mSyncId);
mReady = ready;
if (!ready) return;
@@ -239,7 +242,9 @@ class BLASTSyncEngine {
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Adding to group: %s", mSyncId, wc);
wc.setSyncGroup(this);
wc.prepareSync();
- mWm.mWindowPlacerLocked.requestTraversal();
+ if (mReady) {
+ mWm.mWindowPlacerLocked.requestTraversal();
+ }
}
void onCancelSync(WindowContainer wc) {
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index b033dca465f4..a32e46078d82 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -341,7 +341,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
if (childArea == null) {
continue;
}
- pw.println(prefix + "* " + childArea.getName());
+ pw.print(prefix + "* " + childArea.getName());
+ if (childArea.isOrganized()) {
+ pw.print(" (organized)");
+ }
+ pw.println();
if (childArea.isTaskDisplayArea()) {
// TaskDisplayArea can only contain task. And it is already printed by display.
continue;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3a936a5d7378..b33a83dac6e5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -192,6 +192,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.DisplayUtils;
@@ -348,6 +349,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
final ArraySet<WindowContainer> mChangingContainers = new ArraySet<>();
final UnknownAppVisibilityController mUnknownAppVisibilityController;
+ /**
+ * If a container is closing when resizing, keeps track of its starting bounds when it is
+ * removed from {@link #mChangingContainers}.
+ */
+ final ArrayMap<WindowContainer, Rect> mClosingChangingContainers = new ArrayMap<>();
private MetricsLogger mMetricsLogger;
@@ -3353,7 +3359,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
- controller.mTransitionMetricsReporter.associate(t,
+ controller.mTransitionMetricsReporter.associate(t.getToken(),
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
startAsyncRotation(false /* shouldDebounce */);
}
@@ -3458,9 +3464,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
- super.dump(pw, prefix, dumpAll);
pw.print(prefix);
- pw.println("Display: mDisplayId=" + mDisplayId + " rootTasks=" + getRootTaskCount());
+ pw.println("Display: mDisplayId=" + mDisplayId + (isOrganized() ? " (organized)" : ""));
final String subPrefix = " " + prefix;
pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
@@ -3491,6 +3496,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
pw.println(" mTouchExcludeRegion=" + mTouchExcludeRegion);
pw.println();
+ super.dump(pw, prefix, dumpAll);
pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
pw.print(" mCurrentFocus="); pw.println(mCurrentFocus);
@@ -3582,6 +3588,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
pw.println();
mInsetsStateController.dump(prefix, pw);
mDwpcHelper.dump(prefix, pw);
+ pw.println();
}
@Override
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 74a236bd862c..6edb63c14c64 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -87,10 +87,6 @@ final class LetterboxUiController {
private final LetterboxConfiguration mLetterboxConfiguration;
private final ActivityRecord mActivityRecord;
- // Taskbar expanded height. Used to determine whether to crop an app window to display rounded
- // corners above the taskbar.
- private final float mExpandedTaskBarHeight;
-
private boolean mShowWallpaperForLetterboxBackground;
@Nullable
@@ -102,8 +98,6 @@ final class LetterboxUiController {
// is created in its constructor. It shouldn't be used in this constructor but it's safe
// to use it after since controller is only used in ActivityRecord.
mActivityRecord = activityRecord;
- mExpandedTaskBarHeight =
- getResources().getDimensionPixelSize(R.dimen.taskbar_frame_height);
}
/** Cleans up {@link Letterbox} if it exists.*/
@@ -285,14 +279,17 @@ final class LetterboxUiController {
}
float getSplitScreenAspectRatio() {
+ // Getting the same aspect ratio that apps get in split screen.
+ final DisplayContent displayContent = mActivityRecord.getDisplayContent();
+ if (displayContent == null) {
+ return getDefaultMinAspectRatioForUnresizableApps();
+ }
int dividerWindowWidth =
getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_thickness);
int dividerInsets =
getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_insets);
int dividerSize = dividerWindowWidth - dividerInsets * 2;
-
- // Getting the same aspect ratio that apps get in split screen.
- Rect bounds = new Rect(mActivityRecord.getDisplayContent().getBounds());
+ final Rect bounds = new Rect(displayContent.getBounds());
if (bounds.width() >= bounds.height()) {
bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
bounds.right = bounds.centerX();
@@ -555,7 +552,6 @@ final class LetterboxUiController {
final InsetsSource taskbarInsetsSource = getTaskbarInsetsSource(mainWindow);
return taskbarInsetsSource != null
- && taskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight
&& taskbarInsetsSource.isVisible();
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 95371a52410f..4c5d607ef7be 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -103,10 +103,29 @@ class RemoteAnimationController implements DeathRecipient {
RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
Point position, Rect localBounds, Rect endBounds, Rect startBounds,
boolean showBackdrop) {
+ return createRemoteAnimationRecord(windowContainer, position, localBounds, endBounds,
+ startBounds, showBackdrop, startBounds != null /* shouldCreateSnapshot */);
+ }
+
+ /**
+ * Creates an animation record for each individual {@link WindowContainer}.
+ *
+ * @param windowContainer The windows to animate.
+ * @param position The position app bounds relative to its parent.
+ * @param localBounds The bounds of the app relative to its parent.
+ * @param endBounds The end bounds after the transition, in screen coordinates.
+ * @param startBounds The start bounds before the transition, in screen coordinates.
+ * @param showBackdrop To show background behind a window during animation.
+ * @param shouldCreateSnapshot Whether this target should create a snapshot animation.
+ * @return The record representing animation(s) to run on the app.
+ */
+ RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
+ Point position, Rect localBounds, Rect endBounds, Rect startBounds,
+ boolean showBackdrop, boolean shouldCreateSnapshot) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
windowContainer);
final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
- localBounds, endBounds, startBounds, showBackdrop);
+ localBounds, endBounds, startBounds, showBackdrop, shouldCreateSnapshot);
mPendingAnimations.add(adapters);
return adapters;
}
@@ -438,14 +457,15 @@ class RemoteAnimationController implements DeathRecipient {
private @RemoteAnimationTarget.Mode int mMode = RemoteAnimationTarget.MODE_CHANGING;
RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
- Rect endBounds, Rect startBounds, boolean showBackdrop) {
+ Rect endBounds, @Nullable Rect startBounds, boolean showBackdrop,
+ boolean shouldCreateSnapshot) {
mWindowContainer = windowContainer;
mShowBackdrop = showBackdrop;
if (startBounds != null) {
mStartBounds = new Rect(startBounds);
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
mStartBounds, mShowBackdrop);
- if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
+ if (shouldCreateSnapshot && mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
final Rect thumbnailLocalBounds = new Rect(startBounds);
thumbnailLocalBounds.offsetTo(0, 0);
// Snapshot is located at (0,0) of the animation leash. It doesn't have size
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2866f423dc07..89cad9c53464 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3457,7 +3457,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final DisplayContent display = getChildAt(i);
display.dump(pw, prefix, dumpAll);
}
- pw.println();
}
/**
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index f3670e49f01e..1ae7816ecd24 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -440,6 +440,7 @@ class SurfaceAnimationRunner {
.setPixelFormat(PixelFormat.RGBA_8888)
.setChildrenOnly(true)
.setAllowProtected(true)
+ .setCaptureSecureLayers(true)
.build();
final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
SurfaceControl.captureLayers(captureArgs);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index eac41eac1d03..7eb9c16a25ff 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1149,8 +1149,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
next.delayedResume = false;
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ // If we are currently pausing an activity, then don't do anything until that is done.
+ final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
+ if (!allPausedComplete) {
+ ProtoLog.v(WM_DEBUG_STATES,
+ "resumeTopActivity: Skip resume: some activity pausing.");
+ return false;
+ }
+
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
// If the top activity is the resumed one, nothing to do.
if (mResumedActivity == next && next.isState(RESUMED)
&& taskDisplayArea.allResumedActivitiesComplete()) {
@@ -1173,14 +1181,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
- // If we are currently pausing an activity, then don't do anything until that is done.
- final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
- if (!allPausedComplete) {
- ProtoLog.v(WM_DEBUG_STATES,
- "resumeTopActivity: Skip resume: some activity pausing.");
- return false;
- }
-
// If we are sleeping, and there is no resumed activity, and the top activity is paused,
// well that is the state we want.
if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
@@ -2329,11 +2329,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
super.onConfigurationChanged(newParentConfig);
-
- if (mTaskFragmentOrganizer != null) {
- updateOrganizedTaskFragmentSurface();
- }
-
+ updateOrganizedTaskFragmentSurface();
sendTaskFragmentInfoChanged();
}
@@ -2346,8 +2342,13 @@ class TaskFragment extends WindowContainer<WindowContainer> {
updateOrganizedTaskFragmentSurface();
}
- private void updateOrganizedTaskFragmentSurface() {
- if (mDelayOrganizedTaskFragmentSurfaceUpdate) {
+ /**
+ * TaskFragmentOrganizer doesn't have access to the surface for security reasons, so we need to
+ * update its surface on the server side if it is not collected for Shell or in pending
+ * animation.
+ */
+ void updateOrganizedTaskFragmentSurface() {
+ if (mDelayOrganizedTaskFragmentSurfaceUpdate || mTaskFragmentOrganizer == null) {
return;
}
if (mTransitionController.isShellTransitionsEnabled()
@@ -2379,7 +2380,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return;
}
- final Rect bounds = getBounds();
+ // If this TaskFragment is closing while resizing, crop to the starting bounds instead.
+ final Rect bounds = isClosingWhenResizing()
+ ? mDisplayContent.mClosingChangingContainers.get(this)
+ : getBounds();
final int width = bounds.width();
final int height = bounds.height();
if (!forceUpdate && width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
@@ -2427,6 +2431,15 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|| endBounds.height() != startBounds.height();
}
+ /** Records the starting bounds of the closing organized TaskFragment. */
+ void setClosingChangingStartBoundsIfNeeded() {
+ if (isOrganizedTaskFragment() && mDisplayContent != null
+ && mDisplayContent.mChangingContainers.remove(this)) {
+ mDisplayContent.mClosingChangingContainers.put(
+ this, new Rect(mSurfaceFreezer.mFreezeBounds));
+ }
+ }
+
@Override
boolean isSyncFinished() {
return super.isSyncFinished() && isReadyToTransit();
@@ -2589,6 +2602,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
+ @Override
+ boolean canCustomizeAppTransition() {
+ // This is only called when the app transition is going to be played by system server. In
+ // this case, we should allow custom app transition for fullscreen embedded TaskFragment
+ // just like Activity.
+ return isEmbedded() && matchParentBounds();
+ }
+
/** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
void clearLastPausedActivity() {
forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 39499523833e..14a2d0383019 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -277,31 +277,29 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
// is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
// different, we should recalcuating the bounds.
boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = false;
- // shouldSetAsOverrideWindowingMode is set if the task needs to retain the launchMode
- // regardless of the windowing mode of the parent.
- boolean shouldSetAsOverrideWindowingMode = false;
- if (launchMode == WINDOWING_MODE_PINNED) {
- if (DEBUG) appendLog("picture-in-picture");
- } else if (!root.isResizeable()) {
- if (shouldLaunchUnresizableAppInFreeformInFreeformMode(root, suggestedDisplayArea,
- options)) {
- launchMode = WINDOWING_MODE_UNDEFINED;
- if (outParams.mBounds.isEmpty()) {
- getTaskBounds(root, suggestedDisplayArea, layout, launchMode, hasInitialBounds,
- outParams.mBounds);
- hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = true;
+ if (suggestedDisplayArea.inFreeformWindowingMode()) {
+ if (launchMode == WINDOWING_MODE_PINNED) {
+ if (DEBUG) appendLog("picture-in-picture");
+ } else if (!root.isResizeable()) {
+ if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea, options)) {
+ launchMode = WINDOWING_MODE_FREEFORM;
+ if (outParams.mBounds.isEmpty()) {
+ getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
+ hasInitialBounds, outParams.mBounds);
+ hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = true;
+ }
+ if (DEBUG) appendLog("unresizable-freeform");
+ } else {
+ launchMode = WINDOWING_MODE_FULLSCREEN;
+ outParams.mBounds.setEmpty();
+ if (DEBUG) appendLog("unresizable-forced-maximize");
}
- if (DEBUG) appendLog("unresizable-freeform");
- } else {
- launchMode = WINDOWING_MODE_FULLSCREEN;
- outParams.mBounds.setEmpty();
- shouldSetAsOverrideWindowingMode = true;
- if (DEBUG) appendLog("unresizable-forced-maximize");
}
+ } else {
+ if (DEBUG) appendLog("non-freeform-task-display-area");
}
// If launch mode matches display windowing mode, let it inherit from display.
outParams.mWindowingMode = launchMode == suggestedDisplayArea.getWindowingMode()
- && !shouldSetAsOverrideWindowingMode
? WINDOWING_MODE_UNDEFINED : launchMode;
if (phase == PHASE_WINDOWING_MODE) {
@@ -652,7 +650,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
inOutBounds.offset(xOffset, yOffset);
}
- private boolean shouldLaunchUnresizableAppInFreeformInFreeformMode(ActivityRecord activity,
+ private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
TaskDisplayArea displayArea, @Nullable ActivityOptions options) {
if (options != null && options.getLaunchWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
// Do not launch the activity in freeform if it explicitly requested fullscreen mode.
@@ -665,7 +663,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
final int displayOrientation = orientationFromBounds(displayArea.getBounds());
final int activityOrientation = resolveOrientation(activity, displayArea,
displayArea.getBounds());
- if (displayOrientation != activityOrientation) {
+ if (displayArea.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ && displayOrientation != activityOrientation) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 4d29c4d0a134..ec3962c282f6 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -98,6 +98,7 @@ import com.android.server.wm.utils.RotationAnimationUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -107,7 +108,7 @@ import java.util.function.Predicate;
* Represents a logical transition.
* @see TransitionController
*/
-class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListener {
+class Transition implements BLASTSyncEngine.TransactionReadyListener {
private static final String TAG = "Transition";
private static final String TRACE_NAME_PLAY_TRANSITION = "PlayTransition";
@@ -158,6 +159,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
private @TransitionFlags int mFlags;
private final TransitionController mController;
private final BLASTSyncEngine mSyncEngine;
+ private final Token mToken;
private RemoteTransition mRemoteTransition = null;
/** Only use for clean-up after binder death! */
@@ -165,9 +167,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
private SurfaceControl.Transaction mFinishTransaction = null;
/**
- * Contains change infos for both participants and all ancestors. We have to track ancestors
- * because they are all promotion candidates and thus we need their start-states
- * to be captured.
+ * Contains change infos for both participants and all remote-animatable ancestors. The
+ * ancestors can be the promotion candidates so their start-states need to be captured.
+ * @see #getAnimatableParent
*/
final ArrayMap<WindowContainer, ChangeInfo> mChanges = new ArrayMap<>();
@@ -220,10 +222,27 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mFlags = flags;
mController = controller;
mSyncEngine = syncEngine;
+ mToken = new Token(this);
controller.mTransitionTracer.logState(this);
}
+ @Nullable
+ static Transition fromBinder(@Nullable IBinder token) {
+ if (token == null) return null;
+ try {
+ return ((Token) token).mTransition.get();
+ } catch (ClassCastException e) {
+ Slog.w(TAG, "Invalid transition token: " + token, e);
+ return null;
+ }
+ }
+
+ @NonNull
+ IBinder getToken() {
+ return mToken;
+ }
+
void addFlag(int flag) {
mFlags |= flag;
}
@@ -345,7 +364,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return mFinishTransaction;
}
- private boolean isCollecting() {
+ boolean isCollecting() {
return mState == STATE_COLLECTING || mState == STATE_STARTED;
}
@@ -399,8 +418,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mSyncId, wc);
// "snapshot" all parents (as potential promotion targets). Do this before checking
// if this is already a participant in case it has since been re-parented.
- for (WindowContainer curr = wc.getParent(); curr != null && !mChanges.containsKey(curr);
- curr = curr.getParent()) {
+ for (WindowContainer<?> curr = getAnimatableParent(wc);
+ curr != null && !mChanges.containsKey(curr);
+ curr = getAnimatableParent(curr)) {
mChanges.put(curr, new ChangeInfo(curr));
if (isReadyGroup(curr)) {
mReadyTracker.addGroup(curr);
@@ -733,6 +753,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, TRACE_NAME_PLAY_TRANSITION,
System.identityHashCode(this));
}
+ // Close the transactions now. They were originally copied to Shell in case we needed to
+ // apply them due to a remote failure. Since we don't need to apply them anymore, free them
+ // immediately.
+ if (mStartTransaction != null) mStartTransaction.close();
+ if (mFinishTransaction != null) mFinishTransaction.close();
mStartTransaction = mFinishTransaction = null;
if (mState < STATE_PLAYING) {
throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
@@ -874,6 +899,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mController.mAtm.mWindowManager.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
}
+ cleanUpInternal();
}
void abort() {
@@ -916,15 +942,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
dc.getPendingTransaction().merge(transaction);
mSyncId = -1;
mOverrideOptions = null;
+ cleanUpInternal();
return;
}
- // Ensure that wallpaper visibility is updated with the latest wallpaper target.
- for (int i = mParticipants.size() - 1; i >= 0; --i) {
- final WindowContainer<?> wc = mParticipants.valueAt(i);
- if (isWallpaper(wc) && wc.getDisplayContent() != null) {
- wc.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
- }
- }
mState = STATE_PLAYING;
mStartTransaction = transaction;
@@ -1034,7 +1054,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Calling onTransitionReady: %s", info);
mController.getTransitionPlayer().onTransitionReady(
- this, info, transaction, mFinishTransaction);
+ mToken, info, transaction, mFinishTransaction);
+ // Since we created root-leash but no longer reference it from core, release it now
+ info.releaseAnimSurfaces();
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, TRACE_NAME_PLAY_TRANSITION,
System.identityHashCode(this));
@@ -1067,7 +1089,17 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (mFinishTransaction != null) {
mFinishTransaction.apply();
}
- mController.finishTransition(this);
+ mController.finishTransition(mToken);
+ }
+
+ private void cleanUpInternal() {
+ // Clean-up any native references.
+ for (int i = 0; i < mChanges.size(); ++i) {
+ final ChangeInfo ci = mChanges.valueAt(i);
+ if (ci.mSnapshot != null) {
+ ci.mSnapshot.release();
+ }
+ }
}
/** @see RecentsAnimationController#attachNavigationBarToApp */
@@ -1269,6 +1301,16 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return sb.toString();
}
+ /** Returns the parent that the remote animator can animate or control. */
+ private static WindowContainer<?> getAnimatableParent(WindowContainer<?> wc) {
+ WindowContainer<?> parent = wc.getParent();
+ while (parent != null
+ && (!parent.canCreateRemoteAnimationTarget() && !parent.isOrganized())) {
+ parent = parent.getParent();
+ }
+ return parent;
+ }
+
private static boolean reportIfNotTop(WindowContainer wc) {
// Organized tasks need to be reported anyways because Core won't show() their surfaces
// and we can't rely on onTaskAppeared because it isn't in sync.
@@ -1492,7 +1534,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
intermediates.clear();
boolean foundParentInTargets = false;
// Collect the intermediate parents between target and top changed parent.
- for (WindowContainer<?> p = wc.getParent(); p != null; p = p.getParent()) {
+ for (WindowContainer<?> p = getAnimatableParent(wc); p != null;
+ p = getAnimatableParent(p)) {
final ChangeInfo parentChange = changes.get(p);
if (parentChange == null || !parentChange.hasChanged(p)) break;
if (p.mRemoteToken == null) {
@@ -1850,10 +1893,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return isCollecting() && mSyncId >= 0;
}
- static Transition fromBinder(IBinder binder) {
- return (Transition) binder;
- }
-
@VisibleForTesting
static class ChangeInfo {
private static final int FLAG_NONE = 0;
@@ -2345,4 +2384,18 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
}
+
+ private static class Token extends Binder {
+ final WeakReference<Transition> mTransition;
+
+ Token(Transition transition) {
+ mTransition = new WeakReference<>(transition);
+ }
+
+ @Override
+ public String toString() {
+ return "Token{" + Integer.toHexString(System.identityHashCode(this)) + " "
+ + mTransition.get() + "}";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index e4d39b94ab10..d3d1c163aa19 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -458,8 +458,9 @@ class TransitionController {
info = new ActivityManager.RunningTaskInfo();
startTask.fillTaskInfo(info);
}
- mTransitionPlayer.requestStartTransition(transition, new TransitionRequestInfo(
- transition.mType, info, remoteTransition, displayChange));
+ mTransitionPlayer.requestStartTransition(transition.getToken(),
+ new TransitionRequestInfo(transition.mType, info, remoteTransition,
+ displayChange));
transition.setRemoteTransition(remoteTransition);
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting transition", e);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 908fdbdc74e6..920b1bad48d1 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -113,12 +113,6 @@ class WallpaperController {
private boolean mShouldUpdateZoom;
- /**
- * Temporary storage for taking a screenshot of the wallpaper.
- * @see #screenshotWallpaperLocked()
- */
- private WindowState mTmpTopWallpaper;
-
@Nullable private Point mLargestDisplaySize = null;
private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();
@@ -962,21 +956,16 @@ class WallpaperController {
}
WindowState getTopVisibleWallpaper() {
- mTmpTopWallpaper = null;
-
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
- token.forAllWindows(w -> {
- final WindowStateAnimator winAnim = w.mWinAnimator;
- if (winAnim != null && winAnim.getShown() && winAnim.mLastAlpha > 0f) {
- mTmpTopWallpaper = w;
- return true;
+ for (int i = token.getChildCount() - 1; i >= 0; i--) {
+ final WindowState w = token.getChildAt(i);
+ if (w.mWinAnimator.getShown() && w.mWinAnimator.mLastAlpha > 0f) {
+ return w;
}
- return false;
- }, true /* traverseTopToBottom */);
+ }
}
-
- return mTmpTopWallpaper;
+ return null;
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 80357eb0c7fa..0b80914272dd 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -810,6 +810,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
void removeImmediately() {
final DisplayContent dc = getDisplayContent();
if (dc != null) {
+ dc.mClosingChangingContainers.remove(this);
mSurfaceFreezer.unfreeze(getSyncTransaction());
}
while (!mChildren.isEmpty()) {
@@ -1019,9 +1020,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @param dc The display this container is on after changes.
*/
void onDisplayChanged(DisplayContent dc) {
- if (mDisplayContent != null && mDisplayContent.mChangingContainers.remove(this)) {
- // Cancel any change transition queued-up for this container on the old display.
- mSurfaceFreezer.unfreeze(getSyncTransaction());
+ if (mDisplayContent != null) {
+ mDisplayContent.mClosingChangingContainers.remove(this);
+ if (mDisplayContent.mChangingContainers.remove(this)) {
+ // Cancel any change transition queued-up for this container on the old display.
+ mSurfaceFreezer.unfreeze(getSyncTransaction());
+ }
}
mDisplayContent = dc;
if (dc != null && dc != this) {
@@ -1298,6 +1302,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// If we are losing visibility, then a snapshot isn't necessary and we are no-longer
// part of a change transition.
if (!visible) {
+ if (asTaskFragment() != null) {
+ // If the organized TaskFragment is closing while resizing, we want to keep track of
+ // its starting bounds to make sure the animation starts at the correct position.
+ // This should be called before unfreeze() because we record the starting bounds
+ // in SurfaceFreezer.
+ asTaskFragment().setClosingChangingStartBoundsIfNeeded();
+ }
mSurfaceFreezer.unfreeze(getSyncTransaction());
}
WindowContainer parent = getParent();
@@ -1306,6 +1317,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
+ /** Whether this window is closing while resizing. */
+ boolean isClosingWhenResizing() {
+ return mDisplayContent != null
+ && mDisplayContent.mClosingChangingContainers.containsKey(this);
+ }
+
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
@@ -3004,10 +3021,22 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
final Rect localBounds = new Rect(mTmpRect);
localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
- final RemoteAnimationController.RemoteAnimationRecord adapters =
- controller.createRemoteAnimationRecord(
- this, mTmpPoint, localBounds, screenBounds,
- (isChanging ? mSurfaceFreezer.mFreezeBounds : null), showBackdrop);
+ final RemoteAnimationController.RemoteAnimationRecord adapters;
+ if (!isChanging && !enter && isClosingWhenResizing()) {
+ // Container that is closing while resizing. Pass in the closing start bounds, so
+ // the animation can start with the correct bounds, there won't be a snapshot.
+ // Cleanup the mClosingChangingContainers so that when the animation is finished, it
+ // will reset the surface.
+ final Rect closingStartBounds = getDisplayContent().mClosingChangingContainers
+ .remove(this);
+ adapters = controller.createRemoteAnimationRecord(
+ this, mTmpPoint, localBounds, screenBounds, closingStartBounds,
+ showBackdrop, false /* shouldCreateSnapshot */);
+ } else {
+ final Rect startBounds = isChanging ? mSurfaceFreezer.mFreezeBounds : null;
+ adapters = controller.createRemoteAnimationRecord(
+ this, mTmpPoint, localBounds, screenBounds, startBounds, showBackdrop);
+ }
if (backdropColor != 0) {
adapters.setBackDropColor(backdropColor);
}
@@ -3464,7 +3493,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return;
}
- getRelativePosition(mTmpPos);
+ if (isClosingWhenResizing()) {
+ // This container is closing while resizing, keep its surface at the starting position
+ // to prevent animation flicker.
+ getRelativePosition(mDisplayContent.mClosingChangingContainers.get(this), mTmpPos);
+ } else {
+ getRelativePosition(mTmpPos);
+ }
final int deltaRotation = getRelativeDisplayRotation();
if (mTmpPos.equals(mLastSurfacePosition) && deltaRotation == mLastDeltaRotation) {
return;
@@ -3529,9 +3564,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
outSurfaceInsets.setEmpty();
}
+ /** Gets the position of this container in its parent's coordinate. */
void getRelativePosition(Point outPos) {
- final Rect dispBounds = getBounds();
- outPos.set(dispBounds.left, dispBounds.top);
+ getRelativePosition(getBounds(), outPos);
+ }
+
+ /** Gets the position of {@code curBounds} in this container's parent's coordinate. */
+ void getRelativePosition(Rect curBounds, Point outPos) {
+ outPos.set(curBounds.left, curBounds.top);
final WindowContainer parent = getParent();
if (parent != null) {
final Rect parentBounds = parent.getBounds();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9db5170897a8..68b853dd2695 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2587,6 +2587,12 @@ public class WindowManagerService extends IWindowManager.Stub
&& win.mSyncSeqId > lastSyncSeqId) {
maybeSyncSeqId = win.shouldSyncWithBuffers() ? win.mSyncSeqId : -1;
win.markRedrawForSyncReported();
+ if (win.mSyncState == WindowContainer.SYNC_STATE_WAITING_FOR_DRAW
+ && winAnimator.mDrawState == WindowStateAnimator.HAS_DRAWN
+ && maybeSyncSeqId < 0) {
+ // Do not wait for a drawn window which won't report draw.
+ win.onSyncFinishedDrawing();
+ }
} else {
maybeSyncSeqId = -1;
}
@@ -8872,14 +8878,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public List<DisplayInfo> getPossibleDisplayInfo(int displayId, String packageName) {
+ public List<DisplayInfo> getPossibleDisplayInfo(int displayId) {
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- if (packageName == null || !isRecentsComponent(packageName, callingUid)) {
- Slog.e(TAG, "Unable to verify uid for package " + packageName
- + " for getPossibleMaximumWindowMetrics");
+ if (!mAtmService.isCallerRecents(callingUid)) {
+ Slog.e(TAG, "Unable to verify uid for getPossibleDisplayInfo"
+ + " on uid " + callingUid);
return new ArrayList<>();
}
@@ -8897,31 +8903,6 @@ public class WindowManagerService extends IWindowManager.Stub
return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId);
}
- /**
- * Returns {@code true} when the calling package is the recents component.
- */
- boolean isRecentsComponent(@NonNull String callingPackageName, int callingUid) {
- String recentsPackage;
- try {
- String recentsComponent = mContext.getResources().getString(
- R.string.config_recentsComponentName);
- if (recentsComponent == null) {
- return false;
- }
- recentsPackage = ComponentName.unflattenFromString(recentsComponent).getPackageName();
- } catch (Resources.NotFoundException e) {
- Slog.e(TAG, "Unable to verify if recents component", e);
- return false;
- }
- try {
- return callingUid == mContext.getPackageManager().getPackageUid(callingPackageName, 0)
- && callingPackageName.equals(recentsPackage);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Unable to verify if recents component", e);
- return false;
- }
- }
-
void grantEmbeddedWindowFocus(Session session, IBinder focusToken, boolean grantFocus) {
synchronized (mGlobalLock) {
final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index b30fd0729525..d85bd830cc11 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -307,7 +307,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
nextTransition.setAllReady();
}
});
- return nextTransition;
+ return nextTransition.getToken();
}
transition = mTransitionController.createTransition(type);
}
@@ -316,7 +316,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (needsSetReady) {
transition.setAllReady();
}
- return transition;
+ return transition.getToken();
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/services/tests/servicestests/src/com/android/server/DockObserverTest.java b/services/tests/servicestests/src/com/android/server/DockObserverTest.java
index c325778a5683..ee09074f7625 100644
--- a/services/tests/servicestests/src/com/android/server/DockObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/DockObserverTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.Intent;
import android.os.Looper;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
@@ -74,6 +75,11 @@ public class DockObserverTest {
.isEqualTo(Intent.EXTRA_DOCK_STATE_UNDOCKED);
}
+ void setDeviceProvisioned(boolean provisioned) {
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+ provisioned ? 1 : 0);
+ }
+
@Before
public void setUp() {
if (Looper.myLooper() == null) {
@@ -131,4 +137,25 @@ public class DockObserverTest {
assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY5=5",
Intent.EXTRA_DOCK_STATE_HE_DESK);
}
+
+ @Test
+ public void testDockIntentBroadcast_deviceNotProvisioned()
+ throws ExecutionException, InterruptedException {
+ DockObserver observer = new DockObserver(mInterceptingContext);
+ // Set the device as not provisioned.
+ setDeviceProvisioned(false);
+ observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+
+ BroadcastInterceptingContext.FutureIntent futureIntent =
+ updateExtconDockState(observer, "DOCK=1");
+ TestableLooper.get(this).processAllMessages();
+ // Verify no broadcast was sent as device was not provisioned.
+ futureIntent.assertNotReceived();
+
+ // Ensure we send the broadcast when the device is provisioned.
+ setDeviceProvisioned(true);
+ TestableLooper.get(this).processAllMessages();
+ assertThat(futureIntent.get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1))
+ .isEqualTo(Intent.EXTRA_DOCK_STATE_DESK);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index 7acb6d66c834..64af2969b67c 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -27,18 +27,18 @@ import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.VolumeInfo;
import android.os.test.TestLooper;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
+import junit.framework.Assert;
+
import org.junit.Before;
import org.junit.Test;
public class AudioDeviceVolumeManagerTest {
private static final String TAG = "AudioDeviceVolumeManagerTest";
- private static final AudioDeviceAttributes DEVICE_SPEAKER_OUT = new AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
-
private Context mContext;
private String mPackageName;
private AudioSystemAdapter mSpyAudioSystem;
@@ -84,14 +84,20 @@ public class AudioDeviceVolumeManagerTest {
final AudioDeviceAttributes usbDevice = new AudioDeviceAttributes(
/*native type*/ AudioSystem.DEVICE_OUT_USB_DEVICE, /*address*/ "bla");
- mAudioService.setDeviceVolume(volMin, usbDevice, mPackageName, TAG);
+ mAudioService.setDeviceVolume(volMin, usbDevice, mPackageName);
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
AudioManager.STREAM_MUSIC, minIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
- mAudioService.setDeviceVolume(volMid, usbDevice, mPackageName, TAG);
+ mAudioService.setDeviceVolume(volMid, usbDevice, mPackageName);
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
AudioManager.STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+
+ final VolumeInfo vi = mAudioService.getDeviceVolume(volMin, usbDevice, mPackageName);
+ Assert.assertEquals("getDeviceVolume doesn't return expected value in " + vi
+ + " after setting " + volMid,
+ (volMid.getMaxVolumeIndex() - volMid.getMinVolumeIndex()) / 2,
+ (vi.getMaxVolumeIndex() - vi.getMinVolumeIndex()) / 2);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 6d2631ac3ad4..f28986629c1c 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -456,9 +456,8 @@ public final class DeviceStateProviderImplTest {
new DeviceState(1, "CLOSED", 0 /* flags */),
new DeviceState(2, "HALF_OPENED", 0 /* flags */)
}, mDeviceStateArrayCaptor.getValue());
- // onStateChanged() should be called because the provider could not find the sensor.
- verify(listener).onStateChanged(mIntegerCaptor.capture());
- assertEquals(1, mIntegerCaptor.getValue().intValue());
+ // onStateChanged() should not be called because the provider could not find the sensor.
+ verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
}
private static Sensor newSensor(String name, String type) throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index a42d009c417c..6325008dc1e0 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -1929,6 +1929,50 @@ public class PowerManagerServiceTest {
}
@Test
+ public void testMultiDisplay_defaultDozing_addNewDisplayDefaultGoesBackToDoze() {
+ final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
+ final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+ new AtomicReference<>();
+ doAnswer((Answer<Void>) invocation -> {
+ listener.set(invocation.getArgument(0));
+ return null;
+ }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = nonDefaultDisplayGroupId;
+ when(mDisplayManagerInternalMock.getDisplayInfo(nonDefaultDisplay)).thenReturn(info);
+
+ doAnswer(inv -> {
+ when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+ return null;
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
+
+ createService();
+ startSystem();
+
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+
+ forceDozing();
+ advanceTime(500);
+
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_DOZING);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
+ verify(mDreamManagerInternalMock).startDream(eq(true), anyString());
+
+ listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+ advanceTime(500);
+
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_DOZING);
+ verify(mDreamManagerInternalMock, times(2)).startDream(eq(true), anyString());
+ }
+
+ @Test
public void testLastSleepTime_notUpdatedWhenDreaming() {
createService();
startSystem();
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 077caa408344..3f3b052931ab 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1101,10 +1101,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
new NotificationChannel("id", "name", IMPORTANCE_HIGH);
mBinderService.updateNotificationChannelForPackage(PKG, mUid, updatedChannel);
- // pretend only this following part is called by the app (system permissions are required to
- // update the notification channel on behalf of the user above)
- mService.isSystemUid = false;
-
// Recreating with a lower importance leaves channel unchanged.
final NotificationChannel dupeChannel =
new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_LOW);
@@ -1130,46 +1126,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testCreateNotificationChannels_fromAppCannotSetFields() throws Exception {
- // Confirm that when createNotificationChannels is called from the relevant app and not
- // system, then it cannot set fields that can't be set by apps
- mService.isSystemUid = false;
-
- final NotificationChannel channel =
- new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
- channel.setBypassDnd(true);
- channel.setAllowBubbles(true);
-
- mBinderService.createNotificationChannels(PKG,
- new ParceledListSlice(Arrays.asList(channel)));
-
- final NotificationChannel createdChannel =
- mBinderService.getNotificationChannel(PKG, mContext.getUserId(), PKG, "id");
- assertFalse(createdChannel.canBypassDnd());
- assertFalse(createdChannel.canBubble());
- }
-
- @Test
- public void testCreateNotificationChannels_fromSystemCanSetFields() throws Exception {
- // Confirm that when createNotificationChannels is called from system,
- // then it can set fields that can't be set by apps
- mService.isSystemUid = true;
-
- final NotificationChannel channel =
- new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
- channel.setBypassDnd(true);
- channel.setAllowBubbles(true);
-
- mBinderService.createNotificationChannels(PKG,
- new ParceledListSlice(Arrays.asList(channel)));
-
- final NotificationChannel createdChannel =
- mBinderService.getNotificationChannel(PKG, mContext.getUserId(), PKG, "id");
- assertTrue(createdChannel.canBypassDnd());
- assertTrue(createdChannel.canBubble());
- }
-
- @Test
public void testBlockedNotifications_suspended() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(true);
@@ -3132,8 +3088,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testDeleteChannelGroupChecksForFgses() throws Exception {
- // the setup for this test requires it to seem like it's coming from the app
- mService.isSystemUid = false;
when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
.thenReturn(singletonList(mock(AssociationInfo.class)));
CountDownLatch latch = new CountDownLatch(2);
@@ -3146,7 +3100,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
ParceledListSlice<NotificationChannel> pls =
new ParceledListSlice(ImmutableList.of(notificationChannel));
try {
- mBinderService.createNotificationChannels(PKG, pls);
+ mBinderService.createNotificationChannelsForPackage(PKG, mUid, pls);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -3165,10 +3119,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
ParceledListSlice<NotificationChannel> pls =
new ParceledListSlice(ImmutableList.of(notificationChannel));
try {
- // Because existing channels won't have their groups overwritten when the call
- // is from the app, this call won't take the channel out of the group
- mBinderService.createNotificationChannels(PKG, pls);
- mBinderService.deleteNotificationChannelGroup(PKG, "group");
+ mBinderService.createNotificationChannelsForPackage(PKG, mUid, pls);
+ mBinderService.deleteNotificationChannelGroup(PKG, "group");
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -8673,7 +8625,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertEquals("friend", friendChannel.getConversationId());
assertEquals(null, original.getConversationId());
assertEquals(original.canShowBadge(), friendChannel.canShowBadge());
- assertEquals(original.canBubble(), friendChannel.canBubble()); // called by system
+ assertFalse(friendChannel.canBubble()); // can't be modified by app
assertFalse(original.getId().equals(friendChannel.getId()));
assertNotNull(friendChannel.getId());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index d3aa073c84d8..df7b3cdebe28 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -74,15 +74,15 @@ public class SyncEngineTests extends WindowTestsBase {
int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, mockWC);
- // Make sure a traversal is requested
- verify(mWm.mWindowPlacerLocked, times(1)).requestTraversal();
+ // The traversal is not requested because ready is not set.
+ verify(mWm.mWindowPlacerLocked, times(0)).requestTraversal();
bse.onSurfacePlacement();
verify(listener, times(0)).onTransactionReady(anyInt(), any());
bse.setReady(id);
// Make sure a traversal is requested
- verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+ verify(mWm.mWindowPlacerLocked).requestTraversal();
bse.onSurfacePlacement();
verify(listener, times(1)).onTransactionReady(eq(id), notNull());
@@ -103,14 +103,14 @@ public class SyncEngineTests extends WindowTestsBase {
int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, mockWC);
bse.setReady(id);
- // Make sure traversals requested (one for add and another for setReady)
- verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+ // Make sure traversals requested.
+ verify(mWm.mWindowPlacerLocked).requestTraversal();
bse.onSurfacePlacement();
verify(listener, times(0)).onTransactionReady(anyInt(), any());
mockWC.onSyncFinishedDrawing();
- // Make sure a (third) traversal is requested.
- verify(mWm.mWindowPlacerLocked, times(3)).requestTraversal();
+ // Make sure the second traversal is requested.
+ verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
bse.onSurfacePlacement();
verify(listener, times(1)).onTransactionReady(eq(id), notNull());
}
@@ -127,8 +127,8 @@ public class SyncEngineTests extends WindowTestsBase {
int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, mockWC);
bse.setReady(id);
- // Make sure traversals requested (one for add and another for setReady)
- verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+ // Make sure traversals requested.
+ verify(mWm.mWindowPlacerLocked).requestTraversal();
bse.onSurfacePlacement();
verify(listener, times(0)).onTransactionReady(anyInt(), any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 2a88eb0e7b61..1188f49ae60e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -825,11 +825,10 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
}
@Test
- public void testLaunchesPortraitUnresizableOnFreeformLandscapeDisplay() {
+ public void testLaunchesPortraitUnresizableOnFreeformDisplayWithFreeformSizeCompat() {
mAtm.mDevEnableNonResizableMultiWindow = true;
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- assertTrue(freeformDisplay.getBounds().width() > freeformDisplay.getBounds().height());
final ActivityOptions options = ActivityOptions.makeBasic();
mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
@@ -837,42 +836,12 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
assertEquals(RESULT_CONTINUE,
new CalculateRequestBuilder().setOptions(options).calculate());
- assertEquals(WINDOWING_MODE_UNDEFINED, mResult.mWindowingMode);
- }
-
- @Test
- public void testLaunchesLandscapeUnresizableOnFreeformLandscapeDisplay() {
- mAtm.mDevEnableNonResizableMultiWindow = true;
- final TestDisplayContent freeformDisplay = createNewDisplayContent(
- WINDOWING_MODE_FREEFORM);
- assertTrue(freeformDisplay.getBounds().width() > freeformDisplay.getBounds().height());
- final ActivityOptions options = ActivityOptions.makeBasic();
- mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
- mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
- mActivity.info.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE;
- assertEquals(RESULT_CONTINUE,
- new CalculateRequestBuilder().setOptions(options).calculate());
-
- assertEquals(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode);
- }
-
- @Test
- public void testLaunchesUndefinedUnresizableOnFreeformLandscapeDisplay() {
- mAtm.mDevEnableNonResizableMultiWindow = true;
- final TestDisplayContent freeformDisplay = createNewDisplayContent(
+ assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
WINDOWING_MODE_FREEFORM);
- assertTrue(freeformDisplay.getBounds().width() > freeformDisplay.getBounds().height());
- final ActivityOptions options = ActivityOptions.makeBasic();
- mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
- mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
- assertEquals(RESULT_CONTINUE,
- new CalculateRequestBuilder().setOptions(options).calculate());
-
- assertEquals(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode);
}
@Test
- public void testForceMaximizingAppsOnNonFreeformDisplay() {
+ public void testSkipsForceMaximizingAppsOnNonFreeformDisplay() {
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
options.setLaunchBounds(new Rect(0, 0, 200, 100));
@@ -886,9 +855,8 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
assertEquals(RESULT_CONTINUE,
new CalculateRequestBuilder().setOptions(options).calculate());
- // Non-resizable apps must be launched in fullscreen in a fullscreen display regardless of
- // other properties.
- assertEquals(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode);
+ assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+ WINDOWING_MODE_FULLSCREEN);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 54bcbd9adf5a..999523f3ea19 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -476,6 +476,8 @@ public class TransitionTests extends WindowTestsBase {
wallpaperWindow.mHasSurface = true;
doReturn(true).when(mDisplayContent).isAttached();
transition.collect(mDisplayContent);
+ assertFalse("The change of non-interesting window container should be skipped",
+ transition.mChanges.containsKey(mDisplayContent.getParent()));
mDisplayContent.getWindowConfiguration().setRotation(
(mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
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 63335086859d..aab70b5f9004 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -369,18 +369,9 @@ public class WallpaperControllerTests extends WindowTestsBase {
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
token.finishSync(t, false /* cancel */);
transit.onTransactionReady(transit.getSyncId(), t);
- dc.mTransitionController.finishTransition(transit);
+ dc.mTransitionController.finishTransition(transit.getToken());
assertFalse(wallpaperWindow.isVisible());
assertFalse(token.isVisible());
-
- // Assume wallpaper was visible. When transaction is ready without wallpaper target,
- // wallpaper should be requested to be invisible.
- token.setVisibility(true);
- transit = dc.mTransitionController.createTransition(TRANSIT_CLOSE);
- dc.mTransitionController.collect(token);
- transit.onTransactionReady(transit.getSyncId(), t);
- assertFalse(token.isVisibleRequested());
- assertTrue(token.isVisible());
}
private static void prepareSmallerSecondDisplay(DisplayContent dc, int width, int height) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index b99fd1606f55..894ba3e95261 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1731,7 +1731,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
void startTransition() {
- mOrganizer.startTransition(mLastTransit, null);
+ mOrganizer.startTransition(mLastTransit.getToken(), null);
}
void onTransactionReady(SurfaceControl.Transaction t) {
@@ -1744,7 +1744,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
public void finish() {
- mController.finishTransition(mLastTransit);
+ mController.finishTransition(mLastTransit.getToken());
}
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7f8e4ba405d3..741721d14ac1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2000,6 +2000,17 @@ public class CarrierConfigManager {
"nr_advanced_threshold_bandwidth_khz_int";
/**
+ * Indicating whether to include LTE cell bandwidths when determining whether the aggregated
+ * cell bandwidth meets the required threshold for NR advanced.
+ *
+ * @see TelephonyDisplayInfo#OVERRIDE_NETWORK_TYPE_NR_ADVANCED
+ *
+ * @hide
+ */
+ public static final String KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL =
+ "include_lte_for_nr_advanced_threshold_bandwidth_bool";
+
+ /**
* Boolean indicating if operator name should be shown in the status bar
* @hide
*/
@@ -9001,6 +9012,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true);
sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
sDefaults.putInt(KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 0);
+ sDefaults.putBoolean(KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL, false);
sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA});
sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);