summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ApiDocs.bp2
-rw-r--r--StubLibraries.bp2
-rw-r--r--boot/Android.bp4
-rw-r--r--core/api/current.txt10
-rw-r--r--core/api/test-current.txt57
-rw-r--r--core/java/android/app/ActivityThread.java8
-rw-r--r--core/java/android/app/ActivityTransitionState.java9
-rw-r--r--core/java/android/app/NotificationChannelGroup.java14
-rw-r--r--core/java/android/app/WindowConfiguration.java23
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java26
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl4
-rw-r--r--core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java2
-rw-r--r--core/java/android/app/servertransaction/ActivityRelaunchItem.java2
-rw-r--r--core/java/android/app/servertransaction/ConfigurationChangeItem.java2
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java3
-rw-r--r--core/java/android/app/servertransaction/MoveToDisplayItem.java2
-rw-r--r--core/java/android/companion/BluetoothDeviceFilterUtils.java2
-rw-r--r--core/java/android/companion/BluetoothLeDeviceFilter.java2
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java30
-rw-r--r--core/java/android/companion/CompanionDeviceService.java2
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java105
-rw-r--r--core/java/android/hardware/SystemSensorManager.java1
-rw-r--r--core/java/android/hardware/input/IInputDeviceBatteryListener.aidl29
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl5
-rw-r--r--core/java/android/hardware/input/InputManager.java207
-rw-r--r--core/java/android/os/BatteryUsageStats.java11
-rw-r--r--core/java/android/os/VibrationEffect.java6
-rw-r--r--core/java/android/os/logcat/OWNERS2
-rw-r--r--core/java/android/service/games/TEST_MAPPING16
-rw-r--r--core/java/android/service/trust/TrustAgentService.java4
-rw-r--r--core/java/android/text/style/SpanUtils.java351
-rw-r--r--core/java/android/view/DisplayInfo.java6
-rw-r--r--core/java/android/view/DragEvent.java2
-rw-r--r--core/java/android/view/ImeFocusController.java3
-rw-r--r--core/java/android/view/InputDevice.java149
-rw-r--r--core/java/android/view/KeyEvent.java11
-rw-r--r--core/java/android/view/SurfaceView.java7
-rw-r--r--core/java/android/view/VelocityTracker.java65
-rw-r--r--core/java/android/view/View.java12
-rw-r--r--core/java/android/view/ViewRootImpl.java25
-rw-r--r--core/java/android/view/WindowManager.java8
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java19
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java31
-rw-r--r--core/java/android/widget/EditText.java101
-rw-r--r--core/java/android/widget/TextView.java2
-rw-r--r--core/java/android/window/TaskFragmentOrganizer.java148
-rw-r--r--core/java/android/window/TaskFragmentTransaction.java19
-rw-r--r--core/java/android/window/WindowTokenClient.java2
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java135
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java9
-rw-r--r--core/java/com/android/internal/app/chooser/SelectableTargetInfo.java33
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodDebug.java28
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java81
-rw-r--r--core/java/com/android/internal/os/ZygoteConnectionConstants.java2
-rw-r--r--core/java/com/android/internal/policy/DividerSnapAlgorithm.java4
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java5
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java12
-rw-r--r--core/jni/android_view_VelocityTracker.cpp116
-rw-r--r--core/res/res/layout/notification_template_material_conversation.xml79
-rw-r--r--core/res/res/values-af/strings.xml6
-rw-r--r--core/res/res/values-am/strings.xml16
-rw-r--r--core/res/res/values-ar/strings.xml6
-rw-r--r--core/res/res/values-as/strings.xml6
-rw-r--r--core/res/res/values-az/strings.xml6
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-be/strings.xml6
-rw-r--r--core/res/res/values-bg/strings.xml6
-rw-r--r--core/res/res/values-bn/strings.xml6
-rw-r--r--core/res/res/values-bs/strings.xml6
-rw-r--r--core/res/res/values-ca/strings.xml6
-rw-r--r--core/res/res/values-cs/strings.xml6
-rw-r--r--core/res/res/values-da/strings.xml6
-rw-r--r--core/res/res/values-de/strings.xml6
-rw-r--r--core/res/res/values-el/strings.xml6
-rw-r--r--core/res/res/values-en-rAU/strings.xml3
-rw-r--r--core/res/res/values-en-rCA/strings.xml3
-rw-r--r--core/res/res/values-en-rGB/strings.xml3
-rw-r--r--core/res/res/values-en-rIN/strings.xml3
-rw-r--r--core/res/res/values-en-rXC/strings.xml2
-rw-r--r--core/res/res/values-es-rUS/strings.xml53
-rw-r--r--core/res/res/values-es/strings.xml53
-rw-r--r--core/res/res/values-et/strings.xml6
-rw-r--r--core/res/res/values-eu/strings.xml6
-rw-r--r--core/res/res/values-fa/strings.xml6
-rw-r--r--core/res/res/values-fi/strings.xml6
-rw-r--r--core/res/res/values-fr-rCA/strings.xml53
-rw-r--r--core/res/res/values-fr/strings.xml53
-rw-r--r--core/res/res/values-gl/strings.xml6
-rw-r--r--core/res/res/values-gu/strings.xml6
-rw-r--r--core/res/res/values-hi/strings.xml12
-rw-r--r--core/res/res/values-hr/strings.xml6
-rw-r--r--core/res/res/values-hu/strings.xml6
-rw-r--r--core/res/res/values-hy/strings.xml6
-rw-r--r--core/res/res/values-in/strings.xml6
-rw-r--r--core/res/res/values-is/strings.xml6
-rw-r--r--core/res/res/values-it/strings.xml53
-rw-r--r--core/res/res/values-iw/strings.xml6
-rw-r--r--core/res/res/values-ja/strings.xml5
-rw-r--r--core/res/res/values-ka/strings.xml6
-rw-r--r--core/res/res/values-kk/strings.xml3
-rw-r--r--core/res/res/values-km/strings.xml6
-rw-r--r--core/res/res/values-kn/strings.xml6
-rw-r--r--core/res/res/values-ko/strings.xml6
-rw-r--r--core/res/res/values-ky/strings.xml6
-rw-r--r--core/res/res/values-lo/strings.xml6
-rw-r--r--core/res/res/values-lt/strings.xml6
-rw-r--r--core/res/res/values-lv/strings.xml6
-rw-r--r--core/res/res/values-mk/strings.xml6
-rw-r--r--core/res/res/values-ml/strings.xml6
-rw-r--r--core/res/res/values-mn/strings.xml6
-rw-r--r--core/res/res/values-mr/strings.xml6
-rw-r--r--core/res/res/values-ms/strings.xml6
-rw-r--r--core/res/res/values-my/strings.xml6
-rw-r--r--core/res/res/values-nb/strings.xml6
-rw-r--r--core/res/res/values-ne/strings.xml6
-rw-r--r--core/res/res/values-nl/strings.xml6
-rw-r--r--core/res/res/values-or/strings.xml8
-rw-r--r--core/res/res/values-pa/strings.xml6
-rw-r--r--core/res/res/values-pl/strings.xml6
-rw-r--r--core/res/res/values-pt-rBR/strings.xml53
-rw-r--r--core/res/res/values-pt-rPT/strings.xml53
-rw-r--r--core/res/res/values-pt/strings.xml53
-rw-r--r--core/res/res/values-ro/strings.xml6
-rw-r--r--core/res/res/values-ru/strings.xml6
-rw-r--r--core/res/res/values-si/strings.xml6
-rw-r--r--core/res/res/values-sk/strings.xml6
-rw-r--r--core/res/res/values-sl/strings.xml6
-rw-r--r--core/res/res/values-sq/strings.xml6
-rw-r--r--core/res/res/values-sr/strings.xml6
-rw-r--r--core/res/res/values-sv/strings.xml6
-rw-r--r--core/res/res/values-sw/strings.xml6
-rw-r--r--core/res/res/values-ta/strings.xml6
-rw-r--r--core/res/res/values-te/strings.xml6
-rw-r--r--core/res/res/values-th/strings.xml6
-rw-r--r--core/res/res/values-tl/strings.xml6
-rw-r--r--core/res/res/values-tr/strings.xml6
-rw-r--r--core/res/res/values-uk/strings.xml6
-rw-r--r--core/res/res/values-ur/strings.xml6
-rw-r--r--core/res/res/values-uz/strings.xml6
-rw-r--r--core/res/res/values-vi/strings.xml6
-rw-r--r--core/res/res/values-zh-rCN/strings.xml6
-rw-r--r--core/res/res/values-zh-rHK/strings.xml6
-rw-r--r--core/res/res/values-zh-rTW/strings.xml6
-rw-r--r--core/res/res/values-zu/strings.xml6
-rw-r--r--core/res/res/values/attrs.xml2
-rw-r--r--core/res/res/values/ids.xml6
-rw-r--r--core/res/res/values/public-staging.xml4
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/app/NotificationChannelGroupTest.java16
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java97
-rw-r--r--core/tests/coretests/src/android/hardware/input/InputDeviceBatteryListenerTest.kt227
-rw-r--r--core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java9
-rw-r--r--core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java33
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt155
-rw-r--r--data/keyboards/Generic.kl6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java140
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt29
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt25
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt38
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt56
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt43
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt31
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt51
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt37
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt169
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java67
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java4
-rw-r--r--media/java/android/media/AudioSystem.java8
-rw-r--r--media/java/android/media/ExifInterface.java32
-rw-r--r--media/tests/MediaRouter/Android.bp1
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java42
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java2
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java2
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java2
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java2
-rw-r--r--packages/SettingsLib/AppPreference/res/layout/app_header_preference.xml81
-rw-r--r--packages/SettingsLib/AppPreference/res/values/strings.xml22
-rw-r--r--packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java144
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp1
-rw-r--r--packages/SettingsLib/IllustrationPreference/res/values/colors.xml2
-rw-r--r--packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java88
-rw-r--r--packages/SettingsLib/LayoutPreference/Android.bp1
-rw-r--r--packages/SettingsLib/LayoutPreference/res/values/styles.xml14
-rw-r--r--packages/SettingsLib/SettingsTheme/Android.bp1
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values/styles.xml14
-rw-r--r--packages/SettingsLib/SettingsTransition/Android.bp1
-rw-r--r--packages/SettingsLib/Spa/gallery/AndroidManifest.xml6
-rw-r--r--packages/SettingsLib/Spa/gallery/res/raw/accessibility_shortcut_type_triple_tap.json1959
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt21
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt14
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt5
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt7
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt9
-rw-r--r--packages/SettingsLib/Spa/spa/Android.bp1
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt19
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt195
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt133
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt34
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/Illustration.kt9
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt54
-rw-r--r--packages/SettingsLib/Spa/tests/res/raw/accessibility_shortcut_type_triple_tap.json1959
-rw-r--r--packages/SettingsLib/Spa/tests/res/raw/empty.json0
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/IllustrationTest.kt27
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt3
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt4
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt9
-rw-r--r--packages/SettingsLib/TopIntroPreference/Android.bp1
-rw-r--r--packages/SettingsLib/Utils/Android.bp1
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java61
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java95
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java86
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt103
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt2
-rw-r--r--packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt194
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt6
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt99
-rw-r--r--packages/SystemUI/ktfmt_includes.txt6
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml28
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml1
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml1
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml32
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml17
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml10
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml2
-rw-r--r--packages/SystemUI/res/drawable/media_seekbar_thumb.xml50
-rw-r--r--packages/SystemUI/res/layout/auth_biometric_contents.xml9
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml42
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml2
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_fingerprint_to_error_landscape.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_landscape_base.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_portrait_base_bottomright.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_landscape.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_bottomright.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_topleft.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_landscape.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_bottomright.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_topleft.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_bottomright.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_topleft.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_landscape.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_bottomright.json1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_topleft.json1
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml3
-rw-r--r--packages/SystemUI/res/values-sw720dp-port/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml16
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/res/values/strings.xml8
-rw-r--r--packages/SystemUI/res/values/styles.xml3
-rw-r--r--packages/SystemUI/shared/Android.bp2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AuthKeyguardMessageArea.kt33
-rw-r--r--packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt61
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java69
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java24
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java63
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimationListener.kt23
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java22
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadButton.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt152
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/Text.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/binder/TextViewBinder.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeHost.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepositoryModule.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java181
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java114
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java682
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt120
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java156
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt (renamed from packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt)144
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt (renamed from packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/UserModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt261
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt166
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt110
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt130
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/shared/model/UserModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt220
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserActionViewModel.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt199
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserViewModel.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java)8
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt147
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt181
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java110
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt390
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt93
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt295
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt145
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt)183
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt134
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt217
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt213
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt297
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt55
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt)1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt82
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt27
-rw-r--r--services/Android.bp3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java68
-rw-r--r--services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java2
-rw-r--r--services/companion/java/com/android/server/companion/AssociationStoreImpl.java2
-rw-r--r--services/companion/java/com/android/server/companion/CompanionApplicationController.java8
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java2
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java2
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java2
-rw-r--r--services/companion/java/com/android/server/companion/DataStoreUtils.java2
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java2
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java2
-rw-r--r--services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java2
-rw-r--r--services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java2
-rw-r--r--services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java2
-rw-r--r--services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java2
-rw-r--r--services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java4
-rw-r--r--services/core/java/com/android/server/BootReceiver.java8
-rw-r--r--services/core/java/com/android/server/IntentResolver.java6
-rw-r--r--services/core/java/com/android/server/Watchdog.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java8
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java59
-rw-r--r--services/core/java/com/android/server/am/DropboxRateLimiter.java2
-rw-r--r--services/core/java/com/android/server/am/OWNERS4
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java18
-rw-r--r--services/core/java/com/android/server/app/TEST_MAPPING23
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java19
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java2
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java5
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java159
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java34
-rw-r--r--services/core/java/com/android/server/input/BatteryController.java235
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java21
-rw-r--r--services/core/java/com/android/server/input/NativeInputManagerService.java11
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java14
-rw-r--r--services/core/java/com/android/server/logcat/LogAccessDialogActivity.java1
-rw-r--r--services/core/java/com/android/server/logcat/OWNERS2
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java12
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java25
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java23
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java133
-rw-r--r--services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java2
-rw-r--r--services/core/java/com/android/server/pm/resolution/ComponentResolver.java4
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java5
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java2
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java6
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java8
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java11
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java18
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java6
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java4
-rw-r--r--services/core/java/com/android/server/wm/DragState.java6
-rw-r--r--services/core/java/com/android/server/wm/SafeActivityOptions.java28
-rw-r--r--services/core/java/com/android/server/wm/Task.java8
-rw-r--r--services/core/java/com/android/server/wm/Transition.java9
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java78
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java5
-rw-r--r--services/java/com/android/server/SystemServer.java13
-rw-r--r--services/proguard.flags4
-rw-r--r--services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java237
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java632
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java189
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java164
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java61
-rw-r--r--services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt172
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java66
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java4
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java4
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java3
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt24
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt28
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt16
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt44
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt26
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt36
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt23
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt24
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt27
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt47
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt14
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml12
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml28
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java77
-rw-r--r--tests/Input/src/com/android/test/input/InputDeviceTest.java26
597 files changed, 17750 insertions, 5062 deletions
diff --git a/ApiDocs.bp b/ApiDocs.bp
index e6525c9b8eaa..c87ecde4e5c8 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -132,6 +132,7 @@ droidstubs {
"api-versions-jars-dir",
],
api_levels_sdk_type: "system",
+ extensions_info_file: ":sdk-extensions-info",
}
droidstubs {
@@ -146,6 +147,7 @@ droidstubs {
"packages/modules/Media/apex/aidl/stable",
],
},
+ extensions_info_file: ":sdk-extensions-info",
}
/////////////////////////////////////////////////////////////////////
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 32101c791242..bc2e6dd79fea 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -420,6 +420,7 @@ droidstubs {
"sdk-dir",
"api-versions-jars-dir",
],
+ extensions_info_file: ":sdk-extensions-info",
}
droidstubs {
@@ -432,6 +433,7 @@ droidstubs {
"api-versions-jars-dir",
],
api_levels_sdk_type: "system",
+ extensions_info_file: ":sdk-extensions-info",
}
/////////////////////////////////////////////////////////////////////
diff --git a/boot/Android.bp b/boot/Android.bp
index 3f14ebc4fd92..bb844942b8a7 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -68,6 +68,10 @@ platform_bootclasspath {
module: "com.android.conscrypt-bootclasspath-fragment",
},
{
+ apex: "com.android.healthconnect",
+ module: "com.android.healthconnect-bootclasspath-fragment",
+ },
+ {
apex: "com.android.i18n",
module: "i18n-bootclasspath-fragment",
},
diff --git a/core/api/current.txt b/core/api/current.txt
index 0b41068c95d3..0ab00dfa4a34 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -636,6 +636,7 @@ package android {
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
field public static final int enableOnBackInvokedCallback = 16844396; // 0x101066c
+ field public static final int enableTextStylingShortcuts;
field public static final int enableVrMode = 16844069; // 0x1010525
field public static final int enabled = 16842766; // 0x101000e
field public static final int end = 16843996; // 0x10104dc
@@ -2113,6 +2114,7 @@ package android {
field public static final int addToDictionary = 16908330; // 0x102002a
field public static final int autofill = 16908355; // 0x1020043
field public static final int background = 16908288; // 0x1020000
+ field public static final int bold;
field public static final int button1 = 16908313; // 0x1020019
field public static final int button2 = 16908314; // 0x102001a
field public static final int button3 = 16908315; // 0x102001b
@@ -2138,6 +2140,7 @@ package android {
field public static final int inputExtractAccessories = 16908378; // 0x102005a
field public static final int inputExtractAction = 16908377; // 0x1020059
field public static final int inputExtractEditText = 16908325; // 0x1020025
+ field public static final int italic;
field @Deprecated public static final int keyboardView = 16908326; // 0x1020026
field public static final int list = 16908298; // 0x102000a
field public static final int list_container = 16908351; // 0x102003f
@@ -2169,6 +2172,7 @@ package android {
field public static final int textAssist = 16908353; // 0x1020041
field public static final int title = 16908310; // 0x1020016
field public static final int toggle = 16908311; // 0x1020017
+ field public static final int underline;
field public static final int undo = 16908338; // 0x1020032
field public static final int widget_frame = 16908312; // 0x1020018
}
@@ -48662,6 +48666,9 @@ package android.view {
field public static final int KEYCODE_K = 39; // 0x27
field public static final int KEYCODE_KANA = 218; // 0xda
field public static final int KEYCODE_KATAKANA_HIRAGANA = 215; // 0xd7
+ field public static final int KEYCODE_KEYBOARD_BACKLIGHT_DOWN = 305; // 0x131
+ field public static final int KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE = 307; // 0x133
+ field public static final int KEYCODE_KEYBOARD_BACKLIGHT_UP = 306; // 0x132
field public static final int KEYCODE_L = 40; // 0x28
field public static final int KEYCODE_LANGUAGE_SWITCH = 204; // 0xcc
field public static final int KEYCODE_LAST_CHANNEL = 229; // 0xe5
@@ -51841,6 +51848,7 @@ package android.view.accessibility {
field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
+ field public static final int CONTENT_CHANGE_TYPE_INVALID = 1024; // 0x400
field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
@@ -55992,9 +56000,11 @@ package android.widget {
ctor public EditText(android.content.Context, android.util.AttributeSet, int, int);
method public void extendSelection(int);
method public android.text.Editable getText();
+ method public boolean isStyleShortcutEnabled();
method public void selectAll();
method public void setSelection(int, int);
method public void setSelection(int);
+ method public void setStyleShortcutsEnabled(boolean);
}
public interface ExpandableListAdapter {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 96b8141d2c7d..8c537336bdde 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -523,8 +523,8 @@ package android.app.admin {
method @NonNull public static String operationToString(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, int);
method public void setDeviceOwnerType(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void setProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName, boolean);
@@ -2849,7 +2849,7 @@ package android.view {
method public static String actionToString(int);
method public final void setDisplayId(int);
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
- field public static final int LAST_KEYCODE = 304; // 0x130
+ field public static final int LAST_KEYCODE = 307; // 0x133
}
public final class KeyboardShortcutGroup implements android.os.Parcelable {
@@ -3343,20 +3343,59 @@ package android.window {
ctor public TaskFragmentOrganizer(@NonNull java.util.concurrent.Executor);
method @NonNull public java.util.concurrent.Executor getExecutor();
method @NonNull public android.window.TaskFragmentOrganizerToken getOrganizerToken();
- method public void onActivityReparentedToTask(@NonNull android.window.WindowContainerTransaction, int, @NonNull android.content.Intent, @NonNull android.os.IBinder);
- method public void onTaskFragmentAppeared(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.TaskFragmentInfo);
- method public void onTaskFragmentError(@NonNull android.window.WindowContainerTransaction, @NonNull android.os.IBinder, @Nullable android.window.TaskFragmentInfo, int, @NonNull Throwable);
- method public void onTaskFragmentInfoChanged(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.TaskFragmentInfo);
- method public void onTaskFragmentParentInfoChanged(@NonNull android.window.WindowContainerTransaction, int, @NonNull android.content.res.Configuration);
- method public void onTaskFragmentVanished(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.TaskFragmentInfo);
+ method public void onTransactionReady(@NonNull android.window.TaskFragmentTransaction);
method @CallSuper public void registerOrganizer();
method @CallSuper public void unregisterOrganizer();
+ field public static final String KEY_ERROR_CALLBACK_OP_TYPE = "operation_type";
+ field public static final String KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO = "task_fragment_info";
+ field public static final String KEY_ERROR_CALLBACK_THROWABLE = "fragment_throwable";
}
public final class TaskFragmentOrganizerToken implements android.os.Parcelable {
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentOrganizerToken> CREATOR;
}
+ public final class TaskFragmentTransaction implements android.os.Parcelable {
+ ctor public TaskFragmentTransaction();
+ method public void addChange(@Nullable android.window.TaskFragmentTransaction.Change);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.window.TaskFragmentTransaction.Change> getChanges();
+ method @NonNull public android.os.IBinder getTransactionToken();
+ method public boolean isEmpty();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentTransaction> CREATOR;
+ field public static final int TYPE_ACTIVITY_REPARENTED_TO_TASK = 6; // 0x6
+ field public static final int TYPE_TASK_FRAGMENT_APPEARED = 1; // 0x1
+ field public static final int TYPE_TASK_FRAGMENT_ERROR = 5; // 0x5
+ field public static final int TYPE_TASK_FRAGMENT_INFO_CHANGED = 2; // 0x2
+ field public static final int TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED = 4; // 0x4
+ field public static final int TYPE_TASK_FRAGMENT_VANISHED = 3; // 0x3
+ }
+
+ public static final class TaskFragmentTransaction.Change implements android.os.Parcelable {
+ ctor public TaskFragmentTransaction.Change(int);
+ method public int describeContents();
+ method @Nullable public android.content.Intent getActivityIntent();
+ method @Nullable public android.os.IBinder getActivityToken();
+ method @NonNull public android.os.Bundle getErrorBundle();
+ method @Nullable public android.os.IBinder getErrorCallbackToken();
+ method @Nullable public android.content.res.Configuration getTaskConfiguration();
+ method @Nullable public android.window.TaskFragmentInfo getTaskFragmentInfo();
+ method @Nullable public android.os.IBinder getTaskFragmentToken();
+ method public int getTaskId();
+ method public int getType();
+ method @NonNull public android.window.TaskFragmentTransaction.Change setActivityIntent(@NonNull android.content.Intent);
+ method @NonNull public android.window.TaskFragmentTransaction.Change setActivityToken(@NonNull android.os.IBinder);
+ method @NonNull public android.window.TaskFragmentTransaction.Change setErrorBundle(@NonNull android.os.Bundle);
+ method @NonNull public android.window.TaskFragmentTransaction.Change setErrorCallbackToken(@Nullable android.os.IBinder);
+ method @NonNull public android.window.TaskFragmentTransaction.Change setTaskConfiguration(@NonNull android.content.res.Configuration);
+ method @NonNull public android.window.TaskFragmentTransaction.Change setTaskFragmentInfo(@NonNull android.window.TaskFragmentInfo);
+ method @NonNull public android.window.TaskFragmentTransaction.Change setTaskFragmentToken(@NonNull android.os.IBinder);
+ method @NonNull public android.window.TaskFragmentTransaction.Change setTaskId(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentTransaction.Change> CREATOR;
+ }
+
public class TaskOrganizer extends android.window.WindowOrganizer {
ctor public TaskOrganizer();
method @BinderThread public void copySplashScreenView(int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 82eb62d070c3..ee5d2b7ba327 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1176,9 +1176,16 @@ public final class ActivityThread extends ClientTransactionHandler
data.mSerializedSystemFontMap = serializedSystemFontMap;
data.startRequestedElapsedTime = startRequestedElapsedTime;
data.startRequestedUptime = startRequestedUptime;
+ updateCompatOverrideScale(compatInfo);
+ CompatibilityInfo.applyOverrideScaleIfNeeded(config);
sendMessage(H.BIND_APPLICATION, data);
}
+ private void updateCompatOverrideScale(CompatibilityInfo info) {
+ CompatibilityInfo.setOverrideInvertedScale(
+ info.hasOverrideScaling() ? info.applicationInvertedScale : 1f);
+ }
+
public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = entryPoint;
@@ -1755,6 +1762,7 @@ public final class ActivityThread extends ClientTransactionHandler
UpdateCompatibilityData ucd = new UpdateCompatibilityData();
ucd.pkg = pkg;
ucd.info = info;
+ updateCompatOverrideScale(info);
sendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd);
}
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 877e7d3b3bf7..6f4bb456c478 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -145,7 +145,10 @@ class ActivityTransitionState {
* that it is preserved through activty destroy and restore.
*/
private ArrayList<String> getPendingExitNames() {
- if (mPendingExitNames == null && mEnterTransitionCoordinator != null) {
+ if (mPendingExitNames == null
+ && mEnterTransitionCoordinator != null
+ && !mEnterTransitionCoordinator.isReturning()
+ ) {
mPendingExitNames = mEnterTransitionCoordinator.getPendingExitSharedElementNames();
}
return mPendingExitNames;
@@ -202,6 +205,7 @@ class ActivityTransitionState {
restoreExitedViews();
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
}
+ getPendingExitNames(); // Set mPendingExitNames before resetting mEnterTransitionCoordinator
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
mEnterActivityOptions.isCrossTask());
@@ -250,6 +254,7 @@ class ActivityTransitionState {
public void onStop(Activity activity) {
restoreExitedViews();
if (mEnterTransitionCoordinator != null) {
+ getPendingExitNames(); // Set mPendingExitNames before clearing
mEnterTransitionCoordinator.stop();
mEnterTransitionCoordinator = null;
}
@@ -275,6 +280,7 @@ class ActivityTransitionState {
restoreReenteringViews();
} else if (mEnterTransitionCoordinator.isReturning()) {
mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
+ getPendingExitNames(); // Set mPendingExitNames before clearing
mEnterTransitionCoordinator = null;
});
}
@@ -374,6 +380,7 @@ class ActivityTransitionState {
}
public void startExitOutTransition(Activity activity, Bundle options) {
+ getPendingExitNames(); // Set mPendingExitNames before clearing mEnterTransitionCoordinator
mEnterTransitionCoordinator = null;
if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) ||
mExitTransitionCoordinators == null) {
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 2b245aae915f..5c29eb35fcee 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -95,8 +95,11 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
mId = null;
}
- mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mName = getTrimmedString(mName.toString());
+ if (in.readByte() != 0) {
+ mName = getTrimmedString(in.readString());
+ } else {
+ mName = "";
+ }
if (in.readByte() != 0) {
mDescription = getTrimmedString(in.readString());
} else {
@@ -122,7 +125,12 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
dest.writeByte((byte) 0);
}
- TextUtils.writeToParcel(mName.toString(), dest, flags);
+ if (mName != null) {
+ dest.writeByte((byte) 1);
+ dest.writeString(mName.toString());
+ } else {
+ dest.writeByte((byte) 0);
+ }
if (mDescription != null) {
dest.writeByte((byte) 1);
dest.writeString(mDescription);
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 397c8e010bad..39d77c49eea9 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -462,14 +462,31 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
/** @hide */
public void scale(float scale) {
- mBounds.scale(scale);
- mMaxBounds.scale(scale);
+ scaleBounds(scale, mBounds);
+ scaleBounds(scale, mMaxBounds);
if (mAppBounds != null) {
- mAppBounds.scale(scale);
+ scaleBounds(scale, mAppBounds);
}
}
/**
+ * Size based scaling. This avoid inconsistent length when rounding 4 sides.
+ * E.g. left=12, right=18, scale=0.8. The scaled width can be:
+ * int((right - left) * scale + 0.5) = int(4.8 + 0.5) = 5
+ * But with rounding both left and right, the width will be inconsistent:
+ * int(right * scale + 0.5) - int(left * scale + 0.5) = int(14.9) - int(10.1) = 4
+ * @hide
+ */
+ private static void scaleBounds(float scale, Rect bounds) {
+ final int w = bounds.width();
+ final int h = bounds.height();
+ bounds.left = (int) (bounds.left * scale + .5f);
+ bounds.top = (int) (bounds.top * scale + .5f);
+ bounds.right = bounds.left + (int) (w * scale + .5f);
+ bounds.bottom = bounds.top + (int) (h * scale + .5f);
+ }
+
+ /**
* Copies the fields from delta into this Configuration object, keeping
* track of which ones have changed. Any undefined fields in {@code delta}
* are ignored and not copied in to the current Configuration.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 5875e2d635f4..ab11d6a3e562 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8689,7 +8689,6 @@ public class DevicePolicyManager {
* and no accounts.
*
* @param who the component name to be registered as device owner.
- * @param ownerName the human readable name of the institution that owns this device.
* @param userId ID of the user on which the device owner runs.
*
* @return whether the package was successfully registered as the device owner.
@@ -8701,11 +8700,10 @@ public class DevicePolicyManager {
*/
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- public boolean setDeviceOwner(@NonNull ComponentName who, @Nullable String ownerName,
- @UserIdInt int userId) {
+ public boolean setDeviceOwner(@NonNull ComponentName who, @UserIdInt int userId) {
if (mService != null) {
try {
- return mService.setDeviceOwner(who, ownerName, userId,
+ return mService.setDeviceOwner(who, userId,
/* setProfileOwnerOnCurrentUserIfNecessary= */ true);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -8715,7 +8713,7 @@ public class DevicePolicyManager {
}
/**
- * Same as {@link #setDeviceOwner(ComponentName, String, int)}, but without setting the profile
+ * Same as {@link #setDeviceOwner(ComponentName, int)}, but without setting the profile
* owner on current user when running on headless system user mode - should be used only by
* testing infra.
*
@@ -8724,10 +8722,10 @@ public class DevicePolicyManager {
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public boolean setDeviceOwnerOnly(
- @NonNull ComponentName who, @Nullable String ownerName, @UserIdInt int userId) {
+ @NonNull ComponentName who, @UserIdInt int userId) {
if (mService != null) {
try {
- return mService.setDeviceOwner(who, ownerName, userId,
+ return mService.setDeviceOwner(who, userId,
/* setProfileOwnerOnCurrentUserIfNecessary= */ false);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -8971,7 +8969,7 @@ public class DevicePolicyManager {
try {
final int myUserId = myUserId();
mService.setActiveAdmin(admin, false, myUserId);
- return mService.setProfileOwner(admin, ownerName, myUserId);
+ return mService.setProfileOwner(admin, myUserId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -9033,20 +9031,16 @@ public class DevicePolicyManager {
* - the caller is SYSTEM_UID.
* - or the caller is the shell uid, and there are no accounts on the specified user.
* @param admin the component name to be registered as profile owner.
- * @param ownerName the human readable name of the organisation associated with this DPM.
* @param userHandle the userId to set the profile owner for.
* @return whether the component was successfully registered as the profile owner.
* @throws IllegalArgumentException if admin is null, the package isn't installed, or the
* preconditions mentioned are not met.
*/
- public boolean setProfileOwner(@NonNull ComponentName admin, @Deprecated String ownerName,
- int userHandle) throws IllegalArgumentException {
+ public boolean setProfileOwner(@NonNull ComponentName admin, int userHandle)
+ throws IllegalArgumentException {
if (mService != null) {
try {
- if (ownerName == null) {
- ownerName = "";
- }
- return mService.setProfileOwner(admin, ownerName, userHandle);
+ return mService.setProfileOwner(admin, userHandle);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -15429,7 +15423,7 @@ public class DevicePolicyManager {
*
* @see #setWifiSsidPolicy(WifiSsidPolicy)
* @throws SecurityException if the caller is not a device owner or a profile owner on
- * an organization-owned managed profile or a system app.
+ * an organization-owned managed profile.
*/
@Nullable
public WifiSsidPolicy getWifiSsidPolicy() {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index fea77703191e..75bfc2554442 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -168,14 +168,14 @@ interface IDevicePolicyManager {
void reportKeyguardDismissed(int userHandle);
void reportKeyguardSecured(int userHandle);
- boolean setDeviceOwner(in ComponentName who, String ownerName, int userId, boolean setProfileOwnerOnCurrentUserIfNecessary);
+ boolean setDeviceOwner(in ComponentName who, int userId, boolean setProfileOwnerOnCurrentUserIfNecessary);
ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
boolean hasDeviceOwner();
String getDeviceOwnerName();
void clearDeviceOwner(String packageName);
int getDeviceOwnerUserId();
- boolean setProfileOwner(in ComponentName who, String ownerName, int userHandle);
+ boolean setProfileOwner(in ComponentName who, int userHandle);
ComponentName getProfileOwnerAsUser(int userHandle);
ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent(in UserHandle userHandle);
boolean isSupervisionComponent(in ComponentName who);
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 5a3ad310b4d6..1c490be08381 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Parcel;
@@ -40,6 +41,7 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
@Override
public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
client.updatePendingActivityConfiguration(token, mConfiguration);
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index b3e34b366082..c09461c1fb52 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ResultInfo;
+import android.content.res.CompatibilityInfo;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Trace;
@@ -56,6 +57,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem {
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mConfig);
mActivityClientRecord = client.prepareRelaunchActivity(token, mPendingResults,
mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow);
}
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 2acff497b293..49a1c669537c 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -18,6 +18,7 @@ package android.app.servertransaction;
import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Parcel;
@@ -34,6 +35,7 @@ public class ConfigurationChangeItem extends ClientTransactionItem {
@Override
public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
client.updatePendingConfiguration(mConfiguration);
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 03d6e27d6a8d..7e4db81b8d62 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -30,6 +30,7 @@ import android.app.ResultInfo;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.BaseBundle;
import android.os.Bundle;
@@ -81,6 +82,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
public void preExecute(ClientTransactionHandler client, IBinder token) {
client.countLaunchingActivities(1);
client.updateProcessState(mProcState, false);
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mCurConfig);
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mOverrideConfig);
client.updatePendingConfiguration(mCurConfig);
if (mActivityClientController != null) {
ActivityClient.setActivityClientController(mActivityClientController);
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 2893ff21c3f2..f13bd748f486 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Parcel;
@@ -40,6 +41,7 @@ public class MoveToDisplayItem extends ActivityTransactionItem {
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mConfiguration);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
client.updatePendingActivityConfiguration(token, mConfiguration);
diff --git a/core/java/android/companion/BluetoothDeviceFilterUtils.java b/core/java/android/companion/BluetoothDeviceFilterUtils.java
index ef5f84c42a16..a30fd33072c5 100644
--- a/core/java/android/companion/BluetoothDeviceFilterUtils.java
+++ b/core/java/android/companion/BluetoothDeviceFilterUtils.java
@@ -43,7 +43,7 @@ public class BluetoothDeviceFilterUtils {
private BluetoothDeviceFilterUtils() {}
private static final boolean DEBUG = false;
- private static final String LOG_TAG = "BluetoothDeviceFilterUtils";
+ private static final String LOG_TAG = "CDM_BluetoothDeviceFilterUtils";
@Nullable
static String patternToString(@Nullable Pattern p) {
diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java
index 2934cd27da19..b178699eeef0 100644
--- a/core/java/android/companion/BluetoothLeDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLeDeviceFilter.java
@@ -54,7 +54,7 @@ import java.util.regex.Pattern;
public final class BluetoothLeDeviceFilter implements DeviceFilter<ScanResult> {
private static final boolean DEBUG = false;
- private static final String LOG_TAG = "BluetoothLeDeviceFilter";
+ private static final String LOG_TAG = "CDM_BluetoothLeDeviceFilter";
private static final int RENAME_PREFIX_LENGTH_LIMIT = 10;
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index c1a3c1934dbb..357bf590f267 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -81,7 +81,7 @@ import java.util.function.Consumer;
public final class CompanionDeviceManager {
private static final boolean DEBUG = false;
- private static final String LOG_TAG = "CompanionDeviceManager";
+ private static final String LOG_TAG = "CDM_CompanionDeviceManager";
/**
* The result code to propagate back to the originating activity, indicates the association
@@ -709,27 +709,29 @@ public final class CompanionDeviceManager {
/**
* Register to receive callbacks whenever the associated device comes in and out of range.
*
- * The provided device must be {@link #associate associated} with the calling app before
- * calling this method.
+ * <p>The provided device must be {@link #associate associated} with the calling app before
+ * calling this method.</p>
*
- * Caller must implement a single {@link CompanionDeviceService} which will be bound to and
+ * <p>Caller must implement a single {@link CompanionDeviceService} which will be bound to and
* receive callbacks to {@link CompanionDeviceService#onDeviceAppeared} and
* {@link CompanionDeviceService#onDeviceDisappeared}.
- * The app doesn't need to remain running in order to receive its callbacks.
+ * The app doesn't need to remain running in order to receive its callbacks.</p>
*
- * Calling app must declare uses-permission
- * {@link android.Manifest.permission#REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE}.
+ * <p>Calling app must declare uses-permission
+ * {@link android.Manifest.permission#REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE}.</p>
*
- * Calling app must check for feature presence of
- * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} before calling this API.
+ * <p>Calling app must check for feature presence of
+ * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} before calling this API.</p>
+ *
+ * <p>For Bluetooth LE devices, this is based on scanning for device with the given address.
+ * The system will scan for the device when Bluetooth is ON or Bluetooth scanning is ON.</p>
*
- * For Bluetooth LE devices this is based on scanning for device with the given address.
- * For Bluetooth classic devices this is triggered when the device connects/disconnects.
- * WiFi devices are not supported.
+ * <p>For Bluetooth classic devices this is triggered when the device connects/disconnects.
+ * WiFi devices are not supported.</p>
*
- * If a Bluetooth LE device wants to use a rotating mac address, it is recommended to use
+ * <p>If a Bluetooth LE device wants to use a rotating mac address, it is recommended to use
* Resolvable Private Address, and ensure the device is bonded to the phone so that android OS
- * is able to resolve the address.
+ * is able to resolve the address.</p>
*
* @param deviceAddress a previously-associated companion device's address
*
diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java
index a79983a653f0..3a7dd8ed3bc0 100644
--- a/core/java/android/companion/CompanionDeviceService.java
+++ b/core/java/android/companion/CompanionDeviceService.java
@@ -99,7 +99,7 @@ import java.util.concurrent.Executor;
*/
public abstract class CompanionDeviceService extends Service {
- private static final String LOG_TAG = "CompanionDeviceService";
+ private static final String LOG_TAG = "CDM_CompanionDeviceService";
/**
* An intent action for a service to be bound whenever this app's companion device(s)
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index e146bb9e20e2..6ce22422643e 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -29,6 +29,7 @@ import android.os.Build.VERSION_CODES;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayMetrics;
+import android.util.MergedConfiguration;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.MotionEvent;
@@ -111,6 +112,9 @@ public class CompatibilityInfo implements Parcelable {
*/
public final float applicationInvertedScale;
+ /** The process level override inverted scale. See {@link #HAS_OVERRIDE_SCALING}. */
+ private static float sOverrideInvertedScale = 1f;
+
@UnsupportedAppUsage
@Deprecated
public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw,
@@ -125,6 +129,15 @@ public class CompatibilityInfo implements Parcelable {
if (appInfo.targetSdkVersion < VERSION_CODES.O) {
compatFlags |= NEEDS_COMPAT_RES;
}
+ if (overrideScale != 1.0f) {
+ applicationScale = overrideScale;
+ applicationInvertedScale = 1.0f / overrideScale;
+ applicationDensity = (int) ((DisplayMetrics.DENSITY_DEVICE_STABLE
+ * applicationInvertedScale) + .5f);
+ mCompatibilityFlags = NEVER_NEEDS_COMPAT | HAS_OVERRIDE_SCALING;
+ // Override scale has the highest priority. So ignore other compatibility attributes.
+ return;
+ }
if (appInfo.requiresSmallestWidthDp != 0 || appInfo.compatibleWidthLimitDp != 0
|| appInfo.largestWidthLimitDp != 0) {
// New style screen requirements spec.
@@ -254,13 +267,7 @@ public class CompatibilityInfo implements Parcelable {
compatFlags |= NEVER_NEEDS_COMPAT;
}
- if (overrideScale != 1.0f) {
- applicationScale = overrideScale;
- applicationInvertedScale = 1.0f / overrideScale;
- applicationDensity = (int) ((DisplayMetrics.DENSITY_DEVICE_STABLE
- * applicationInvertedScale) + .5f);
- compatFlags |= HAS_OVERRIDE_SCALING;
- } else if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
+ if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
applicationDensity = DisplayMetrics.DENSITY_DEVICE;
applicationScale = 1.0f;
applicationInvertedScale = 1.0f;
@@ -296,9 +303,14 @@ public class CompatibilityInfo implements Parcelable {
*/
@UnsupportedAppUsage
public boolean isScalingRequired() {
- return (mCompatibilityFlags & (SCALING_REQUIRED | HAS_OVERRIDE_SCALING)) != 0;
+ return (mCompatibilityFlags & SCALING_REQUIRED) != 0;
}
-
+
+ /** Returns {@code true} if {@link #sOverrideInvertedScale} should be set. */
+ public boolean hasOverrideScaling() {
+ return (mCompatibilityFlags & HAS_OVERRIDE_SCALING) != 0;
+ }
+
@UnsupportedAppUsage
public boolean supportsScreen() {
return (mCompatibilityFlags&NEEDS_SCREEN_COMPAT) == 0;
@@ -513,7 +525,19 @@ public class CompatibilityInfo implements Parcelable {
}
}
+ /** Applies the compatibility adjustment to the display metrics. */
+ public void applyDisplayMetricsIfNeeded(DisplayMetrics inoutDm, boolean applyToSize) {
+ if (hasOverrideScale()) {
+ scaleDisplayMetrics(sOverrideInvertedScale, inoutDm, applyToSize);
+ return;
+ }
+ if (!equals(DEFAULT_COMPATIBILITY_INFO)) {
+ applyToDisplayMetrics(inoutDm);
+ }
+ }
+
public void applyToDisplayMetrics(DisplayMetrics inoutDm) {
+ if (hasOverrideScale()) return;
if (!supportsScreen()) {
// This is a larger screen device and the app is not
// compatible with large screens, so diddle it.
@@ -524,18 +548,26 @@ public class CompatibilityInfo implements Parcelable {
}
if (isScalingRequired()) {
- float invertedRatio = applicationInvertedScale;
- inoutDm.density = inoutDm.noncompatDensity * invertedRatio;
- inoutDm.densityDpi = (int)((inoutDm.noncompatDensityDpi * invertedRatio) + .5f);
- inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * invertedRatio;
- inoutDm.xdpi = inoutDm.noncompatXdpi * invertedRatio;
- inoutDm.ydpi = inoutDm.noncompatYdpi * invertedRatio;
+ scaleDisplayMetrics(applicationInvertedScale, inoutDm, true /* applyToSize */);
+ }
+ }
+
+ /** Scales the density of the given display metrics. */
+ private static void scaleDisplayMetrics(float invertedRatio, DisplayMetrics inoutDm,
+ boolean applyToSize) {
+ inoutDm.density = inoutDm.noncompatDensity * invertedRatio;
+ inoutDm.densityDpi = (int) ((inoutDm.noncompatDensityDpi * invertedRatio) + .5f);
+ inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * invertedRatio;
+ inoutDm.xdpi = inoutDm.noncompatXdpi * invertedRatio;
+ inoutDm.ydpi = inoutDm.noncompatYdpi * invertedRatio;
+ if (applyToSize) {
inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f);
inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f);
}
}
public void applyToConfiguration(int displayDensity, Configuration inoutConfig) {
+ if (hasOverrideScale()) return;
if (!supportsScreen()) {
// This is a larger screen device and the app is not
// compatible with large screens, so we are forcing it to
@@ -549,12 +581,45 @@ public class CompatibilityInfo implements Parcelable {
}
inoutConfig.densityDpi = displayDensity;
if (isScalingRequired()) {
- float invertedRatio = applicationInvertedScale;
- inoutConfig.densityDpi = (int)((inoutConfig.densityDpi * invertedRatio) + .5f);
- inoutConfig.windowConfiguration.scale(invertedRatio);
+ scaleConfiguration(applicationInvertedScale, inoutConfig);
}
}
+ /** Scales the density and bounds of the given configuration. */
+ public static void scaleConfiguration(float invertedRatio, Configuration inoutConfig) {
+ inoutConfig.densityDpi = (int) ((inoutConfig.densityDpi * invertedRatio) + .5f);
+ inoutConfig.windowConfiguration.scale(invertedRatio);
+ }
+
+ /** @see #sOverrideInvertedScale */
+ public static void applyOverrideScaleIfNeeded(Configuration config) {
+ if (!hasOverrideScale()) return;
+ scaleConfiguration(sOverrideInvertedScale, config);
+ }
+
+ /** @see #sOverrideInvertedScale */
+ public static void applyOverrideScaleIfNeeded(MergedConfiguration mergedConfig) {
+ if (!hasOverrideScale()) return;
+ scaleConfiguration(sOverrideInvertedScale, mergedConfig.getGlobalConfiguration());
+ scaleConfiguration(sOverrideInvertedScale, mergedConfig.getOverrideConfiguration());
+ scaleConfiguration(sOverrideInvertedScale, mergedConfig.getMergedConfiguration());
+ }
+
+ /** Returns {@code true} if this process is in a environment with override scale. */
+ private static boolean hasOverrideScale() {
+ return sOverrideInvertedScale != 1f;
+ }
+
+ /** @see #sOverrideInvertedScale */
+ public static void setOverrideInvertedScale(float invertedRatio) {
+ sOverrideInvertedScale = invertedRatio;
+ }
+
+ /** @see #sOverrideInvertedScale */
+ public static float getOverrideInvertedScale() {
+ return sOverrideInvertedScale;
+ }
+
/**
* Compute the frame Rect for applications runs under compatibility mode.
*
@@ -632,6 +697,10 @@ public class CompatibilityInfo implements Parcelable {
sb.append(applicationScale);
sb.append("x");
}
+ if (hasOverrideScaling()) {
+ sb.append(" overrideInvScale=");
+ sb.append(applicationInvertedScale);
+ }
if (!supportsScreen()) {
sb.append(" resizing");
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 18d86d69206f..1c4898a4c8d0 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -882,6 +882,7 @@ public class SystemSensorManager extends SensorManager {
}
// Indicate if the discontinuity count changed
+ t.firstEventAfterDiscontinuity = false;
if (t.sensor.getType() == Sensor.TYPE_HEAD_TRACKER) {
final int lastCount = mSensorDiscontinuityCounts.get(handle);
final int curCount = Float.floatToIntBits(values[6]);
diff --git a/core/java/android/hardware/input/IInputDeviceBatteryListener.aidl b/core/java/android/hardware/input/IInputDeviceBatteryListener.aidl
new file mode 100644
index 000000000000..dc5a96684606
--- /dev/null
+++ b/core/java/android/hardware/input/IInputDeviceBatteryListener.aidl
@@ -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 android.hardware.input;
+
+/** @hide */
+oneway interface IInputDeviceBatteryListener {
+
+ /**
+ * Called when there is a change in battery state for a monitored device. This will be called
+ * immediately after the listener is successfully registered for a new device via IInputManager.
+ * The parameters are values exposed through {@link android.hardware.BatteryState}.
+ */
+ void onBatteryStateChanged(int deviceId, boolean isBatteryPresent, int status, float capacity,
+ long eventTime);
+}
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c9a096cb1a1f..36297b991912 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -20,6 +20,7 @@ import android.graphics.Rect;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.IInputDevicesChangedListener;
+import android.hardware.input.IInputDeviceBatteryListener;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.TouchCalibration;
import android.os.CombinedVibration;
@@ -157,4 +158,8 @@ interface IInputManager {
void closeLightSession(int deviceId, in IBinder token);
void cancelCurrentTouch();
+
+ void registerBatteryListener(int deviceId, IInputDeviceBatteryListener listener);
+
+ void unregisterBatteryListener(int deviceId, IInputDeviceBatteryListener listener);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6ad1c7274ee5..8960d2a336b1 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -30,6 +30,7 @@ import android.app.ActivityThread;
import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.hardware.BatteryState;
import android.hardware.SensorManager;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
@@ -65,6 +66,7 @@ import android.view.PointerIcon;
import android.view.VerifiedInputEvent;
import android.view.WindowManager.LayoutParams;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
@@ -72,6 +74,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* Provides information about input devices and available key layouts.
@@ -103,6 +107,14 @@ public final class InputManager {
private TabletModeChangedListener mTabletModeChangedListener;
private List<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners;
+ private final Object mBatteryListenersLock = new Object();
+ // Maps a deviceId whose battery is currently being monitored to an entry containing the
+ // registered listeners for that device.
+ @GuardedBy("mBatteryListenersLock")
+ private SparseArray<RegisteredBatteryListeners> mBatteryListeners;
+ @GuardedBy("mBatteryListenersLock")
+ private IInputDeviceBatteryListener mInputDeviceBatteryListener;
+
private InputDeviceSensorManager mInputDeviceSensorManager;
/**
* Broadcast Action: Query available keyboard layouts.
@@ -1707,6 +1719,129 @@ public final class InputManager {
}
/**
+ * Adds a battery listener to be notified about {@link BatteryState} changes for an input
+ * device. The same listener can be registered for multiple input devices.
+ * The listener will be notified of the initial battery state of the device after it is
+ * successfully registered.
+ * @param deviceId the input device that should be monitored
+ * @param executor an executor on which the callback will be called
+ * @param listener the {@link InputDeviceBatteryListener}
+ * @see #removeInputDeviceBatteryListener(int, InputDeviceBatteryListener)
+ * @hide
+ */
+ public void addInputDeviceBatteryListener(int deviceId, @NonNull Executor executor,
+ @NonNull InputDeviceBatteryListener listener) {
+ Objects.requireNonNull(executor, "executor should not be null");
+ Objects.requireNonNull(listener, "listener should not be null");
+
+ synchronized (mBatteryListenersLock) {
+ if (mBatteryListeners == null) {
+ mBatteryListeners = new SparseArray<>();
+ mInputDeviceBatteryListener = new LocalInputDeviceBatteryListener();
+ }
+ RegisteredBatteryListeners listenersForDevice = mBatteryListeners.get(deviceId);
+ if (listenersForDevice == null) {
+ // The deviceId is currently not being monitored for battery changes.
+ // Start monitoring the device.
+ listenersForDevice = new RegisteredBatteryListeners();
+ mBatteryListeners.put(deviceId, listenersForDevice);
+ try {
+ mIm.registerBatteryListener(deviceId, mInputDeviceBatteryListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ // The deviceId is already being monitored for battery changes.
+ // Ensure that the listener is not already registered.
+ for (InputDeviceBatteryListenerDelegate delegate : listenersForDevice.mDelegates) {
+ if (Objects.equals(listener, delegate.mListener)) {
+ throw new IllegalArgumentException(
+ "Attempting to register an InputDeviceBatteryListener that has "
+ + "already been registered for deviceId: "
+ + deviceId);
+ }
+ }
+ }
+ final InputDeviceBatteryListenerDelegate delegate =
+ new InputDeviceBatteryListenerDelegate(listener, executor);
+ listenersForDevice.mDelegates.add(delegate);
+
+ // Notify the listener immediately if we already have the latest battery state.
+ if (listenersForDevice.mLatestBatteryState != null) {
+ delegate.notifyBatteryStateChanged(listenersForDevice.mLatestBatteryState);
+ }
+ }
+ }
+
+ /**
+ * Removes a previously registered battery listener for an input device.
+ * @see #addInputDeviceBatteryListener(int, Executor, InputDeviceBatteryListener)
+ * @hide
+ */
+ public void removeInputDeviceBatteryListener(int deviceId,
+ @NonNull InputDeviceBatteryListener listener) {
+ Objects.requireNonNull(listener, "listener should not be null");
+
+ synchronized (mBatteryListenersLock) {
+ if (mBatteryListeners == null) {
+ return;
+ }
+ RegisteredBatteryListeners listenersForDevice = mBatteryListeners.get(deviceId);
+ if (listenersForDevice == null) {
+ // The deviceId is not currently being monitored.
+ return;
+ }
+ final List<InputDeviceBatteryListenerDelegate> delegates =
+ listenersForDevice.mDelegates;
+ for (int i = 0; i < delegates.size();) {
+ if (Objects.equals(listener, delegates.get(i).mListener)) {
+ delegates.remove(i);
+ continue;
+ }
+ i++;
+ }
+ if (!delegates.isEmpty()) {
+ return;
+ }
+
+ // There are no more battery listeners for this deviceId. Stop monitoring this device.
+ mBatteryListeners.remove(deviceId);
+ try {
+ mIm.unregisterBatteryListener(deviceId, mInputDeviceBatteryListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (mBatteryListeners.size() == 0) {
+ // There are no more devices being monitored, so the registered
+ // IInputDeviceBatteryListener will be automatically dropped by the server.
+ mBatteryListeners = null;
+ mInputDeviceBatteryListener = null;
+ }
+ }
+ }
+
+ /**
+ * A callback used to be notified about battery state changes for an input device. The
+ * {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the
+ * listener is successfully registered to provide the initial battery state of the device.
+ * @see InputDevice#getBatteryState()
+ * @see #addInputDeviceBatteryListener(int, Executor, InputDeviceBatteryListener)
+ * @see #removeInputDeviceBatteryListener(int, InputDeviceBatteryListener)
+ * @hide
+ */
+ public interface InputDeviceBatteryListener {
+ /**
+ * Called when the battery state of an input device changes.
+ * @param deviceId the input device for which the battery changed.
+ * @param eventTimeMillis the time (in ms) when the battery change took place.
+ * This timestamp is in the {@link SystemClock#uptimeMillis()} time base.
+ * @param batteryState the new battery state, never null.
+ */
+ void onBatteryStateChanged(
+ int deviceId, long eventTimeMillis, @NonNull BatteryState batteryState);
+ }
+
+ /**
* Listens for changes in input devices.
*/
public interface InputDeviceListener {
@@ -1816,4 +1951,76 @@ public final class InputManager {
}
}
}
+
+ private static final class LocalBatteryState extends BatteryState {
+ final int mDeviceId;
+ final boolean mIsPresent;
+ final int mStatus;
+ final float mCapacity;
+ final long mEventTime;
+
+ LocalBatteryState(int deviceId, boolean isPresent, int status, float capacity,
+ long eventTime) {
+ mDeviceId = deviceId;
+ mIsPresent = isPresent;
+ mStatus = status;
+ mCapacity = capacity;
+ mEventTime = eventTime;
+ }
+
+ @Override
+ public boolean isPresent() {
+ return mIsPresent;
+ }
+
+ @Override
+ public int getStatus() {
+ return mStatus;
+ }
+
+ @Override
+ public float getCapacity() {
+ return mCapacity;
+ }
+ }
+
+ private static final class RegisteredBatteryListeners {
+ final List<InputDeviceBatteryListenerDelegate> mDelegates = new ArrayList<>();
+ LocalBatteryState mLatestBatteryState;
+ }
+
+ private static final class InputDeviceBatteryListenerDelegate {
+ final InputDeviceBatteryListener mListener;
+ final Executor mExecutor;
+
+ InputDeviceBatteryListenerDelegate(InputDeviceBatteryListener listener, Executor executor) {
+ mListener = listener;
+ mExecutor = executor;
+ }
+
+ void notifyBatteryStateChanged(LocalBatteryState batteryState) {
+ mExecutor.execute(() ->
+ mListener.onBatteryStateChanged(batteryState.mDeviceId, batteryState.mEventTime,
+ batteryState));
+ }
+ }
+
+ private class LocalInputDeviceBatteryListener extends IInputDeviceBatteryListener.Stub {
+ @Override
+ public void onBatteryStateChanged(int deviceId, boolean isBatteryPresent, int status,
+ float capacity, long eventTime) {
+ synchronized (mBatteryListenersLock) {
+ if (mBatteryListeners == null) return;
+ final RegisteredBatteryListeners entry = mBatteryListeners.get(deviceId);
+ if (entry == null) return;
+
+ entry.mLatestBatteryState =
+ new LocalBatteryState(
+ deviceId, isBatteryPresent, status, capacity, eventTime);
+ for (InputDeviceBatteryListenerDelegate delegate : entry.mDelegates) {
+ delegate.notifyBatteryStateChanged(entry.mLatestBatteryState);
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 7105b1ad8b38..0c5f77860750 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -33,6 +33,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.Closeable;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -448,6 +449,16 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
return proto.getBytes();
}
+ /**
+ * Writes contents in a binary protobuffer format, using
+ * the android.os.BatteryUsageStatsAtomsProto proto.
+ */
+ public void dumpToProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ writeStatsProto(proto, /* max size */ Integer.MAX_VALUE);
+ proto.flush();
+ }
+
@NonNull
private void writeStatsProto(ProtoOutputStream proto, int maxRawSize) {
final AggregateBatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 237f6ed819f6..71bc4b36f4e0 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -1077,13 +1077,13 @@ public abstract class VibrationEffect implements Parcelable {
}
/**
- * Compose all of the added primitives together into a single {@link VibrationEffect}.
+ * Compose all of the added elements together into a single {@link VibrationEffect}.
*
* <p>The {@link Composition} object is still valid after this call, so you can continue
- * adding more primitives to it and generating more {@link VibrationEffect}s by calling this
+ * adding more elements to it and generating more {@link VibrationEffect}s by calling this
* method again.
*
- * @return The {@link VibrationEffect} resulting from the composition of the primitives.
+ * @return The {@link VibrationEffect} resulting from the composition of the elements.
*/
@NonNull
public VibrationEffect compose() {
diff --git a/core/java/android/os/logcat/OWNERS b/core/java/android/os/logcat/OWNERS
index cb21a6fafd7e..9f14eba65880 100644
--- a/core/java/android/os/logcat/OWNERS
+++ b/core/java/android/os/logcat/OWNERS
@@ -1 +1 @@
-include platform/frameworks/base:/services/core/java/com/android/server/logcat/OWNERS
+file:platform/frameworks/base:/services/core/java/com/android/server/logcat/OWNERS
diff --git a/core/java/android/service/games/TEST_MAPPING b/core/java/android/service/games/TEST_MAPPING
new file mode 100644
index 000000000000..3e551ef6bb54
--- /dev/null
+++ b/core/java/android/service/games/TEST_MAPPING
@@ -0,0 +1,16 @@
+{
+ "presubmit": [
+ // TODO(b/245615658): fix flaky CTS test CtsGameServiceTestCases and add it as presubmit
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "android.service.games"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index ad4f9f718f7f..684d5665c862 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -141,7 +141,9 @@ public class TrustAgentService extends Service {
*
* Without this flag, the message passed to {@code grantTrust} is only used for debugging
* purposes. With the flag, it may be displayed to the user as the reason why the device is
- * unlocked.
+ * unlocked. If this flag isn't set OR the message is set to null, the device will display
+ * its own default message for trust granted. If the TrustAgent intentionally doesn't want to
+ * show any message, then it can set this flag AND set the message to an empty string.
*/
public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 1 << 3;
diff --git a/core/java/android/text/style/SpanUtils.java b/core/java/android/text/style/SpanUtils.java
new file mode 100644
index 000000000000..6b4bd1a76358
--- /dev/null
+++ b/core/java/android/text/style/SpanUtils.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.style;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Typeface;
+import android.text.Spannable;
+import android.text.Spanned;
+import android.util.LongArray;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class SpanUtils {
+ private SpanUtils() {} // Do not instantiate
+
+ /**
+ * Toggle the bold state of the given range.
+ *
+ * If there is at least one character is not bold in the given range, make the entire region to
+ * be bold. If all characters of the given range is already bolded, this method removes bold
+ * style from the given selection.
+ *
+ * @param spannable a spannable string
+ * @param min minimum inclusive index of the selection.
+ * @param max maximum exclusive index of the selection.
+ * @return true if the selected region is toggled.
+ */
+ public static boolean toggleBold(@NonNull Spannable spannable,
+ @IntRange(from = 0) int min, @IntRange(from = 0) int max) {
+
+ if (min == max) {
+ return false;
+ }
+
+ final StyleSpan[] boldSpans = spannable.getSpans(min, max, StyleSpan.class);
+ final ArrayList<StyleSpan> filteredBoldSpans = new ArrayList<>();
+ for (StyleSpan span : boldSpans) {
+ if ((span.getStyle() & Typeface.BOLD) == Typeface.BOLD) {
+ filteredBoldSpans.add(span);
+ }
+ }
+
+ if (!isCovered(spannable, filteredBoldSpans, min, max)) {
+ // At least one character doesn't have bold style. Making given region bold.
+ spannable.setSpan(
+ new StyleSpan(Typeface.BOLD), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ return true;
+ }
+
+ // Span covers the entire selection. Removing spans from tha region.
+ for (int si = 0; si < filteredBoldSpans.size(); ++si) {
+ final StyleSpan span = filteredBoldSpans.get(si);
+ final int start = spannable.getSpanStart(span);
+ final int end = spannable.getSpanEnd(span);
+ final int flag = spannable.getSpanFlags(span);
+
+ // If BOLD_ITALIC style is attached, need to set ITALIC span to the subtracted range.
+ final boolean needItalicSpan = (span.getStyle() & Typeface.ITALIC) == Typeface.ITALIC;
+
+ if (start < min) {
+ if (end > max) {
+ // selection: ------------|===================|----------------
+ // span: <-------------------------------->
+ // result: <-------> <---->
+ spannable.setSpan(span, start, min, flag);
+ spannable.setSpan(new StyleSpan(span.getStyle()), max, end, flag);
+ if (needItalicSpan) {
+ spannable.setSpan(new StyleSpan(Typeface.ITALIC), min, max, flag);
+ }
+ } else {
+ // selection: ------------|===================|----------------
+ // span: <----------->
+ // result: <------->
+ spannable.setSpan(span, start, min, flag);
+ if (needItalicSpan) {
+ spannable.setSpan(new StyleSpan(Typeface.ITALIC), min, end, flag);
+ }
+ }
+ } else {
+ if (end > max) {
+ // selection: ------------|===================|----------------
+ // span: <------------------------>
+ // result: <------------>
+ spannable.setSpan(span, max, end, flag);
+ if (needItalicSpan) {
+ spannable.setSpan(new StyleSpan(Typeface.ITALIC), max, end, flag);
+ }
+ } else {
+ // selection: ------------|===================|----------------
+ // span: <----------->
+ // result:
+ spannable.removeSpan(span);
+ if (needItalicSpan) {
+ spannable.setSpan(new StyleSpan(Typeface.ITALIC), start, end, flag);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Toggle the italic state of the given range.
+ *
+ * If there is at least one character is not italic in the given range, make the entire region
+ * to be italic. If all characters of the given range is already italic, this method removes
+ * italic style from the given selection.
+ *
+ * @param spannable a spannable string
+ * @param min minimum inclusive index of the selection.
+ * @param max maximum exclusive index of the selection.
+ * @return true if the selected region is toggled.
+ */
+ public static boolean toggleItalic(@NonNull Spannable spannable,
+ @IntRange(from = 0) int min, @IntRange(from = 0) int max) {
+
+ if (min == max) {
+ return false;
+ }
+
+ final StyleSpan[] boldSpans = spannable.getSpans(min, max, StyleSpan.class);
+ final ArrayList<StyleSpan> filteredBoldSpans = new ArrayList<>();
+ for (StyleSpan span : boldSpans) {
+ if ((span.getStyle() & Typeface.ITALIC) == Typeface.ITALIC) {
+ filteredBoldSpans.add(span);
+ }
+ }
+
+ if (!isCovered(spannable, filteredBoldSpans, min, max)) {
+ // At least one character doesn't have italic style. Making given region italic.
+ spannable.setSpan(
+ new StyleSpan(Typeface.ITALIC), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ return true;
+ }
+
+ // Span covers the entire selection. Removing spans from tha region.
+ for (int si = 0; si < filteredBoldSpans.size(); ++si) {
+ final StyleSpan span = filteredBoldSpans.get(si);
+ final int start = spannable.getSpanStart(span);
+ final int end = spannable.getSpanEnd(span);
+ final int flag = spannable.getSpanFlags(span);
+
+ // If BOLD_ITALIC style is attached, need to set BOLD span to the subtracted range.
+ final boolean needBoldSpan = (span.getStyle() & Typeface.BOLD) == Typeface.BOLD;
+
+ if (start < min) {
+ if (end > max) {
+ // selection: ------------|===================|----------------
+ // span: <-------------------------------->
+ // result: <-------> <---->
+ spannable.setSpan(span, start, min, flag);
+ spannable.setSpan(new StyleSpan(span.getStyle()), max, end, flag);
+ if (needBoldSpan) {
+ spannable.setSpan(new StyleSpan(Typeface.BOLD), min, max, flag);
+ }
+ } else {
+ // selection: ------------|===================|----------------
+ // span: <----------->
+ // result: <------->
+ spannable.setSpan(span, start, min, flag);
+ if (needBoldSpan) {
+ spannable.setSpan(new StyleSpan(Typeface.BOLD), min, end, flag);
+ }
+ }
+ } else {
+ if (end > max) {
+ // selection: ------------|===================|----------------
+ // span: <------------------------>
+ // result: <------------>
+ spannable.setSpan(span, max, end, flag);
+ if (needBoldSpan) {
+ spannable.setSpan(new StyleSpan(Typeface.BOLD), max, end, flag);
+ }
+ } else {
+ // selection: ------------|===================|----------------
+ // span: <----------->
+ // result:
+ spannable.removeSpan(span);
+ if (needBoldSpan) {
+ spannable.setSpan(new StyleSpan(Typeface.BOLD), start, end, flag);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Toggle the underline state of the given range.
+ *
+ * If there is at least one character is not underlined in the given range, make the entire
+ * region to underlined. If all characters of the given range is already underlined, this
+ * method removes underline from the given selection.
+ *
+ * @param spannable a spannable string
+ * @param min minimum inclusive index of the selection.
+ * @param max maximum exclusive index of the selection.
+ * @return true if the selected region is toggled.
+ */
+ public static boolean toggleUnderline(@NonNull Spannable spannable,
+ @IntRange(from = 0) int min, @IntRange(from = 0) int max) {
+
+ if (min == max) {
+ return false;
+ }
+
+ final List<UnderlineSpan> spans =
+ Arrays.asList(spannable.getSpans(min, max, UnderlineSpan.class));
+
+ if (!isCovered(spannable, spans, min, max)) {
+ // At least one character doesn't have underline style. Making given region underline.
+ spannable.setSpan(new UnderlineSpan(), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ return true;
+ }
+ // Span covers the entire selection. Removing spans from tha region.
+ for (int si = 0; si < spans.size(); ++si) {
+ final UnderlineSpan span = spans.get(si);
+ final int start = spannable.getSpanStart(span);
+ final int end = spannable.getSpanEnd(span);
+ final int flag = spannable.getSpanFlags(span);
+
+ if (start < min) {
+ if (end > max) {
+ // selection: ------------|===================|----------------
+ // span: <-------------------------------->
+ // result: <-------> <---->
+ spannable.setSpan(span, start, min, flag);
+ spannable.setSpan(new UnderlineSpan(), max, end, flag);
+ } else {
+ // selection: ------------|===================|----------------
+ // span: <----------->
+ // result: <------->
+ spannable.setSpan(span, start, min, flag);
+ }
+ } else {
+ if (end > max) {
+ // selection: ------------|===================|----------------
+ // span: <------------------------>
+ // result: <------------>
+ spannable.setSpan(span, max, end, flag);
+ } else {
+ // selection: ------------|===================|----------------
+ // span: <----------->
+ // result:
+ spannable.removeSpan(span);
+ }
+ }
+ }
+ return true;
+ }
+
+ private static long pack(int from, int to) {
+ return ((long) from) << 32 | (long) to;
+ }
+
+ private static int min(long packed) {
+ return (int) (packed >> 32);
+ }
+
+ private static int max(long packed) {
+ return (int) (packed & 0xFFFFFFFFL);
+ }
+
+ private static boolean hasIntersection(int aMin, int aMax, int bMin, int bMax) {
+ return aMin < bMax && bMin < aMax;
+ }
+
+ private static long intersection(int aMin, int aMax, int bMin, int bMax) {
+ return pack(Math.max(aMin, bMin), Math.min(aMax, bMax));
+ }
+
+ private static <T> boolean isCovered(@NonNull Spannable spannable, @NonNull List<T> spans,
+ @IntRange(from = 0) int min, @IntRange(from = 0) int max) {
+
+ if (min == max) {
+ return false;
+ }
+
+ LongArray uncoveredRanges = new LongArray();
+ LongArray nextUncoveredRanges = new LongArray();
+
+ uncoveredRanges.add(pack(min, max));
+
+ for (int si = 0; si < spans.size(); ++si) {
+ final T span = spans.get(si);
+ final int start = spannable.getSpanStart(span);
+ final int end = spannable.getSpanEnd(span);
+
+ for (int i = 0; i < uncoveredRanges.size(); ++i) {
+ final long packed = uncoveredRanges.get(i);
+ final int uncoveredStart = min(packed);
+ final int uncoveredEnd = max(packed);
+
+ if (!hasIntersection(start, end, uncoveredStart, uncoveredEnd)) {
+ // This span doesn't affect this uncovered range. Try next span.
+ nextUncoveredRanges.add(packed);
+ } else {
+ // This span has an intersection with uncovered range. Update the uncovered
+ // range.
+ long intersectionPack = intersection(start, end, uncoveredStart, uncoveredEnd);
+ int intersectStart = min(intersectionPack);
+ int intersectEnd = max(intersectionPack);
+
+ // Uncovered Range : ----------|=======================|-------------
+ // Intersection : <---------->
+ // Remaining uncovered ranges: ----------|=====|----------|======|-------------
+ if (uncoveredStart != intersectStart) {
+ // There is still uncovered area on the left.
+ nextUncoveredRanges.add(pack(uncoveredStart, intersectStart));
+ }
+ if (intersectEnd != uncoveredEnd) {
+ // There is still uncovered area on the right.
+ nextUncoveredRanges.add(pack(intersectEnd, uncoveredEnd));
+ }
+ }
+ }
+
+ if (nextUncoveredRanges.size() == 0) {
+ return true;
+ }
+
+ // Swap the uncoveredRanges and nextUncoveredRanges and clear the next one.
+ final LongArray tmp = nextUncoveredRanges;
+ nextUncoveredRanges = uncoveredRanges;
+ uncoveredRanges = tmp;
+ nextUncoveredRanges.clear();
+ }
+
+ return false;
+ }
+}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index f65a69a8e2bc..0ba3072c0813 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -723,9 +723,9 @@ public final class DisplayInfo implements Parcelable {
outMetrics.noncompatWidthPixels = outMetrics.widthPixels = width;
outMetrics.noncompatHeightPixels = outMetrics.heightPixels = height;
- if (!compatInfo.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
- compatInfo.applyToDisplayMetrics(outMetrics);
- }
+ // Apply to size if the configuration is EMPTY because the size is from real display info.
+ final boolean applyToSize = configuration != null && appBounds == null;
+ compatInfo.applyDisplayMetricsIfNeeded(outMetrics, applyToSize);
}
// For debugging purposes
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index b6b029bdbb44..bda707b48627 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -195,7 +195,7 @@ public class DragEvent implements Parcelable {
* {@link #ACTION_DRAG_ENTERED} while the drag shadow is still within the View object's bounding
* box, but not within a descendant view that can accept the data. The {@link #getX()} and
* {@link #getY()} methods supply
- * the X and Y position of of the drag point within the View object's bounding box.
+ * the X and Y position of the drag point within the View object's bounding box.
* <p>
* A View receives an {@link #ACTION_DRAG_ENTERED} event before receiving any
* ACTION_DRAG_LOCATION events.
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index b48b5258237f..a5ac9480d2cf 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -181,7 +181,8 @@ public final class ImeFocusController {
if (!view.hasImeFocus() || !view.hasWindowFocus()) {
return;
}
- if (DEBUG) Log.d(TAG, "onViewFocusChanged, view=" + view + ", mServedView=" + mServedView);
+ if (DEBUG) Log.d(TAG, "onViewFocusChanged, view=" + InputMethodDebug.dumpViewInfo(view)
+ + ", mServedView=" + InputMethodDebug.dumpViewInfo(mServedView));
// We don't need to track the next served view when the view lost focus here because:
// 1) The current view focus may be cleared temporary when in touch mode, closing input
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 2a0246b200eb..38911e07eb64 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -460,10 +460,8 @@ public final class InputDevice implements Parcelable {
/**
* Called by native code
- * @hide
*/
- @VisibleForTesting
- public InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
+ private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
KeyCharacterMap keyCharacterMap, @InputDeviceCountryCode int countryCode,
boolean hasVibrator, boolean hasMicrophone, boolean hasButtonUnderPad,
@@ -520,6 +518,142 @@ public final class InputDevice implements Parcelable {
}
/**
+ * InputDevice builder used to create an InputDevice for tests in Java.
+ * @hide
+ */
+ @VisibleForTesting
+ public static class Builder {
+ private int mId = 0;
+ private int mGeneration = 0;
+ private int mControllerNumber = 0;
+ private String mName = "";
+ private int mVendorId = 0;
+ private int mProductId = 0;
+ private String mDescriptor = "";
+ private boolean mIsExternal = false;
+ private int mSources = 0;
+ private int mKeyboardType = 0;
+ private KeyCharacterMap mKeyCharacterMap = null;
+ private boolean mHasVibrator = false;
+ private boolean mHasMicrophone = false;
+ private boolean mHasButtonUnderPad = false;
+ private boolean mHasSensor = false;
+ private boolean mHasBattery = false;
+ @InputDeviceCountryCode
+ private int mCountryCode = InputDeviceCountryCode.INVALID;
+
+ /** @see InputDevice#getId() */
+ public Builder setId(int id) {
+ mId = id;
+ return this;
+ }
+
+ /** @see InputDevice#getGeneration() */
+ public Builder setGeneration(int generation) {
+ mGeneration = generation;
+ return this;
+ }
+
+ /** @see InputDevice#getControllerNumber() */
+ public Builder setControllerNumber(int controllerNumber) {
+ mControllerNumber = controllerNumber;
+ return this;
+ }
+
+ /** @see InputDevice#getName() */
+ public Builder setName(String name) {
+ mName = name;
+ return this;
+ }
+
+ /** @see InputDevice#getVendorId() */
+ public Builder setVendorId(int vendorId) {
+ mVendorId = vendorId;
+ return this;
+ }
+
+ /** @see InputDevice#getProductId() */
+ public Builder setProductId(int productId) {
+ mProductId = productId;
+ return this;
+ }
+
+ /** @see InputDevice#getDescriptor() */
+ public Builder setDescriptor(String descriptor) {
+ mDescriptor = descriptor;
+ return this;
+ }
+
+ /** @see InputDevice#isExternal() */
+ public Builder setExternal(boolean external) {
+ mIsExternal = external;
+ return this;
+ }
+
+ /** @see InputDevice#getSources() */
+ public Builder setSources(int sources) {
+ mSources = sources;
+ return this;
+ }
+
+ /** @see InputDevice#getKeyboardType() */
+ public Builder setKeyboardType(int keyboardType) {
+ mKeyboardType = keyboardType;
+ return this;
+ }
+
+ /** @see InputDevice#getKeyCharacterMap() */
+ public Builder setKeyCharacterMap(KeyCharacterMap keyCharacterMap) {
+ mKeyCharacterMap = keyCharacterMap;
+ return this;
+ }
+
+ /** @see InputDevice#getVibrator() */
+ public Builder setHasVibrator(boolean hasVibrator) {
+ mHasVibrator = hasVibrator;
+ return this;
+ }
+
+ /** @see InputDevice#hasMicrophone() */
+ public Builder setHasMicrophone(boolean hasMicrophone) {
+ mHasMicrophone = hasMicrophone;
+ return this;
+ }
+
+ /** @see InputDevice#hasButtonUnderPad() */
+ public Builder setHasButtonUnderPad(boolean hasButtonUnderPad) {
+ mHasButtonUnderPad = hasButtonUnderPad;
+ return this;
+ }
+
+ /** @see InputDevice#hasSensor() */
+ public Builder setHasSensor(boolean hasSensor) {
+ mHasSensor = hasSensor;
+ return this;
+ }
+
+ /** @see InputDevice#hasBattery() */
+ public Builder setHasBattery(boolean hasBattery) {
+ mHasBattery = hasBattery;
+ return this;
+ }
+
+ /** @see InputDevice#getCountryCode() */
+ public Builder setCountryCode(@InputDeviceCountryCode int countryCode) {
+ mCountryCode = countryCode;
+ return this;
+ }
+
+ /** Build {@link InputDevice}. */
+ public InputDevice build() {
+ return new InputDevice(mId, mGeneration, mControllerNumber, mName, mVendorId,
+ mProductId, mDescriptor, mIsExternal, mSources, mKeyboardType, mKeyCharacterMap,
+ mCountryCode, mHasVibrator, mHasMicrophone, mHasButtonUnderPad, mHasSensor,
+ mHasBattery);
+ }
+ }
+
+ /**
* Gets information about the input device with the specified id.
* @param id The device id.
* @return The input device or null if not found.
@@ -1040,6 +1174,15 @@ public final class InputDevice implements Parcelable {
}
/**
+ * Reports whether the device has a battery.
+ * @return true if the device has a battery, false otherwise.
+ * @hide
+ */
+ public boolean hasBattery() {
+ return mHasBattery;
+ }
+
+ /**
* Provides information about the range of values for a particular {@link MotionEvent} axis.
*
* @see InputDevice#getMotionRange(int)
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c3a638c4c36a..6c238e5698c5 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -866,13 +866,19 @@ public class KeyEvent extends InputEvent implements Parcelable {
public static final int KEYCODE_DEMO_APP_3 = 303;
/** Key code constant: Demo Application key #4. */
public static final int KEYCODE_DEMO_APP_4 = 304;
+ /** Key code constant: Keyboard backlight down */
+ public static final int KEYCODE_KEYBOARD_BACKLIGHT_DOWN = 305;
+ /** Key code constant: Keyboard backlight up */
+ public static final int KEYCODE_KEYBOARD_BACKLIGHT_UP = 306;
+ /** Key code constant: Keyboard backlight toggle */
+ public static final int KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE = 307;
/**
* Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent.
* @hide
*/
@TestApi
- public static final int LAST_KEYCODE = KEYCODE_DEMO_APP_4;
+ public static final int LAST_KEYCODE = KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
@@ -2019,6 +2025,9 @@ public class KeyEvent extends InputEvent implements Parcelable {
case KeyEvent.KEYCODE_SEARCH:
case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
case KeyEvent.KEYCODE_BRIGHTNESS_UP:
+ case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
+ case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
+ case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP:
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b6c92e3fd264..7acf319a5049 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1622,8 +1622,11 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
*/
@Override
public void unlockCanvasAndPost(Canvas canvas) {
- mSurface.unlockCanvasAndPost(canvas);
- mSurfaceLock.unlock();
+ try {
+ mSurface.unlockCanvasAndPost(canvas);
+ } finally {
+ mSurfaceLock.unlock();
+ }
}
@Override
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 2d4da2c88ce8..a52fc7586f72 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -180,9 +180,9 @@ public final class VelocityTracker {
private static native void nativeClear(long ptr);
private static native void nativeAddMovement(long ptr, MotionEvent event);
private static native void nativeComputeCurrentVelocity(long ptr, int units, float maxVelocity);
- private static native float nativeGetXVelocity(long ptr, int id);
- private static native float nativeGetYVelocity(long ptr, int id);
- private static native boolean nativeGetEstimator(long ptr, int id, Estimator outEstimator);
+ private static native float nativeGetVelocity(long ptr, int axis, int id);
+ private static native boolean nativeGetEstimator(
+ long ptr, int axis, int id, Estimator outEstimator);
static {
// Strategy string and IDs mapping lookup.
@@ -361,7 +361,7 @@ public final class VelocityTracker {
* @return The previously computed X velocity.
*/
public float getXVelocity() {
- return nativeGetXVelocity(mPtr, ACTIVE_POINTER_ID);
+ return getXVelocity(ACTIVE_POINTER_ID);
}
/**
@@ -371,7 +371,7 @@ public final class VelocityTracker {
* @return The previously computed Y velocity.
*/
public float getYVelocity() {
- return nativeGetYVelocity(mPtr, ACTIVE_POINTER_ID);
+ return getYVelocity(ACTIVE_POINTER_ID);
}
/**
@@ -382,7 +382,7 @@ public final class VelocityTracker {
* @return The previously computed X velocity.
*/
public float getXVelocity(int id) {
- return nativeGetXVelocity(mPtr, id);
+ return nativeGetVelocity(mPtr, MotionEvent.AXIS_X, id);
}
/**
@@ -393,7 +393,7 @@ public final class VelocityTracker {
* @return The previously computed Y velocity.
*/
public float getYVelocity(int id) {
- return nativeGetYVelocity(mPtr, id);
+ return nativeGetVelocity(mPtr, MotionEvent.AXIS_Y, id);
}
/**
@@ -403,6 +403,8 @@ public final class VelocityTracker {
* It is not necessary to call {@link #computeCurrentVelocity(int)} before calling
* this method.
*
+ * @param axis Which axis's velocity to return.
+ * Should be one of the axes defined in {@link MotionEvent}.
* @param id Which pointer's velocity to return.
* @param outEstimator The estimator to populate.
* @return True if an estimator was obtained, false if there is no information
@@ -410,11 +412,11 @@ public final class VelocityTracker {
*
* @hide For internal use only. Not a final API.
*/
- public boolean getEstimator(int id, Estimator outEstimator) {
+ public boolean getEstimator(int axis, int id, Estimator outEstimator) {
if (outEstimator == null) {
throw new IllegalArgumentException("outEstimator must not be null");
}
- return nativeGetEstimator(mPtr, id, outEstimator);
+ return nativeGetEstimator(mPtr, axis, id, outEstimator);
}
/**
@@ -434,16 +436,9 @@ public final class VelocityTracker {
private static final int MAX_DEGREE = 4;
/**
- * Polynomial coefficients describing motion in X.
+ * Polynomial coefficients describing motion.
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public final float[] xCoeff = new float[MAX_DEGREE + 1];
-
- /**
- * Polynomial coefficients describing motion in Y.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public final float[] yCoeff = new float[MAX_DEGREE + 1];
+ public final float[] coeff = new float[MAX_DEGREE + 1];
/**
* Polynomial degree, or zero if only position information is available.
@@ -458,39 +453,21 @@ public final class VelocityTracker {
public float confidence;
/**
- * Gets an estimate of the X position of the pointer at the specified time point.
+ * Gets an estimate of the position of the pointer at the specified time point.
* @param time The time point in seconds, 0 is the last recorded time.
- * @return The estimated X coordinate.
- */
- public float estimateX(float time) {
- return estimate(time, xCoeff);
- }
-
- /**
- * Gets an estimate of the Y position of the pointer at the specified time point.
- * @param time The time point in seconds, 0 is the last recorded time.
- * @return The estimated Y coordinate.
- */
- public float estimateY(float time) {
- return estimate(time, yCoeff);
- }
-
- /**
- * Gets the X coefficient with the specified index.
- * @param index The index of the coefficient to return.
- * @return The X coefficient, or 0 if the index is greater than the degree.
+ * @return The estimated axis value.
*/
- public float getXCoeff(int index) {
- return index <= degree ? xCoeff[index] : 0;
+ public float estimate(float time) {
+ return estimate(time, coeff);
}
/**
- * Gets the Y coefficient with the specified index.
+ * Gets the coefficient with the specified index.
* @param index The index of the coefficient to return.
- * @return The Y coefficient, or 0 if the index is greater than the degree.
+ * @return The coefficient, or 0 if the index is greater than the degree.
*/
- public float getYCoeff(int index) {
- return index <= degree ? yCoeff[index] : 0;
+ public float getCoeff(int index) {
+ return index <= degree ? coeff[index] : 0;
}
private float estimate(float time, float[] c) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3b8b479f5fd4..8fee4db458b3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -62,6 +62,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.ColorStateList;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -12889,7 +12890,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mViewTranslationCallback != null) {
mViewTranslationCallback.onClearTranslation(this);
}
- clearViewTranslationCallback();
clearViewTranslationResponse();
if (hasTranslationTransientState()) {
setHasTransientState(false);
@@ -27546,6 +27546,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|| (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) {
throw new IllegalStateException("Drag shadow dimensions must not be negative");
}
+ final float overrideInvScale = CompatibilityInfo.getOverrideInvertedScale();
+ if (overrideInvScale != 1f) {
+ shadowTouchPoint.x = (int) (shadowTouchPoint.x / overrideInvScale);
+ shadowTouchPoint.y = (int) (shadowTouchPoint.y / overrideInvScale);
+ }
// Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder
// does not accept zero size surface.
@@ -27570,6 +27575,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
.setFormat(PixelFormat.TRANSLUCENT)
.setCallsite("View.startDragAndDrop")
.build();
+ if (overrideInvScale != 1f) {
+ final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ transaction.setMatrix(surfaceControl, 1 / overrideInvScale, 0, 0, 1 / overrideInvScale)
+ .apply();
+ }
final Surface surface = new Surface();
surface.copyFrom(surfaceControl);
IBinder token = null;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 864ea2191390..074cbe5a6947 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1787,6 +1787,7 @@ public final class ViewRootImpl implements ViewParent,
final ClientWindowFrames frames = (ClientWindowFrames) args.arg1;
final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg2;
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mergedConfiguration);
final boolean forceNextWindowRelayout = args.argi1 != 0;
final int displayId = args.argi3;
final int resizeMode = args.argi5;
@@ -2058,6 +2059,7 @@ public final class ViewRootImpl implements ViewParent,
}
private void invalidateRectOnScreen(Rect dirty) {
+ if (DEBUG_DRAW) Log.v(mTag, "invalidateRectOnScreen: " + dirty);
final Rect localDirty = mDirty;
// Add the new dirty rect to the current one
@@ -4751,25 +4753,8 @@ public final class ViewRootImpl implements ViewParent,
// Draw with software renderer.
final Canvas canvas;
- // We already have the offset of surfaceInsets in xoff, yoff and dirty region,
- // therefore we need to add it back when moving the dirty region.
- int dirtyXOffset = xoff;
- int dirtyYOffset = yoff;
- if (surfaceInsets != null) {
- dirtyXOffset += surfaceInsets.left;
- dirtyYOffset += surfaceInsets.top;
- }
-
try {
- dirty.offset(-dirtyXOffset, -dirtyYOffset);
- final int left = dirty.left;
- final int top = dirty.top;
- final int right = dirty.right;
- final int bottom = dirty.bottom;
-
canvas = mSurface.lockCanvas(dirty);
-
- // TODO: Do this in native
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
@@ -4781,14 +4766,13 @@ public final class ViewRootImpl implements ViewParent,
// kill stuff (or ourself) for no reason.
mLayoutRequested = true; // ask wm for a new surface next time.
return false;
- } finally {
- dirty.offset(dirtyXOffset, dirtyYOffset); // Reset to the original value.
}
try {
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
- + canvas.getWidth() + ", h=" + canvas.getHeight());
+ + canvas.getWidth() + ", h=" + canvas.getHeight() + ", dirty: " + dirty
+ + ", xOff=" + xoff + ", yOff=" + yoff);
//canvas.drawARGB(255, 255, 0, 0);
}
@@ -8242,6 +8226,7 @@ public final class ViewRootImpl implements ViewParent,
mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
}
mInvSizeCompatScale = 1f / mTmpFrames.sizeCompatScale;
+ CompatibilityInfo.applyOverrideScaleIfNeeded(mPendingMergedConfiguration);
mInsetsController.onStateChanged(mTempInsets);
mInsetsController.onControlsChanged(mTempControls);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a49caafc63f1..8656af2151d9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -285,12 +285,18 @@ public interface WindowManager extends ViewManager {
int TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
/**
- * Keyguard is being occluded.
+ * Keyguard is being occluded by non-Dream.
* @hide
*/
int TRANSIT_OLD_KEYGUARD_OCCLUDE = 22;
/**
+ * Keyguard is being occluded by Dream.
+ * @hide
+ */
+ int TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM = 33;
+
+ /**
* Keyguard is being unoccluded.
* @hide
*/
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 2db0dcbce45e..f2c8355adb84 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -24,6 +24,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
+import android.widget.TextView;
import com.android.internal.util.BitUtils;
@@ -685,6 +686,18 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
*/
public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0x0000200;
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * It means the content is invalid or associated with an error.
+ * For example, text that sets an error message, such as when input isn't in a valid format,
+ * should send this event and use {@link AccessibilityNodeInfo#setError} to
+ * provide more context.
+ *
+ * @see AccessibilityNodeInfo#setError
+ * @see TextView#setError
+ */
+ public static final int CONTENT_CHANGE_TYPE_INVALID = 0x0000400;
+
/** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
public static final int SPEECH_STATE_SPEAKING_START = 0x00000001;
@@ -810,6 +823,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
CONTENT_CHANGE_TYPE_DRAG_STARTED,
CONTENT_CHANGE_TYPE_DRAG_DROPPED,
CONTENT_CHANGE_TYPE_DRAG_CANCELLED,
+ CONTENT_CHANGE_TYPE_INVALID,
})
public @interface ContentChangeTypes {}
@@ -1076,6 +1090,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
case CONTENT_CHANGE_TYPE_DRAG_STARTED: return "CONTENT_CHANGE_TYPE_DRAG_STARTED";
case CONTENT_CHANGE_TYPE_DRAG_DROPPED: return "CONTENT_CHANGE_TYPE_DRAG_DROPPED";
case CONTENT_CHANGE_TYPE_DRAG_CANCELLED: return "CONTENT_CHANGE_TYPE_DRAG_CANCELLED";
+ case CONTENT_CHANGE_TYPE_INVALID: return "CONTENT_CHANGE_TYPE_INVALID";
default: return Integer.toHexString(type);
}
}
@@ -1332,6 +1347,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* Convenience method to obtain a {@link #TYPE_WINDOWS_CHANGED} event for a specific window and
* change set.
*
+ * @param displayId The ID of the display from which the event comes from
* @param windowId The ID of the window that changed
* @param windowChangeTypes The changes to populate
* @return An instance of a TYPE_WINDOWS_CHANGED, populated with the requested fields and with
@@ -1340,8 +1356,9 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* @hide
*/
public static AccessibilityEvent obtainWindowsChangedEvent(
- int windowId, int windowChangeTypes) {
+ int displayId, int windowId, int windowChangeTypes) {
final AccessibilityEvent event = new AccessibilityEvent(TYPE_WINDOWS_CHANGED);
+ event.setDisplayId(displayId);
event.setWindowId(windowId);
event.setWindowChanges(windowChangeTypes);
event.setImportantForAccessibility(true);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 9acd1af952ae..0c7c1639e6c7 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1771,7 +1771,7 @@ public final class InputMethodManager {
if (getServedViewLocked() != null) {
if (DEBUG) {
Log.v(TAG, "FINISH INPUT: mServedView="
- + dumpViewInfo(getServedViewLocked()));
+ + InputMethodDebug.dumpViewInfo(getServedViewLocked()));
}
setServedViewLocked(null);
mCompletions = null;
@@ -2304,7 +2304,7 @@ public final class InputMethodManager {
// Make sure we have a window token for the served view.
if (DEBUG) {
- Log.v(TAG, "Starting input: view=" + dumpViewInfo(view) +
+ Log.v(TAG, "Starting input: view=" + InputMethodDebug.dumpViewInfo(view) +
" reason=" + InputMethodDebug.startInputReasonToString(startInputReason));
}
if (view == null) {
@@ -2375,9 +2375,9 @@ public final class InputMethodManager {
final View servedView = getServedViewLocked();
if (servedView != view || !mServedConnecting) {
// Something else happened, so abort.
- if (DEBUG) Log.v(TAG,
- "Starting input: finished by someone else. view=" + dumpViewInfo(view)
- + " servedView=" + dumpViewInfo(servedView)
+ if (DEBUG) Log.v(TAG, "Starting input: finished by someone else."
+ + " view=" + InputMethodDebug.dumpViewInfo(view)
+ + " servedView=" + InputMethodDebug.dumpViewInfo(servedView)
+ " mServedConnecting=" + mServedConnecting);
if (mServedInputConnection != null && startInputReason == BOUND_TO_IMMS) {
// This is not an error. Once IME binds (MSG_BIND), InputConnection is fully
@@ -2437,8 +2437,8 @@ public final class InputMethodManager {
mServedInputConnection = servedInputConnection;
if (DEBUG) {
- Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
- + ic + " editorInfo=" + editorInfo + " startInputFlags="
+ Log.v(TAG, "START INPUT: view=" + InputMethodDebug.dumpViewInfo(view)
+ + " ic=" + ic + " editorInfo=" + editorInfo + " startInputFlags="
+ InputMethodDebug.startInputFlagsToString(startInputFlags));
}
@@ -3774,23 +3774,6 @@ public final class InputMethodManager {
return mCurBindState != null ? mCurBindState.mBindSequence : -1;
}
- private static String dumpViewInfo(@Nullable final View view) {
- if (view == null) {
- return "null";
- }
- final StringBuilder sb = new StringBuilder();
- sb.append(view);
- sb.append(",focus=" + view.hasFocus());
- sb.append(",windowFocus=" + view.hasWindowFocus());
- sb.append(",autofillUiShowing=" + isAutofillUIShowing(view));
- sb.append(",window=" + view.getWindowToken());
- sb.append(",displayId=" + view.getContext().getDisplayId());
- sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
- sb.append(",hasImeFocus=" + view.hasImeFocus());
-
- return sb.toString();
- }
-
/**
* Checks the args to see if a proto-based ime dump was requested and writes the client side
* ime dump to the given {@link FileDescriptor}.
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 2c612804d718..aa2474d7903e 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -17,13 +17,17 @@
package android.widget;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
+import android.text.style.SpanUtils;
import android.util.AttributeSet;
+import android.view.KeyEvent;
/*
* This is supposed to be a *very* thin veneer over TextView.
@@ -69,8 +73,18 @@ import android.util.AttributeSet;
* See {@link android.R.styleable#EditText EditText Attributes},
* {@link android.R.styleable#TextView TextView Attributes},
* {@link android.R.styleable#View View Attributes}
+ *
+ * @attr ref android.R.styleable#EditText_enableTextStylingShortcuts
*/
public class EditText extends TextView {
+
+ // True if the style shortcut is enabled.
+ private boolean mStyleShortcutsEnabled = false;
+
+ private static final int ID_BOLD = android.R.id.bold;
+ private static final int ID_ITALIC = android.R.id.italic;
+ private static final int ID_UNDERLINE = android.R.id.underline;
+
public EditText(Context context) {
this(context, null);
}
@@ -85,6 +99,20 @@ public class EditText extends TextView {
public EditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ final Resources.Theme theme = context.getTheme();
+ final TypedArray a = theme.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.EditText, defStyleAttr, defStyleRes);
+
+ final int n = a.getIndexCount();
+ for (int i = 0; i < n; ++i) {
+ int attr = a.getIndex(i);
+ switch (attr) {
+ case com.android.internal.R.styleable.EditText_enableTextStylingShortcuts:
+ mStyleShortcutsEnabled = a.getBoolean(attr, false);
+ break;
+ }
+ }
}
@Override
@@ -178,4 +206,77 @@ public class EditText extends TextView {
protected boolean supportsAutoSizeText() {
return false;
}
+
+ @Override
+ public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+ if (event.hasModifiers(KeyEvent.META_CTRL_ON)) {
+ // Handle Ctrl-only shortcuts.
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_B:
+ if (mStyleShortcutsEnabled && hasSelection()) {
+ return onTextContextMenuItem(ID_BOLD);
+ }
+ break;
+ case KeyEvent.KEYCODE_I:
+ if (mStyleShortcutsEnabled && hasSelection()) {
+ return onTextContextMenuItem(ID_ITALIC);
+ }
+ break;
+ case KeyEvent.KEYCODE_U:
+ if (mStyleShortcutsEnabled && hasSelection()) {
+ return onTextContextMenuItem(ID_UNDERLINE);
+ }
+ break;
+ }
+ }
+ return super.onKeyShortcut(keyCode, event);
+ }
+
+ @Override
+ public boolean onTextContextMenuItem(int id) {
+ // TODO: Move to switch-case once the resource ID is finalized.
+ if (id == ID_BOLD || id == ID_ITALIC || id == ID_UNDERLINE) {
+ return performStylingAction(id);
+ }
+ return super.onTextContextMenuItem(id);
+ }
+
+ private boolean performStylingAction(int actionId) {
+ final int selectionStart = getSelectionStart();
+ final int selectionEnd = getSelectionEnd();
+ if (selectionStart < 0 || selectionEnd < 0) {
+ return false; // There is no selection.
+ }
+ int min = Math.min(selectionStart, selectionEnd);
+ int max = Math.max(selectionStart, selectionEnd);
+
+
+ Spannable spannable = getText();
+ if (actionId == ID_BOLD) {
+ return SpanUtils.toggleBold(spannable, min, max);
+ } else if (actionId == ID_ITALIC) {
+ return SpanUtils.toggleItalic(spannable, min, max);
+ } else if (actionId == ID_UNDERLINE) {
+ return SpanUtils.toggleUnderline(spannable, min, max);
+ }
+
+ return false;
+ }
+
+ /**
+ * Enables styls shortcuts, e.g. Ctrl+B for making text bold.
+ *
+ * @param enabled true for enabled, false for disabled.
+ */
+ public void setStyleShortcutsEnabled(boolean enabled) {
+ mStyleShortcutsEnabled = enabled;
+ }
+
+ /**
+ * Return true if style shortcut is enabled, otherwise returns false.
+ * @return true if style shortcut is enabled, otherwise returns false.
+ */
+ public boolean isStyleShortcutEnabled() {
+ return mStyleShortcutsEnabled;
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 18ebe30a4990..68b902f14079 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7644,7 +7644,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
createEditorIfNeeded();
mEditor.setError(error, icon);
notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_INVALID);
}
@Override
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 3aee472ceeab..8df6541aed9e 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -20,12 +20,6 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK;
-import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
-import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
-import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
-import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
-import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
@@ -35,8 +29,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.WindowConfiguration;
-import android.content.Intent;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -55,21 +47,18 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
/**
* Key to the {@link Throwable} in {@link TaskFragmentTransaction.Change#getErrorBundle()}.
- * @hide
*/
public static final String KEY_ERROR_CALLBACK_THROWABLE = "fragment_throwable";
/**
* Key to the {@link TaskFragmentInfo} in
* {@link TaskFragmentTransaction.Change#getErrorBundle()}.
- * @hide
*/
public static final String KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO = "task_fragment_info";
/**
* Key to the {@link WindowContainerTransaction.HierarchyOp} in
* {@link TaskFragmentTransaction.Change#getErrorBundle()}.
- * @hide
*/
public static final String KEY_ERROR_CALLBACK_OP_TYPE = "operation_type";
@@ -195,7 +184,7 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
* Routes to {@link ITaskFragmentOrganizerController#applyTransaction} instead of
* {@link IWindowOrganizerController#applyTransaction} for the different transition options.
*
- * @see #applyTransaction(WindowContainerTransaction, int, boolean, boolean)
+ * @see #applyTransaction(WindowContainerTransaction, int, boolean)
*/
@Override
public void applyTransaction(@NonNull WindowContainerTransaction wct) {
@@ -274,142 +263,13 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
}
/**
- * Called when a TaskFragment is created and organized by this organizer.
- *
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed. No
- * need to call {@link #applyTransaction} as it will be applied by the caller.
- * @param taskFragmentInfo Info of the TaskFragment that is created.
- */
- public void onTaskFragmentAppeared(@NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentInfo taskFragmentInfo) {}
-
- /**
- * Called when the status of an organized TaskFragment is changed.
- *
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed. No
- * need to call {@link #applyTransaction} as it will be applied by the caller.
- * @param taskFragmentInfo Info of the TaskFragment that is changed.
- */
- public void onTaskFragmentInfoChanged(@NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentInfo taskFragmentInfo) {}
-
- /**
- * Called when an organized TaskFragment is removed.
- *
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed. No
- * need to call {@link #applyTransaction} as it will be applied by the caller.
- * @param taskFragmentInfo Info of the TaskFragment that is removed.
- */
- public void onTaskFragmentVanished(@NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentInfo taskFragmentInfo) {}
-
- /**
- * Called when the parent leaf Task of organized TaskFragments is changed.
- * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
- * transaction.
- *
- * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
- * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
- * bounds.
- *
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed. No
- * need to call {@link #applyTransaction} as it will be applied by the caller.
- * @param taskId Id of the parent Task that is changed.
- * @param parentConfig Config of the parent Task.
- */
- public void onTaskFragmentParentInfoChanged(@NonNull WindowContainerTransaction wct, int taskId,
- @NonNull Configuration parentConfig) {}
-
- /**
- * Called when the {@link WindowContainerTransaction} created with
- * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
- *
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed. No
- * need to call {@link #applyTransaction} as it will be applied by the caller.
- * @param errorCallbackToken token set in
- * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
- * @param taskFragmentInfo The {@link TaskFragmentInfo}. This could be {@code null} if no
- * TaskFragment created.
- * @param opType The {@link WindowContainerTransaction.HierarchyOp} of the failed
- * transaction operation.
- * @param exception exception from the server side.
- */
- public void onTaskFragmentError(@NonNull WindowContainerTransaction wct,
- @NonNull IBinder errorCallbackToken, @Nullable TaskFragmentInfo taskFragmentInfo,
- int opType, @NonNull Throwable exception) {}
-
- /**
- * Called when an Activity is reparented to the Task with organized TaskFragment. For example,
- * when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
- * original Task. In this case, we need to notify the organizer so that it can check if the
- * Activity matches any split rule.
- *
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed. No
- * need to call {@link #applyTransaction} as it will be applied by the caller.
- * @param taskId The Task that the activity is reparented to.
- * @param activityIntent The intent that the activity is original launched with.
- * @param activityToken If the activity belongs to the same process as the organizer, this
- * will be the actual activity token; if the activity belongs to a
- * different process, the server will generate a temporary token that
- * the organizer can use to reparent the activity through
- * {@link WindowContainerTransaction} if needed.
- */
- public void onActivityReparentedToTask(@NonNull WindowContainerTransaction wct,
- int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken) {}
-
- /**
* Called when the transaction is ready so that the organizer can update the TaskFragments based
* on the changes in transaction.
- * @hide
*/
public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
- // TODO(b/240519866): move to SplitController#onTransactionReady to make sure the whole
- // transaction is handled in one sync block. Keep the implementation below to keep CTS
- // compatibility. Remove in the next release.
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
- for (TaskFragmentTransaction.Change change : changes) {
- final int taskId = change.getTaskId();
- switch (change.getType()) {
- case TYPE_TASK_FRAGMENT_APPEARED:
- onTaskFragmentAppeared(wct, change.getTaskFragmentInfo());
- break;
- case TYPE_TASK_FRAGMENT_INFO_CHANGED:
- onTaskFragmentInfoChanged(wct, change.getTaskFragmentInfo());
- break;
- case TYPE_TASK_FRAGMENT_VANISHED:
- onTaskFragmentVanished(wct, change.getTaskFragmentInfo());
- break;
- case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
- onTaskFragmentParentInfoChanged(wct, taskId, change.getTaskConfiguration());
- break;
- case TYPE_TASK_FRAGMENT_ERROR:
- final Bundle errorBundle = change.getErrorBundle();
- onTaskFragmentError(
- wct,
- change.getErrorCallbackToken(),
- errorBundle.getParcelable(
- KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class),
- errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
- errorBundle.getSerializable(KEY_ERROR_CALLBACK_THROWABLE,
- java.lang.Throwable.class));
- break;
- case TYPE_ACTIVITY_REPARENTED_TO_TASK:
- onActivityReparentedToTask(
- wct,
- change.getTaskId(),
- change.getActivityIntent(),
- change.getActivityToken());
- break;
- default:
- throw new IllegalArgumentException(
- "Unknown TaskFragmentEvent=" + change.getType());
- }
- }
-
- // Notify the server, and the server should apply the WindowContainerTransaction.
- onTransactionHandled(transaction.getTransactionToken(), wct, getTransitionType(wct),
- false /* shouldApplyIndependently */);
+ // Notify the server to finish the transaction.
+ onTransactionHandled(transaction.getTransactionToken(), new WindowContainerTransaction(),
+ TRANSIT_NONE, false /* shouldApplyIndependently */);
}
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
index 84a5fea9f57f..04fcd3afcf1d 100644
--- a/core/java/android/window/TaskFragmentTransaction.java
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -21,6 +21,8 @@ import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Binder;
@@ -40,6 +42,7 @@ import java.util.List;
* @see TaskFragmentTransaction.Change
* @hide
*/
+@TestApi
public final class TaskFragmentTransaction implements Parcelable {
/** Unique token to represent this transaction. */
@@ -63,6 +66,7 @@ public final class TaskFragmentTransaction implements Parcelable {
dest.writeTypedList(mChanges);
}
+ @NonNull
public IBinder getTransactionToken() {
return mTransactionToken;
}
@@ -105,6 +109,7 @@ public final class TaskFragmentTransaction implements Parcelable {
return 0;
}
+ @NonNull
public static final Creator<TaskFragmentTransaction> CREATOR = new Creator<>() {
@Override
public TaskFragmentTransaction createFromParcel(Parcel in) {
@@ -218,24 +223,28 @@ public final class TaskFragmentTransaction implements Parcelable {
}
/** The change is related to the TaskFragment created with this unique token. */
+ @NonNull
public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) {
mTaskFragmentToken = requireNonNull(taskFragmentToken);
return this;
}
/** Info of the embedded TaskFragment. */
+ @NonNull
public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) {
mTaskFragmentInfo = requireNonNull(info);
return this;
}
/** Task id the parent Task. */
+ @NonNull
public Change setTaskId(int taskId) {
mTaskId = taskId;
return this;
}
/** Configuration of the parent Task. */
+ @NonNull
public Change setTaskConfiguration(@NonNull Configuration configuration) {
mTaskConfiguration = requireNonNull(configuration);
return this;
@@ -246,6 +255,7 @@ public final class TaskFragmentTransaction implements Parcelable {
* from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
* report back.
*/
+ @NonNull
public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
mErrorCallbackToken = errorCallbackToken;
return this;
@@ -255,6 +265,7 @@ public final class TaskFragmentTransaction implements Parcelable {
* Bundle with necessary info about the failure operation of
* {@link #TYPE_TASK_FRAGMENT_ERROR}.
*/
+ @NonNull
public Change setErrorBundle(@NonNull Bundle errorBundle) {
mErrorBundle = requireNonNull(errorBundle);
return this;
@@ -264,6 +275,7 @@ public final class TaskFragmentTransaction implements Parcelable {
* Intent of the activity that is reparented to the Task for
* {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}.
*/
+ @NonNull
public Change setActivityIntent(@NonNull Intent intent) {
mActivityIntent = requireNonNull(intent);
return this;
@@ -276,6 +288,7 @@ public final class TaskFragmentTransaction implements Parcelable {
* a temporary token that the organizer can use to reparent the activity through
* {@link WindowContainerTransaction} if needed.
*/
+ @NonNull
public Change setActivityToken(@NonNull IBinder activityToken) {
mActivityToken = requireNonNull(activityToken);
return this;
@@ -310,11 +323,12 @@ public final class TaskFragmentTransaction implements Parcelable {
return mErrorCallbackToken;
}
- @Nullable
+ @NonNull
public Bundle getErrorBundle() {
- return mErrorBundle;
+ return mErrorBundle != null ? mErrorBundle : Bundle.EMPTY;
}
+ @SuppressLint("IntentBuilderName") // This is not creating new Intent.
@Nullable
public Intent getActivityIntent() {
return mActivityIntent;
@@ -335,6 +349,7 @@ public final class TaskFragmentTransaction implements Parcelable {
return 0;
}
+ @NonNull
public static final Creator<Change> CREATOR = new Creator<>() {
@Override
public Change createFromParcel(Parcel in) {
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 0976f45c02b0..a208634abe78 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -28,6 +28,7 @@ import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
import android.content.Context;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.inputmethodservice.AbstractInputMethodService;
import android.os.Build;
@@ -221,6 +222,7 @@ public class WindowTokenClient extends IWindowToken.Stub {
if (context == null) {
return;
}
+ CompatibilityInfo.applyOverrideScaleIfNeeded(newConfig);
final boolean displayChanged;
final boolean shouldUpdateResources;
final int diff;
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index e57b90ad1612..1ec5325623ec 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -43,7 +43,6 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.ChooserTargetInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
@@ -87,7 +86,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
private final ChooserActivityLogger mChooserActivityLogger;
private int mNumShortcutResults = 0;
- private final Map<TargetInfo, AsyncTask> mIconLoaders = new HashMap<>();
+ private Map<DisplayResolveInfo, LoadIconTask> mIconLoaders = new HashMap<>();
private boolean mApplySharingAppLimits;
// Reserve spots for incoming direct share targets by adding placeholders
@@ -105,8 +104,6 @@ public class ChooserListAdapter extends ResolverListAdapter {
private AppPredictor mAppPredictor;
private AppPredictor.Callback mAppPredictorCallback;
- private LoadDirectShareIconTaskProvider mTestLoadDirectShareTaskProvider;
-
// For pinned direct share labels, if the text spans multiple lines, the TextView will consume
// the full width, even if the characters actually take up less than that. Measure the actual
// line widths and constrain the View's width based upon that so that the pin doesn't end up
@@ -243,6 +240,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
mListViewDataChanged = false;
}
+
private void createPlaceHolders() {
mNumShortcutResults = 0;
mServiceTargets.clear();
@@ -267,25 +265,31 @@ public class ChooserListAdapter extends ResolverListAdapter {
return;
}
- if (info instanceof DisplayResolveInfo) {
- DisplayResolveInfo dri = (DisplayResolveInfo) info;
- holder.bindLabel(dri.getDisplayLabel(), dri.getExtendedInfo(), alwaysShowSubLabel());
- startDisplayResolveInfoIconLoading(holder, dri);
- } else {
+ if (!(info instanceof DisplayResolveInfo)) {
holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel());
+ holder.bindIcon(info);
if (info instanceof SelectableTargetInfo) {
- SelectableTargetInfo selectableInfo = (SelectableTargetInfo) info;
// direct share targets should append the application name for a better readout
- DisplayResolveInfo rInfo = selectableInfo.getDisplayResolveInfo();
+ DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo();
CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
- CharSequence extendedInfo = selectableInfo.getExtendedInfo();
- String contentDescription = String.join(" ", selectableInfo.getDisplayLabel(),
+ CharSequence extendedInfo = info.getExtendedInfo();
+ String contentDescription = String.join(" ", info.getDisplayLabel(),
extendedInfo != null ? extendedInfo : "", appName);
holder.updateContentDescription(contentDescription);
- startSelectableTargetInfoIconLoading(holder, selectableInfo);
+ }
+ } else {
+ DisplayResolveInfo dri = (DisplayResolveInfo) info;
+ holder.bindLabel(dri.getDisplayLabel(), dri.getExtendedInfo(), alwaysShowSubLabel());
+ LoadIconTask task = mIconLoaders.get(dri);
+ if (task == null) {
+ task = new LoadIconTask(dri, holder);
+ mIconLoaders.put(dri, task);
+ task.execute();
} else {
- holder.bindIcon(info);
+ // The holder was potentially changed as the underlying items were
+ // reshuffled, so reset the target holder
+ task.setViewHolder(holder);
}
}
@@ -326,32 +330,6 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
}
- private void startDisplayResolveInfoIconLoading(ViewHolder holder, DisplayResolveInfo info) {
- LoadIconTask task = (LoadIconTask) mIconLoaders.get(info);
- if (task == null) {
- task = new LoadIconTask(info, holder);
- mIconLoaders.put(info, task);
- task.execute();
- } else {
- // The holder was potentially changed as the underlying items were
- // reshuffled, so reset the target holder
- task.setViewHolder(holder);
- }
- }
-
- private void startSelectableTargetInfoIconLoading(
- ViewHolder holder, SelectableTargetInfo info) {
- LoadDirectShareIconTask task = (LoadDirectShareIconTask) mIconLoaders.get(info);
- if (task == null) {
- task = mTestLoadDirectShareTaskProvider == null
- ? new LoadDirectShareIconTask(info)
- : mTestLoadDirectShareTaskProvider.get();
- mIconLoaders.put(info, task);
- task.loadIcon();
- }
- task.setViewHolder(holder);
- }
-
void updateAlphabeticalList() {
new AsyncTask<Void, Void, List<DisplayResolveInfo>>() {
@Override
@@ -366,7 +344,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
Map<String, DisplayResolveInfo> consolidated = new HashMap<>();
for (DisplayResolveInfo info : allTargets) {
String resolvedTarget = info.getResolvedComponentName().getPackageName()
- + '#' + info.getDisplayLabel();
+ + '#' + info.getDisplayLabel();
DisplayResolveInfo multiDri = consolidated.get(resolvedTarget);
if (multiDri == null) {
consolidated.put(resolvedTarget, info);
@@ -375,7 +353,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
} else {
// create consolidated target from the single DisplayResolveInfo
MultiDisplayResolveInfo multiDisplayResolveInfo =
- new MultiDisplayResolveInfo(resolvedTarget, multiDri);
+ new MultiDisplayResolveInfo(resolvedTarget, multiDri);
multiDisplayResolveInfo.addTarget(info);
consolidated.put(resolvedTarget, multiDisplayResolveInfo);
}
@@ -762,24 +740,10 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
/**
- * An alias for onBindView to use with unit tests.
- */
- @VisibleForTesting
- public void testViewBind(View view, TargetInfo info, int position) {
- onBindView(view, info, position);
- }
-
- @VisibleForTesting
- public void setTestLoadDirectShareTaskProvider(LoadDirectShareIconTaskProvider provider) {
- mTestLoadDirectShareTaskProvider = provider;
- }
-
- /**
* Necessary methods to communicate between {@link ChooserListAdapter}
* and {@link ChooserActivity}.
*/
- @VisibleForTesting
- public interface ChooserListCommunicator extends ResolverListCommunicator {
+ interface ChooserListCommunicator extends ResolverListCommunicator {
int getMaxRankedTargets();
@@ -787,59 +751,4 @@ public class ChooserListAdapter extends ResolverListAdapter {
boolean isSendAction(Intent targetIntent);
}
-
- /**
- * Loads direct share targets icons.
- */
- @VisibleForTesting
- public class LoadDirectShareIconTask extends AsyncTask<Void, Void, Void> {
- private final SelectableTargetInfo mTargetInfo;
- private ViewHolder mViewHolder;
-
- private LoadDirectShareIconTask(SelectableTargetInfo targetInfo) {
- mTargetInfo = targetInfo;
- }
-
- @Override
- protected Void doInBackground(Void... voids) {
- mTargetInfo.loadIcon();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void arg) {
- if (mViewHolder != null) {
- mViewHolder.bindIcon(mTargetInfo);
- notifyDataSetChanged();
- }
- }
-
- /**
- * Specifies a view holder that will be updated when the task is completed.
- */
- public void setViewHolder(ViewHolder viewHolder) {
- mViewHolder = viewHolder;
- mViewHolder.bindIcon(mTargetInfo);
- notifyDataSetChanged();
- }
-
- /**
- * An alias for execute to use with unit tests.
- */
- public void loadIcon() {
- execute();
- }
- }
-
- /**
- * An interface for the unit tests to override icon loading task creation
- */
- @VisibleForTesting
- public interface LoadDirectShareIconTaskProvider {
- /**
- * Provides an instance of the task.
- * @return
- */
- LoadDirectShareIconTask get();
- }
}
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 3a3baa726f17..66fff5c13ab7 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -834,11 +834,7 @@ public class ResolverListAdapter extends BaseAdapter {
void onHandlePackagesChanged(ResolverListAdapter listAdapter);
}
- /**
- * A view holder.
- */
- @VisibleForTesting
- public static class ViewHolder {
+ static class ViewHolder {
public View itemView;
public Drawable defaultItemViewBackground;
@@ -846,8 +842,7 @@ public class ResolverListAdapter extends BaseAdapter {
public TextView text2;
public ImageView icon;
- @VisibleForTesting
- public ViewHolder(View view) {
+ ViewHolder(View view) {
itemView = view;
defaultItemViewBackground = view.getBackground();
text = (TextView) view.findViewById(com.android.internal.R.id.text1);
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 37eab40ba2b8..264e4f76d35d 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -37,7 +37,6 @@ import android.service.chooser.ChooserTarget;
import android.text.SpannableStringBuilder;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ChooserActivity;
import com.android.internal.app.ResolverActivity;
import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter;
@@ -60,11 +59,8 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
private final String mDisplayLabel;
private final PackageManager mPm;
private final SelectableTargetInfoCommunicator mSelectableTargetInfoCommunicator;
- @GuardedBy("this")
- private ShortcutInfo mShortcutInfo;
private Drawable mBadgeIcon = null;
private CharSequence mBadgeContentDescription;
- @GuardedBy("this")
private Drawable mDisplayIcon;
private final Intent mFillInIntent;
private final int mFillInFlags;
@@ -82,7 +78,6 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
mModifiedScore = modifiedScore;
mPm = mContext.getPackageManager();
mSelectableTargetInfoCommunicator = selectableTargetInfoComunicator;
- mShortcutInfo = shortcutInfo;
mIsPinned = shortcutInfo != null && shortcutInfo.isPinned();
if (sourceInfo != null) {
final ResolveInfo ri = sourceInfo.getResolveInfo();
@@ -97,6 +92,8 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
}
}
}
+ // TODO(b/121287224): do this in the background thread, and only for selected targets
+ mDisplayIcon = getChooserTargetIconDrawable(chooserTarget, shortcutInfo);
if (sourceInfo != null) {
mBackupResolveInfo = null;
@@ -121,10 +118,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
mChooserTarget = other.mChooserTarget;
mBadgeIcon = other.mBadgeIcon;
mBadgeContentDescription = other.mBadgeContentDescription;
- synchronized (other) {
- mShortcutInfo = other.mShortcutInfo;
- mDisplayIcon = other.mDisplayIcon;
- }
+ mDisplayIcon = other.mDisplayIcon;
mFillInIntent = fillInIntent;
mFillInFlags = flags;
mModifiedScore = other.mModifiedScore;
@@ -147,25 +141,6 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
return mSourceInfo;
}
- /**
- * Load display icon, if needed.
- */
- public void loadIcon() {
- ShortcutInfo shortcutInfo;
- Drawable icon;
- synchronized (this) {
- shortcutInfo = mShortcutInfo;
- icon = mDisplayIcon;
- }
- if (icon == null && shortcutInfo != null) {
- icon = getChooserTargetIconDrawable(mChooserTarget, shortcutInfo);
- synchronized (this) {
- mDisplayIcon = icon;
- mShortcutInfo = null;
- }
- }
- }
-
private Drawable getChooserTargetIconDrawable(ChooserTarget target,
@Nullable ShortcutInfo shortcutInfo) {
Drawable directShareIcon = null;
@@ -295,7 +270,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
}
@Override
- public synchronized Drawable getDisplayIcon(Context context) {
+ public Drawable getDisplayIcon(Context context) {
return mDisplayIcon;
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index b5fc05bc9938..1852c593535b 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -16,10 +16,11 @@
package com.android.internal.inputmethod;
-import android.annotation.AnyThread;
-import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+import android.view.autofill.AutofillManager;
import android.view.inputmethod.HandwritingGesture;
import java.util.StringJoiner;
@@ -281,20 +282,21 @@ public final class InputMethodDebug {
}
/**
- * Return a fixed size string of the object.
- * TODO(b/151575861): Take & return with StringBuilder to make more memory efficient.
+ * Dumps the given {@link View} related to input method focus state for debugging.
*/
- @NonNull
- @AnyThread
- public static String objToString(Object obj) {
- if (obj == null) {
+ public static String dumpViewInfo(@Nullable View view) {
+ if (view == null) {
return "null";
}
- StringBuilder sb = new StringBuilder(64);
- sb.setLength(0);
- sb.append(obj.getClass().getName());
- sb.append("@");
- sb.append(Integer.toHexString(obj.hashCode()));
+ final StringBuilder sb = new StringBuilder();
+ sb.append(view);
+ sb.append(",focus=" + view.hasFocus());
+ sb.append(",windowFocus=" + view.hasWindowFocus());
+ sb.append(",window=" + view.getWindowToken());
+ sb.append(",displayId=" + view.getContext().getDisplayId());
+ sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
+ sb.append(",hasImeFocus=" + view.hasImeFocus());
+
return sb.toString();
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 7001c69f4042..f097bf73eb76 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -16,17 +16,23 @@
package com.android.internal.os;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.BatteryStats.BitDescription;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.HistoryStepDetails;
import android.os.BatteryStats.HistoryTag;
import android.os.BatteryStats.MeasuredEnergyDetails;
+import android.os.Build;
import android.os.Parcel;
import android.os.ParcelFormatException;
import android.os.Process;
import android.os.StatFs;
import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
@@ -210,6 +216,42 @@ public class BatteryStatsHistory {
}
/**
+ * A delegate for android.os.Trace to allow testing static calls. Due to
+ * limitations in Android Tracing (b/153319140), the delegate also records
+ * counter values in system properties which allows reading the value at the
+ * start of a tracing session. This overhead is limited to userdebug builds.
+ * On user builds, tracing still occurs but the counter value will be missing
+ * until the first change occurs.
+ */
+ @VisibleForTesting
+ public static class TraceDelegate {
+ // Note: certain tests currently run as platform_app which is not allowed
+ // to set debug system properties. To ensure that system properties are set
+ // only when allowed, we check the current UID.
+ private final boolean mShouldSetProperty =
+ Build.IS_USERDEBUG && (Process.myUid() == Process.SYSTEM_UID);
+
+ /**
+ * Returns true if trace counters should be recorded.
+ */
+ public boolean tracingEnabled() {
+ return Trace.isTagEnabled(Trace.TRACE_TAG_POWER) || mShouldSetProperty;
+ }
+
+ /**
+ * Records the counter value with the given name.
+ */
+ public void traceCounter(@NonNull String name, int value) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, name, value);
+ if (mShouldSetProperty) {
+ SystemProperties.set("debug.tracing." + name, Integer.toString(value));
+ }
+ }
+ }
+
+ private TraceDelegate mTracer;
+
+ /**
* Constructor
*
* @param systemDir typically /data/system
@@ -219,19 +261,20 @@ public class BatteryStatsHistory {
public BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize,
HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
this(Parcel.obtain(), systemDir, maxHistoryFiles, maxHistoryBufferSize,
- stepDetailsCalculator, clock);
+ stepDetailsCalculator, clock, new TraceDelegate());
initHistoryBuffer();
}
@VisibleForTesting
public BatteryStatsHistory(Parcel historyBuffer, File systemDir,
int maxHistoryFiles, int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
+ HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer) {
mHistoryBuffer = historyBuffer;
mSystemDir = systemDir;
mMaxHistoryFiles = maxHistoryFiles;
mMaxHistoryBufferSize = maxHistoryBufferSize;
mStepDetailsCalculator = stepDetailsCalculator;
+ mTracer = tracer;
mClock = clock;
mHistoryDir = new File(systemDir, HISTORY_DIR);
@@ -272,6 +315,7 @@ public class BatteryStatsHistory {
public BatteryStatsHistory(HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
mStepDetailsCalculator = stepDetailsCalculator;
+ mTracer = new TraceDelegate();
mClock = clock;
mHistoryBuffer = Parcel.obtain();
@@ -287,6 +331,7 @@ public class BatteryStatsHistory {
private BatteryStatsHistory(Parcel historyBuffer,
HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
mHistoryBuffer = historyBuffer;
+ mTracer = new TraceDelegate();
mClock = clock;
mSystemDir = null;
mHistoryDir = null;
@@ -338,7 +383,7 @@ public class BatteryStatsHistory {
// Make a copy of battery history to avoid concurrent modification.
Parcel historyBuffer = Parcel.obtain();
historyBuffer.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
- return new BatteryStatsHistory(historyBuffer, mSystemDir, 0, 0, null, null);
+ return new BatteryStatsHistory(historyBuffer, mSystemDir, 0, 0, null, null, mTracer);
}
/**
@@ -1120,6 +1165,30 @@ public class BatteryStatsHistory {
}
/**
+ * Writes changes to a HistoryItem state bitmap to Atrace.
+ */
+ private void recordTraceCounters(int oldval, int newval, BitDescription[] descriptions) {
+ if (!mTracer.tracingEnabled()) return;
+
+ int diff = oldval ^ newval;
+ if (diff == 0) return;
+
+ for (int i = 0; i < descriptions.length; i++) {
+ BitDescription bd = descriptions[i];
+ if ((diff & bd.mask) == 0) continue;
+
+ int value;
+ if (bd.shift < 0) {
+ value = (newval & bd.mask) != 0 ? 1 : 0;
+ } else {
+ value = (newval & bd.mask) >> bd.shift;
+ }
+
+ mTracer.traceCounter("battery_stats." + bd.name, value);
+ }
+ }
+
+ /**
* Writes the current history item to history.
*/
public void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs) {
@@ -1159,6 +1228,12 @@ public class BatteryStatsHistory {
+ Integer.toHexString(diffStates2) + " lastDiff2="
+ Integer.toHexString(lastDiffStates2));
}
+
+ recordTraceCounters(mHistoryLastWritten.states,
+ cur.states & mActiveHistoryStates, BatteryStats.HISTORY_STATE_DESCRIPTIONS);
+ recordTraceCounters(mHistoryLastWritten.states2,
+ cur.states2 & mActiveHistoryStates2, BatteryStats.HISTORY_STATE2_DESCRIPTIONS);
+
if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
&& timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0
&& (diffStates2 & lastDiffStates2) == 0
diff --git a/core/java/com/android/internal/os/ZygoteConnectionConstants.java b/core/java/com/android/internal/os/ZygoteConnectionConstants.java
index 0c1cd6de1bb4..fe9b99241345 100644
--- a/core/java/com/android/internal/os/ZygoteConnectionConstants.java
+++ b/core/java/com/android/internal/os/ZygoteConnectionConstants.java
@@ -41,5 +41,5 @@ public class ZygoteConnectionConstants {
* WARNING: This may trigger the watchdog in debug mode. However, to support
* wrapping on lower-end devices we do not have much choice.
*/
- public static final int WRAPPED_PID_TIMEOUT_MILLIS = 30000;
+ public static final int WRAPPED_PID_TIMEOUT_MILLIS = 20000;
}
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 527286cf000e..a065e2b78d80 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -287,7 +287,6 @@ public class DividerSnapAlgorithm {
int dividerMax = isHorizontalDivision
? mDisplayHeight
: mDisplayWidth;
- int navBarSize = isHorizontalDivision ? mInsets.bottom : mInsets.right;
int startPos = -mDividerSize;
if (dockedSide == DOCKED_RIGHT) {
startPos += mInsets.left;
@@ -308,8 +307,7 @@ public class DividerSnapAlgorithm {
addMinimizedTarget(isHorizontalDivision, dockedSide);
break;
}
- mTargets.add(new SnapTarget(dividerMax - navBarSize, dividerMax,
- SnapTarget.FLAG_DISMISS_END, 0.35f));
+ mTargets.add(new SnapTarget(dividerMax, dividerMax, SnapTarget.FLAG_DISMISS_END, 0.35f));
}
private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 9f21760a49e0..4cf0ba19a887 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -111,6 +111,7 @@ public class ConversationLayout extends FrameLayout
private Icon mLargeIcon;
private View mExpandButtonContainer;
private ViewGroup mExpandButtonAndContentContainer;
+ private ViewGroup mExpandButtonContainerA11yContainer;
private NotificationExpandButton mExpandButton;
private MessagingLinearLayout mImageMessageContainer;
private int mBadgeProtrusion;
@@ -234,6 +235,8 @@ public class ConversationLayout extends FrameLayout
});
mConversationText = findViewById(R.id.conversation_text);
mExpandButtonContainer = findViewById(R.id.expand_button_container);
+ mExpandButtonContainerA11yContainer =
+ findViewById(R.id.expand_button_a11y_container);
mConversationHeader = findViewById(R.id.conversation_header);
mContentContainer = findViewById(R.id.notification_action_list_margin_target);
mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container);
@@ -1091,7 +1094,7 @@ public class ConversationLayout extends FrameLayout
newContainer = mExpandButtonAndContentContainer;
} else {
buttonGravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
- newContainer = this;
+ newContainer = mExpandButtonContainerA11yContainer;
}
mExpandButton.setExpanded(!mIsCollapsed);
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 9ee9b8249493..80d50ff5d310 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -90,8 +90,10 @@ public class PointerLocationView extends View implements InputDeviceListener,
private float mBoundingBottom;
// Position estimator.
- private VelocityTracker.Estimator mEstimator = new VelocityTracker.Estimator();
- private VelocityTracker.Estimator mAltEstimator = new VelocityTracker.Estimator();
+ private VelocityTracker.Estimator mEstimatorX = new VelocityTracker.Estimator();
+ private VelocityTracker.Estimator mAltEstimatorX = new VelocityTracker.Estimator();
+ private VelocityTracker.Estimator mEstimatorY = new VelocityTracker.Estimator();
+ private VelocityTracker.Estimator mAltEstimatorY = new VelocityTracker.Estimator();
@UnsupportedAppUsage
public PointerState() {
@@ -679,11 +681,13 @@ public class PointerLocationView extends View implements InputDeviceListener,
ps.addTrace(coords.x, coords.y, true);
ps.mXVelocity = mVelocity.getXVelocity(id);
ps.mYVelocity = mVelocity.getYVelocity(id);
- mVelocity.getEstimator(id, ps.mEstimator);
+ mVelocity.getEstimator(MotionEvent.AXIS_X, id, ps.mEstimatorX);
+ mVelocity.getEstimator(MotionEvent.AXIS_Y, id, ps.mEstimatorY);
if (mAltVelocity != null) {
ps.mAltXVelocity = mAltVelocity.getXVelocity(id);
ps.mAltYVelocity = mAltVelocity.getYVelocity(id);
- mAltVelocity.getEstimator(id, ps.mAltEstimator);
+ mAltVelocity.getEstimator(MotionEvent.AXIS_X, id, ps.mAltEstimatorX);
+ mAltVelocity.getEstimator(MotionEvent.AXIS_Y, id, ps.mAltEstimatorY);
}
ps.mToolType = event.getToolType(i);
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index 46bd6824da7e..16b9f008ec65 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -32,8 +32,7 @@ namespace android {
static const int ACTIVE_POINTER_ID = -1;
static struct {
- jfieldID xCoeff;
- jfieldID yCoeff;
+ jfieldID coeff;
jfieldID degree;
jfieldID confidence;
} gEstimatorClassInfo;
@@ -47,28 +46,22 @@ public:
void clear();
void addMovement(const MotionEvent* event);
+ // TODO(b/32830165): consider supporting an overload that supports computing velocity only for
+ // a subset of the supported axes.
void computeCurrentVelocity(int32_t units, float maxVelocity);
- void getVelocity(int32_t id, float* outVx, float* outVy);
- bool getEstimator(int32_t id, VelocityTracker::Estimator* outEstimator);
+ float getVelocity(int32_t axis, int32_t id);
+ bool getEstimator(int32_t axis, int32_t id, VelocityTracker::Estimator* outEstimator);
private:
- struct Velocity {
- float vx, vy;
- };
-
VelocityTracker mVelocityTracker;
- int32_t mActivePointerId;
- BitSet32 mCalculatedIdBits;
- Velocity mCalculatedVelocity[MAX_POINTERS];
+ VelocityTracker::ComputedVelocity mComputedVelocity;
};
VelocityTrackerState::VelocityTrackerState(const VelocityTracker::Strategy strategy)
- : mVelocityTracker(strategy), mActivePointerId(-1) {}
+ : mVelocityTracker(strategy) {}
void VelocityTrackerState::clear() {
mVelocityTracker.clear();
- mActivePointerId = -1;
- mCalculatedIdBits.clear();
}
void VelocityTrackerState::addMovement(const MotionEvent* event) {
@@ -76,61 +69,20 @@ void VelocityTrackerState::addMovement(const MotionEvent* event) {
}
void VelocityTrackerState::computeCurrentVelocity(int32_t units, float maxVelocity) {
- BitSet32 idBits(mVelocityTracker.getCurrentPointerIdBits());
- mCalculatedIdBits = idBits;
-
- for (uint32_t index = 0; !idBits.isEmpty(); index++) {
- uint32_t id = idBits.clearFirstMarkedBit();
-
- float vx, vy;
- mVelocityTracker.getVelocity(id, &vx, &vy);
-
- vx = vx * units / 1000;
- vy = vy * units / 1000;
-
- if (vx > maxVelocity) {
- vx = maxVelocity;
- } else if (vx < -maxVelocity) {
- vx = -maxVelocity;
- }
- if (vy > maxVelocity) {
- vy = maxVelocity;
- } else if (vy < -maxVelocity) {
- vy = -maxVelocity;
- }
-
- Velocity& velocity = mCalculatedVelocity[index];
- velocity.vx = vx;
- velocity.vy = vy;
- }
+ mComputedVelocity = mVelocityTracker.getComputedVelocity(units, maxVelocity);
}
-void VelocityTrackerState::getVelocity(int32_t id, float* outVx, float* outVy) {
+float VelocityTrackerState::getVelocity(int32_t axis, int32_t id) {
if (id == ACTIVE_POINTER_ID) {
id = mVelocityTracker.getActivePointerId();
}
- float vx, vy;
- if (id >= 0 && id <= MAX_POINTER_ID && mCalculatedIdBits.hasBit(id)) {
- uint32_t index = mCalculatedIdBits.getIndexOfBit(id);
- const Velocity& velocity = mCalculatedVelocity[index];
- vx = velocity.vx;
- vy = velocity.vy;
- } else {
- vx = 0;
- vy = 0;
- }
-
- if (outVx) {
- *outVx = vx;
- }
- if (outVy) {
- *outVy = vy;
- }
+ return mComputedVelocity.getVelocity(axis, id).value_or(0);
}
-bool VelocityTrackerState::getEstimator(int32_t id, VelocityTracker::Estimator* outEstimator) {
- return mVelocityTracker.getEstimator(id, outEstimator);
+bool VelocityTrackerState::getEstimator(int32_t axis, int32_t id,
+ VelocityTracker::Estimator* outEstimator) {
+ return mVelocityTracker.getEstimator(axis, id, outEstimator);
}
// Return a strategy enum from integer value.
@@ -177,37 +129,25 @@ static void android_view_VelocityTracker_nativeComputeCurrentVelocity(JNIEnv* en
state->computeCurrentVelocity(units, maxVelocity);
}
-static jfloat android_view_VelocityTracker_nativeGetXVelocity(JNIEnv* env, jclass clazz,
- jlong ptr, jint id) {
+static jfloat android_view_VelocityTracker_nativeGetVelocity(JNIEnv* env, jclass clazz, jlong ptr,
+ jint axis, jint id) {
VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
- float vx;
- state->getVelocity(id, &vx, NULL);
- return vx;
-}
-
-static jfloat android_view_VelocityTracker_nativeGetYVelocity(JNIEnv* env, jclass clazz,
- jlong ptr, jint id) {
- VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
- float vy;
- state->getVelocity(id, NULL, &vy);
- return vy;
+ return state->getVelocity(axis, id);
}
static jboolean android_view_VelocityTracker_nativeGetEstimator(JNIEnv* env, jclass clazz,
- jlong ptr, jint id, jobject outEstimatorObj) {
+ jlong ptr, jint axis, jint id,
+ jobject outEstimatorObj) {
VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
VelocityTracker::Estimator estimator;
- bool result = state->getEstimator(id, &estimator);
- jfloatArray xCoeffObj = jfloatArray(env->GetObjectField(outEstimatorObj,
- gEstimatorClassInfo.xCoeff));
- jfloatArray yCoeffObj = jfloatArray(env->GetObjectField(outEstimatorObj,
- gEstimatorClassInfo.yCoeff));
+ bool result = state->getEstimator(axis, id, &estimator);
+
+ jfloatArray coeffObj =
+ jfloatArray(env->GetObjectField(outEstimatorObj, gEstimatorClassInfo.coeff));
- env->SetFloatArrayRegion(xCoeffObj, 0, VelocityTracker::Estimator::MAX_DEGREE + 1,
- estimator.xCoeff);
- env->SetFloatArrayRegion(yCoeffObj, 0, VelocityTracker::Estimator::MAX_DEGREE + 1,
- estimator.yCoeff);
+ env->SetFloatArrayRegion(coeffObj, 0, VelocityTracker::Estimator::MAX_DEGREE + 1,
+ estimator.coeff);
env->SetIntField(outEstimatorObj, gEstimatorClassInfo.degree, estimator.degree);
env->SetFloatField(outEstimatorObj, gEstimatorClassInfo.confidence, estimator.confidence);
return result;
@@ -224,9 +164,8 @@ static const JNINativeMethod gVelocityTrackerMethods[] = {
(void*)android_view_VelocityTracker_nativeAddMovement},
{"nativeComputeCurrentVelocity", "(JIF)V",
(void*)android_view_VelocityTracker_nativeComputeCurrentVelocity},
- {"nativeGetXVelocity", "(JI)F", (void*)android_view_VelocityTracker_nativeGetXVelocity},
- {"nativeGetYVelocity", "(JI)F", (void*)android_view_VelocityTracker_nativeGetYVelocity},
- {"nativeGetEstimator", "(JILandroid/view/VelocityTracker$Estimator;)Z",
+ {"nativeGetVelocity", "(JII)F", (void*)android_view_VelocityTracker_nativeGetVelocity},
+ {"nativeGetEstimator", "(JIILandroid/view/VelocityTracker$Estimator;)Z",
(void*)android_view_VelocityTracker_nativeGetEstimator},
};
@@ -236,8 +175,7 @@ int register_android_view_VelocityTracker(JNIEnv* env) {
jclass clazz = FindClassOrDie(env, "android/view/VelocityTracker$Estimator");
- gEstimatorClassInfo.xCoeff = GetFieldIDOrDie(env, clazz, "xCoeff", "[F");
- gEstimatorClassInfo.yCoeff = GetFieldIDOrDie(env, clazz, "yCoeff", "[F");
+ gEstimatorClassInfo.coeff = GetFieldIDOrDie(env, clazz, "coeff", "[F");
gEstimatorClassInfo.degree = GetFieldIDOrDie(env, clazz, "degree", "I");
gEstimatorClassInfo.confidence = GetFieldIDOrDie(env, clazz, "confidence", "F");
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index 42fb4a26dd3b..ce8a90495572 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -89,45 +89,62 @@
<include layout="@layout/notification_material_action_list" />
</com.android.internal.widget.RemeasuringLinearLayout>
- <!--This is dynamically placed between here and at the end of the layout. It starts here since
- only FrameLayout layout params have gravity-->
+ <!--expand_button_a11y_container ensures talkback focus order is correct when view is expanded.
+ The -1px of marginTop and 1px of paddingTop make sure expand_button_a11y_container is prior to
+ its sibling view in accessibility focus order.
+ {see android.view.ViewGroup.addChildrenForAccessibility()}
+ expand_button_container will be moved under expand_button_and_content_container when collapsed,
+ this dynamic movement ensures message can flow under expand button when expanded-->
<FrameLayout
- android:id="@+id/expand_button_container"
- android:layout_width="wrap_content"
+ android:id="@+id/expand_button_a11y_container"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="end|top"
android:clipChildren="false"
- android:clipToPadding="false">
- <!--This layout makes sure that we can nicely center the expand content in the
- collapsed layout while the parent makes sure that we're never laid out bigger
- than the messaging content.-->
- <LinearLayout
- android:id="@+id/expand_button_touch_container"
+ android:clipToPadding="false"
+ android:layout_marginTop="-1px"
+ android:paddingTop="1px"
+ >
+ <!--expand_button_container is dynamically placed between here and at the end of the
+ layout. It starts here since only FrameLayout layout params have gravity-->
+ <FrameLayout
+ android:id="@+id/expand_button_container"
android:layout_width="wrap_content"
- android:layout_height="@dimen/conversation_expand_button_height"
- android:orientation="horizontal"
+ android:layout_height="match_parent"
android:layout_gravity="end|top"
- android:paddingEnd="0dp"
- android:clipToPadding="false"
android:clipChildren="false"
- >
- <!-- Images -->
- <com.android.internal.widget.MessagingLinearLayout
- android:id="@+id/conversation_image_message_container"
- android:forceHasOverlappingRendering="false"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:layout_marginStart="@dimen/conversation_image_start_margin"
- android:spacing="0dp"
- android:layout_gravity="center"
+ android:clipToPadding="false">
+ <!--expand_button_touch_container makes sure that we can nicely center the expand
+ content in the collapsed layout while the parent makes sure that we're never laid out
+ bigger than the messaging content.-->
+ <LinearLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/conversation_expand_button_height"
+ android:orientation="horizontal"
+ android:layout_gravity="end|top"
+ android:paddingEnd="0dp"
android:clipToPadding="false"
android:clipChildren="false"
- />
- <include layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- />
- </LinearLayout>
+ >
+ <!-- Images -->
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/conversation_image_message_container"
+ android:forceHasOverlappingRendering="false"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_marginStart="@dimen/conversation_image_start_margin"
+ android:spacing="0dp"
+ android:layout_gravity="center"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ />
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ />
+ </LinearLayout>
+ </FrameLayout>
</FrameLayout>
</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 99e98f2d7e26..e5b97f7c8423 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Vingerafdrukhandeling is gekanselleer."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vingerafdrukhandeling is deur gebruiker gekanselleer."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Te veel pogings. Probeer later weer."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Te veel pogings. Gebruik eerder skermslot."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Probeer weer."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukke is geregistreer nie."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Hierdie toetstel het nie \'n vingerafdruksensor nie."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Besoek \'n verskaffer wat herstelwerk doen."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Kan nie jou gesigmodel skep nie. Probeer weer."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Te helder. Probeer sagter beligting."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Probeer helderder beligting"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Beweeg foon verder weg"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Beweeg foon nader"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Beweeg foon hoër op"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index efb66869ea73..c291d9b02a16 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -106,8 +106,8 @@
<string name="roamingText0" msgid="7793257871609854208">"በዝውውር ላይ አመላካች በርቷል"</string>
<string name="roamingText1" msgid="5073028598334616445">"በዝውውር ላይ አመልካች ጠፍቷል"</string>
<string name="roamingText2" msgid="2834048284153110598">"በዝውውር ላይ አመልካች ብልጭ ብልጭ ይላል"</string>
- <string name="roamingText3" msgid="831690234035748988">"ከጎረቤት ውጪ"</string>
- <string name="roamingText4" msgid="2171252529065590728">"ከህንፃ ውጪ"</string>
+ <string name="roamingText3" msgid="831690234035748988">"ከጎረቤት ውጭ"</string>
+ <string name="roamingText4" msgid="2171252529065590728">"ከህንፃ ውጭ"</string>
<string name="roamingText5" msgid="4294671587635796641">"የዝውውር- ተመራጭ ስርዓት"</string>
<string name="roamingText6" msgid="5536156746637992029">"ዝውውር- ዝግጁ የሆነ ስርዓት"</string>
<string name="roamingText7" msgid="1783303085512907706">" የዝውውር- አጋር ስምምነት"</string>
@@ -428,7 +428,7 @@
<string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"ይህ መተግበሪያ ሁሉንም በእርስዎ ጡባዊ ላይ የተከማቹ የቀን መቁጠሪያ ክስተቶችን ማንበብ ወይም የእርስዎን የቀን መቁጠሪያ ውሂብ ማስቀመጥ ይችላል።"</string>
<string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ይህ መተግበሪያ ሁሉንም በእርስዎ Android TV መሣሪያ ላይ የተከማቹ የቀን መቁጠሪያ ክስተቶችን ማንበብ ወይም የእርስዎን የቀን መቁጠሪያ ውሂብ ማስቀመጥ ይችላል።"</string>
<string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"ይህ መተግበሪያ ሁሉንም በእርስዎ ስልክ ላይ የተከማቹ የቀን መቁጠሪያ ክስተቶችን ማንበብ ወይም የእርስዎን የቀን መቁጠሪያ ውሂብ ማስቀመጥ ይችላል።"</string>
- <string name="permlab_writeCalendar" msgid="6422137308329578076">"የቀን መቁጠሪያ ክስተቶችን ቀይር ወይም አክል እና ለእንግዶች ከባለቤቱ ዕውቅና ውጪ ላክ።"</string>
+ <string name="permlab_writeCalendar" msgid="6422137308329578076">"የቀን መቁጠሪያ ክስተቶችን ቀይር ወይም አክል እና ለእንግዶች ከባለቤቱ ዕውቅና ውጭ ላክ።"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"ይህ መተግበሪያ በእርስዎ ጡባዊ ላይ የቀን መቁጠሪያ ክስተቶችን ሊያክል፣ ሊያስወግድ ወይም ሊለውጥ ይችላል። ይህ መተግበሪያ ከቀን መቁጠሪያ የመጡ መስለው የሚታዩ መልእክቶችን ሊልክ ወይም ባለቤቶቹን ሳያሳውቅ ክስተቶችን ሊለውጥ ይችላል።"</string>
<string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ይህ መተግበሪያ በእርስዎ Android TV መሣሪያ ላይ የቀን መቁጠሪያ ክስተቶችን ሊያክል፣ ሊያስወግድ ወይም ሊለውጥ ይችላል። ይህ መተግበሪያ ከቀን መቁጠሪያ የመጡ መስለው የሚታዩ መልእክቶችን ሊልክ ወይም ባለቤቶቹን ሳያሳውቅ ክስተቶችን ሊለውጥ ይችላል።"</string>
<string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ይህ መተግበሪያ በእርስዎ ስልክ ላይ የቀን መቁጠሪያ ክስተቶችን ሊያክል፣ ሊያስወግድ ወይም ሊለውጥ ይችላል። ይህ መተግበሪያ ከቀን መቁጠሪያ የመጡ መስለው የሚታዩ መልእክቶችን ሊልክ ወይም ባለቤቶቹን ሳያሳውቅ ክስተቶችን ሊለውጥ ይችላል።"</string>
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"የጣት አሻራ ስርዓተ ክወና ተትቷል።"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"የጣት አሻራ ክወና በተጠቃሚ ተሰርዟል።"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ከልክ በላይ ብዙ ሙከራዎች። በኋላ ላይ እንደገና ይሞክሩ።"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"በጣም ብዙ ሙከራዎች። በምትኩ የማያ ገጽ መቆለፊያን ይጠቀሙ።"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"እንደገና ይሞክሩ።"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ምንም የጣት አሻራዎች አልተመዘገቡም።"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም።"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"የጥገና አገልግሎት ሰጪን ይጎብኙ።"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"የመልክዎን ሞዴል መፍጠር አልተቻለም። እንደገና ይሞክሩ።"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ከልክ በላይ ፈካ ያለ። ይበልጥ ረጋ ያለ ብርሃን አጠቃቀምን ይሞክሩ።"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"ከዚህ ፈካ ያለ ብርሃንን ይሞክሩ"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ስልኩን ያርቁት"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ስልኩን ያቅርቡት"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ስልኩን ከፍ ወዳለ ቦታ ይውሰዱት"</string>
@@ -1163,7 +1163,7 @@
<string name="dialog_alert_title" msgid="651856561974090712">"ትኩረት"</string>
<string name="loading" msgid="3138021523725055037">"በመጫን ላይ…"</string>
<string name="capital_on" msgid="2770685323900821829">"በ"</string>
- <string name="capital_off" msgid="7443704171014626777">"ውጪ"</string>
+ <string name="capital_off" msgid="7443704171014626777">"ውጭ"</string>
<string name="checked" msgid="9179896827054513119">"ምልክት ተደርጎበታል"</string>
<string name="not_checked" msgid="7972320087569023342">"ምልክት አልተደረገበትም"</string>
<string name="selected" msgid="6614607926197755875">"ተመርጧል"</string>
@@ -1476,7 +1476,7 @@
<string name="permission_request_notification_title" msgid="1810025922441048273">"ፈቃድ ተጠይቋል"</string>
<string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">\n" ለ<xliff:g id="ACCOUNT">%s</xliff:g> መለያ ፈቃድ ተጠይቋል"</string>
<string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"ለመለያ <xliff:g id="ACCOUNT">%2$s</xliff:g>\nበ<xliff:g id="APP">%1$s</xliff:g> የተጠየቀ ፈቃድ።"</string>
- <string name="forward_intent_to_owner" msgid="4620359037192871015">"ከስራ መገለጫዎ ውጪ ሆነው መተግበሪያ እየተጠቀሙ ነው"</string>
+ <string name="forward_intent_to_owner" msgid="4620359037192871015">"ከስራ መገለጫዎ ውጭ ሆነው መተግበሪያ እየተጠቀሙ ነው"</string>
<string name="forward_intent_to_work" msgid="3620262405636021151">"ይህን መተግበሪያ በእርስዎ የስራ መገለጫ ላይ እየተጠቀሙበት ነው"</string>
<string name="input_method_binding_label" msgid="1166731601721983656">"ግቤት ስልት"</string>
<string name="sync_binding_label" msgid="469249309424662147">"አስምር"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 503b6973ce27..39f68729e362 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -609,8 +609,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"تم إلغاء تشغيل بصمة الإصبع."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"تم إلغاء تشغيل بصمة الإصبع بواسطة المستخدم."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"تم إجراء عدد كبير من المحاولات. أعد المحاولة لاحقًا."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"أعد المحاولة."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ليست هناك بصمات إصبع مسجَّلة."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"لا يحتوي هذا الجهاز على مستشعِر بصمات إصبع."</string>
@@ -639,7 +638,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"يُرجى التواصل مع مقدِّم خدمات إصلاح."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"يتعذّر إنشاء نموذج الوجه. يُرجى إعادة المحاولة."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ساطع للغاية. تجربة مستوى سطوع أقلّ."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"جرِّب زيادة الإضاءة."</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"يُرجى إبعاد الهاتف عنك."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"يُرجى تقريب الهاتف منك."</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"يُرجى رفع الهاتف للأعلى."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index ce464194d1e6..d6a4bb0a88be 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ফিংগাৰপ্ৰিণ্ট কাৰ্য বাতিল কৰা হ’ল।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ব্যৱহাৰকাৰীয়ে ফিংগাৰপ্ৰিণ্ট ক্ৰিয়া বাতিল কৰিছে।"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"অতি বেছিসংখ্যক প্ৰয়াস। ইয়াৰ সলনি স্ক্ৰীন লক ব্যৱহাৰ কৰক।"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"আকৌ চেষ্টা কৰক।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনো ফিংগাৰপ্ৰিণ্ট যোগ কৰা নহ\'ল।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই।"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"মেৰামতি সেৱা প্ৰদানকাৰী কোনো প্ৰতিষ্ঠানলৈ যাওক।"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"মুখাৱয়বৰ মডেল সৃষ্টি কৰিব নোৱাৰি। পুনৰ চেষ্টা কৰক।"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"অতি উজ্জ্বল। ইয়াতকৈ কম পোহৰৰ উৎস ব্যৱহাৰ কৰক।"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"উজ্জ্বল পোহৰ থকা ঠাইলৈ গৈ চাওক"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ফ’নটো আৰু আঁতৰলৈ নিয়ক"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ফ’নটো ওচৰলৈ আনক"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ফ’নটো ওপৰলৈ নিয়ক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 0e2fd3fe77ae..370fc6d8a637 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Barmaq izi əməliyyatı ləğv edildi."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Barmaq izi əməliyyatı istifadəçi tərəfindən ləğv edildi."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Cəhdlər çox oldu. Sonraya saxlayın."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Həddindən çox cəhd edilib. Əvəzində ekran kilidindən istifadə edin."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Yenidən cəhd edin."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Barmaq izi qeydə alınmayıb."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda barmaq izi sensoru yoxdur."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Təmir provayderini ziyarət edin."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Üz modelinizi yaratmaq olmur. Yenə cəhd edin."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Çox işıqlıdır. Daha az işıqlı şəkli sınayın."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Parlaq işıqdan istifadə edin"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Telefonu uzaq tutun"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Telefonu yaxına tutun"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Telefonu yuxarı tutun"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 05d100b38798..ef2a68cbcfe3 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -606,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja sa otiskom prsta je otkazana."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Korisnik je otkazao radnju sa otiskom prsta."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Previše pokušaja. Probajte ponovo kasnije."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Koristite zaključavanje ekrana umesto toga."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Probajte ponovo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registrovan nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
@@ -636,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Posetite dobavljača za popravke."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Pravljenje modela lica nije uspelo. Probajte ponovo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Previše je svetlo. Probajte sa slabijim osvetljenjem."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Probajte sa jačim osvetljenjem"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Udaljite telefon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Približite telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Pomerite telefon nagore"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 511dfdcd19fb..1212c02a9410 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -607,8 +607,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Аперацыя з адбіткамі пальцаў скасавана."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Аўтэнтыфікацыя па адбітках пальцаў скасавана карыстальнікам."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Занадта шмат спроб. Паспрабуйце яшчэ раз пазней."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Занадта шмат спроб. Скарыстайце блакіроўку экрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Паўтарыце спробу."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Адбіткі пальцаў не зарэгістраваны."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На гэтай прыладзе няма сканера адбіткаў пальцаў."</string>
@@ -637,7 +636,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Звярніцеся ў сэрвісны цэнтр."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Не ўдалося стварыць мадэль твару. Паўтарыце."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Занадта светла. Прыглушыце асвятленне."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Павялічце асвятленне"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Перамясціце тэлефон далей"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Перамясціце тэлефон бліжэй"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Перамясціце тэлефон вышэй"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 8f5aa43c69f9..100774630f6d 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Операцията за отпечатък е анулирана."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Операцията за удостоверяване чрез отпечатък бе анулирана от потребителя."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Твърде много опити. Пробвайте отново по-късно."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Твърде много опити. Вместо това използвайте опция за заключване на екрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Опитайте отново."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Няма регистрирани отпечатъци."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Това устройство няма сензор за отпечатъци."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Посетете оторизиран сервиз."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Моделът на лицето ви не бе създаден. Опитайте пак."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Твърде светло е. Опитайте при по-слабо осветление."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Опитайте при по-силно осветление"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Отдалечете телефона"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Доближете телефона"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Преместете телефона по-високо"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 7beea9a02dae..96acf7fc6b19 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"আঙ্গুলের ছাপ অপারেশন বাতিল করা হয়েছে৷"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ব্যবহারকারী আঙ্গুলের ছাপের অপারেশনটি বাতিল করেছেন।"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"অনেকবার প্রচেষ্টা করা হয়েছে৷ পরে আবার চেষ্টা করুন৷"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"অনেকবার চেষ্টা করেছেন। পরিবর্তে স্ক্রিন লক ব্যবহার করুন।"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনও আঙ্গুলের ছাপ নথিভুক্ত করা হয়নি।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই।"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"একজন মেরামতি মিস্ত্রির কাছে যান।"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ফেস মডেল তৈরি করা যাচ্ছে না। আবার চেষ্টা করুন।"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"খুব উজ্জ্বল। আলো কমিয়ে চেষ্টা করে দেখুন।"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"আরও উজ্জ্বল আলো ব্যবহার করে দেখুন"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ফোন আরও দূরে নিয়ে যান"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ফোন আরও কাছে নিয়ে আসুন"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ফোন আরও উঁচুতে তুলুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f182ab4eca14..f7a5f388405e 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -606,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja s otiskom prsta je otkazana."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Korisnik je otkazao radnju s otiskom prsta."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Umjesto toga koristite zaključavanje ekrana."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Pokušajte ponovo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije prijavljen nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
@@ -636,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Posjetite pružaoca usluga za popravke."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nije moguće kreirati model lica. Pokušajte ponovo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Previše svijetlo. Probajte s blažim osvjetljenjem."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Pokušajte s jačim osvjetljenjem"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Odmaknite telefon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Primaknite telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Pomjerite telefon naviše"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e88bb559f7ba..4819c9eb44f8 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"S\'ha cancel·lat l\'operació d\'empremta digital."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"L\'usuari ha cancel·lat l\'operació d\'empremta digital."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"S\'han produït massa intents. Torna-ho a provar més tard."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Massa intents. Utilitza el bloqueig de pantalla."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Torna-ho a provar."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No s\'ha registrat cap empremta digital."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aquest dispositiu no té sensor d\'empremtes digitals."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visita un proveïdor de reparacions."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"No es pot crear el model facial. Torna-ho a provar."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Massa brillant Prova una il·luminació més suau."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Prova amb més il·luminació"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Allunya\'t del telèfon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Apropa el telèfon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mou el telèfon més amunt"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index d1c4d76b1b9c..83c4afbb2179 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -607,8 +607,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operace otisku prstu byla zrušena."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Uživatel operaci s otiskem prstu zrušil."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Příliš mnoho pokusů. Zkuste to později."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Příliš mnoho pokusů. Použijte zámek obrazovky."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Zkuste to znovu."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nejsou zaregistrovány žádné otisky prstů."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zařízení nemá snímač otisků prstů."</string>
@@ -637,7 +636,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Navštivte servis"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Model se nepodařilo vytvořit. Zkuste to znovu."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Je příliš světlo. Zmírněte osvětlení."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Přejděte na světlo"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Umístěte telefon dál"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Umístěte telefon blíž"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Umístěte telefon výš"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 5191aead207c..672e7c7ac492 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeraftrykshandlingen blev annulleret."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeraftrykshandlingen blev annulleret af brugeren."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Du har prøvet for mange gange. Prøv igen senere."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Du har brugt for mange forsøg. Brug skærmlåsen i stedet."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Prøv igen."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Der er ikke registreret nogen fingeraftryk."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykslæser."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Få den repareret."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Din ansigtsmodel kan ikke oprettes. Prøv igen."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Der er for lyst. Prøv en mere dæmpet belysning."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Prøv med mere belysning"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Flyt telefonen længere væk"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Flyt telefonen tættere på"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Løft telefonen højere op"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index b5af566e9380..01e3d457ec1a 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerabdruckvorgang abgebrochen"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Zu viele Versuche. Verwende stattdessen die Displaysperre."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Keine Fingerabdrücke erfasst."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dieses Gerät hat keinen Fingerabdrucksensor."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Suche einen Reparaturdienstleister auf."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Kein Gesichtsmodell möglich. Versuche es erneut."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Zu hell. Schwächere Beleuchtung ausprobieren."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Probiere es mit einer helleren Beleuchtung"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Bewege das Smartphone weiter weg"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Bewege das Smartphone näher heran"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Bewege das Smartphone nach oben"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 72290963aaa6..0e3c0a1da97c 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε από τον χρήστη."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Πάρα πολλές προσπάθειες. Δοκιμάστε ξανά αργότερα."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Υπερβολικά πολλές προσπάθειες. Χρησιμοποιήστε εναλλακτικά το κλείδωμα οθόνης."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Δοκιμάστε ξανά."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Επισκεφτείτε έναν πάροχο υπηρεσιών επισκευής."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Αδύν. η δημιουρ. του μοντ. προσώπ. Δοκιμάστε ξανά."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Υπερβολικά έντονος φωτισμός. Δοκιμάστε πιο ήπιο."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Δοκιμάστε με περισσότερο φως"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Απομακρύνετε περισσότερο το τηλέφωνο"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Φέρτε πιο κοντά το τηλέφωνό σας"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Μετακινήστε το τηλέφωνο πιο ψηλά"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index f0980bd6f0b6..805e0147d65e 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -634,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Can’t create your face model. Try again."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Too bright. Try gentler lighting."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Try brighter lighting"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Move phone further away"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Move phone closer"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Move phone higher"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 5907d8813cde..79d39239d1b1 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -634,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Can’t create your face model. Try again."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Too bright. Try gentler lighting."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Try brighter lighting"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Move phone further away"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Move phone closer"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Move phone higher"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ebe3a4ebbd97..805496984bbe 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -634,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Can’t create your face model. Try again."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Too bright. Try gentler lighting."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Try brighter lighting"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Move phone further away"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Move phone closer"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Move phone higher"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 5d9c8a2f9d19..9d1c6a46bf8c 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -634,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Can’t create your face model. Try again."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Too bright. Try gentler lighting."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Try brighter lighting"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Move phone further away"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Move phone closer"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Move phone higher"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 3920362b1835..eae7c27a7afa 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -634,7 +634,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎Visit a repair provider.‎‏‎‎‏‎"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‎‎Can’t create your face model. Try again.‎‏‎‎‏‎"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‏‏‏‎‎‎Too bright. Try gentler lighting.‎‏‎‎‏‎"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎Try brighter lighting‎‏‎‎‏‎"</string>
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‏‏‏‎‎Not enough light‎‏‎‎‏‎"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎Move phone farther away‎‏‎‎‏‎"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‎Move phone closer‎‏‎‎‏‎"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‎‎Move phone higher‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 575476d44608..f06903b559d1 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -46,6 +46,7 @@
<string name="needPuk2" msgid="7032612093451537186">"Escribir PUK2 para desbloquear la tarjeta SIM."</string>
<string name="enablePin" msgid="2543771964137091212">"Error; habilita el bloqueo de SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
+ <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
<item quantity="other">Tienes <xliff:g id="NUMBER_1">%d</xliff:g> intentos más antes de que se bloquee la tarjeta SIM.</item>
<item quantity="one">Tienes <xliff:g id="NUMBER_0">%d</xliff:g> un intento más antes de que se bloquee la tarjeta SIM.</item>
</plurals>
@@ -175,7 +176,7 @@
<string name="low_memory" product="watch" msgid="3479447988234030194">"El almacenamiento del reloj está completo. Elimina algunos archivos para liberar espacio."</string>
<string name="low_memory" product="tv" msgid="6663680413790323318">"El almacenamiento del dispositivo Android TV está lleno. Borra algunos archivos para liberar espacio."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"Se ha agotado el espacio de almacenamiento del dispositivo. Elimina algunos archivos para liberar espacio."</string>
- <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Se instaló la autoridad certificadora}other{Se instalaron las autoridades certificadoras}}"</string>
+ <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Se instaló la autoridad certificadora}many{Se instalaron las autoridades certificadoras}other{Se instalaron las autoridades certificadoras}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Por un tercero desconocido"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Por parte de tu administrador del perfil de trabajo"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Por <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
@@ -249,7 +250,7 @@
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Usa esta opción en la mayoría de los casos. Te permite realizar un seguimiento del progreso del informe, ingresar más detalles acerca del problema y tomar capturas de pantalla. Es posible que se omitan secciones menos usadas cuyos informes demoran más en completarse."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Informe completo"</string>
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"Usa esta opción para reducir al mínimo la interferencia del sistema cuando tu dispositivo no responde o funciona muy lento, o cuando necesitas todas las secciones del informe. No permite ingresar más detalles ni tomar capturas de pantalla adicionales."</string>
- <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Se tomará una captura de pantalla para el informe de errores en # segundo.}other{Se tomará una captura de pantalla para el informe de errores en # segundos.}}"</string>
+ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Se tomará una captura de pantalla para el informe de errores en # segundo.}many{Se tomará una captura de pantalla para el informe de errores en # segundos.}other{Se tomará una captura de pantalla para el informe de errores en # segundos.}}"</string>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Se tomó la captura de pantalla con el informe de errores"</string>
<string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"No se pudo tomar la captura de pantalla con el informe de errores"</string>
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo silencioso"</string>
@@ -605,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Se canceló la operación de huella dactilar."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"El usuario canceló la operación de huella dactilar."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiados intentos. Vuelve a intentarlo más tarde."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Utiliza el bloqueo de pantalla en su lugar."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Vuelve a intentarlo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se registraron huellas digitales."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas dactilares."</string>
@@ -635,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Consulta a un proveedor de reparaciones."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"No se puede crear modelo de rostro. Reinténtalo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Demasiado brillante. Prueba con menos iluminación."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Prueba con más iluminación"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Aleja el teléfono un poco más"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Acerca el teléfono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mueve el teléfono hacia arriba"</string>
@@ -1085,7 +1086,7 @@
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> desea activar la exploración táctil. Cuando esta función esté activada, podrás escuchar o ver descripciones del contenido seleccionado o usar gestos para interactuar con el dispositivo."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"Hace 1 mes."</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Anterior a 1 mes atrás"</string>
- <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Último # día}other{Últimos # días}}"</string>
+ <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Último # día}many{Últimos # días}other{Últimos # días}}"</string>
<string name="last_month" msgid="1528906781083518683">"Último mes"</string>
<string name="older" msgid="1645159827884647400">"Antiguos"</string>
<string name="preposition_for_date" msgid="2780767868832729599">"activado <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1112,14 +1113,14 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g> años"</string>
- <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}other{Hace # minutos}}"</string>
- <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}other{Hace # horas}}"</string>
- <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}other{Hace # días}}"</string>
- <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Hace # año}other{Hace # años}}"</string>
- <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}other{# minutos}}"</string>
- <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}other{# horas}}"</string>
- <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# día}other{# días}}"</string>
- <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# año}other{# años}}"</string>
+ <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}many{Hace # minutos}other{Hace # minutos}}"</string>
+ <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}many{Hace # horas}other{Hace # horas}}"</string>
+ <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}many{Hace # días}other{Hace # días}}"</string>
+ <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Hace # año}many{Hace # años}other{Hace # años}}"</string>
+ <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string>
+ <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string>
+ <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# día}many{# días}other{# días}}"</string>
+ <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# año}many{# años}other{# años}}"</string>
<string name="VideoView_error_title" msgid="5750686717225068016">"Problemas de video"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"No es posible transmitir este video al dispositivo."</string>
<string name="VideoView_error_text_unknown" msgid="7658683339707607138">"No se puede reproducir el video."</string>
@@ -1168,7 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"desactivado"</string>
<string name="selected" msgid="6614607926197755875">"seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
- <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Una estrella de {max}}other{# estrellas de {max}}}"</string>
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Una estrella de {max}}many{# estrellas de {max}}other{# estrellas de {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar la acción mediante"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
@@ -1507,7 +1508,7 @@
<string name="skip_button_label" msgid="3566599811326688389">"Omitir"</string>
<string name="no_matches" msgid="6472699895759164599">"Sin coincidencias"</string>
<string name="find_on_page" msgid="5400537367077438198">"Buscar en la página"</string>
- <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# coincidencia}other{# de {total}}}"</string>
+ <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# coincidencia}many{# de {total}}other{# de {total}}}"</string>
<string name="action_mode_done" msgid="2536182504764803222">"Listo"</string>
<string name="progress_erasing" msgid="6891435992721028004">"Borrando almacenamiento compartido…"</string>
<string name="share" msgid="4157615043345227321">"Compartir"</string>
@@ -1860,14 +1861,14 @@
<string name="data_saver_description" msgid="4995164271550590517">"Para reducir el uso de datos, el modo Ahorro de datos evita que algunas apps envíen y reciban datos en segundo plano. La app que estés usando podrá acceder a los datos, pero con menor frecuencia. De esta forma, por ejemplo, las imágenes no se mostrarán hasta que las presiones."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"¿Deseas activar Ahorro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
- <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Por un minuto (hasta {formattedTime})}other{Por # minutos (hasta {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durante 1 min (hasta {formattedTime})}other{Durante # min (hasta {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Durante 1 hora (hasta {formattedTime})}other{Durante # horas (hasta {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Durante 1 h (hasta {formattedTime})}other{Durante # h (hasta {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Durante un minuto}other{Durante # minutos}}"</string>
- <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Durante 1 min}other{Durante # min}}"</string>
- <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Durante 1 hora}other{Durante # horas}}"</string>
- <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Durante 1 h}other{Durante # h}}"</string>
+ <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Por un minuto (hasta {formattedTime})}many{Por # minutos (hasta {formattedTime})}other{Por # minutos (hasta {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durante 1 min (hasta {formattedTime})}many{Durante # min (hasta {formattedTime})}other{Durante # min (hasta {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Durante 1 hora (hasta {formattedTime})}many{Durante # horas (hasta {formattedTime})}other{Durante # horas (hasta {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Durante 1 h (hasta {formattedTime})}many{Durante # h (hasta {formattedTime})}other{Durante # h (hasta {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Durante un minuto}many{Durante # minutos}other{Durante # minutos}}"</string>
+ <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Durante 1 min}many{Durante # min}other{Durante # min}}"</string>
+ <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Durante 1 hora}many{Durante # horas}other{Durante # horas}}"</string>
+ <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Durante 1 h}many{Durante # h}other{Durante # h}}"</string>
<string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hasta la(s) <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Hasta la hora <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
@@ -2000,7 +2001,7 @@
<string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Guardar para Autocompletar"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"El contenido no puede autocompletarse"</string>
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"No hay sugerencias de Autocompletar"</string>
- <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Una sugerencia de autocompletar}other{# sugerencias de autocompletar}}"</string>
+ <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Una sugerencia de autocompletar}many{# sugerencias de autocompletar}other{# sugerencias de autocompletar}}"</string>
<string name="autofill_save_title" msgid="7719802414283739775">"¿Quieres guardar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_type" msgid="3002460014579799605">"¿Quieres guardar la <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_2types" msgid="3783270967447869241">"¿Quieres guardar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
@@ -2110,7 +2111,7 @@
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"Presentación <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"La conexión Bluetooth permanecerá activa durante el modo de avión"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"Cargando"</string>
- <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} y # archivo más}other{{file_name} y # archivos más}}"</string>
+ <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} y # archivo más}many{{file_name} y # archivos más}other{{file_name} y # archivos más}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"No hay personas recomendadas con quienes compartir"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Lista de apps"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Aunque no se le otorgó permiso de grabación a esta app, puede capturar audio con este dispositivo USB."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index f1b4bdc9f192..542351244ba9 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -46,6 +46,7 @@
<string name="needPuk2" msgid="7032612093451537186">"Introduce el código PUK2 para desbloquear la tarjeta SIM."</string>
<string name="enablePin" msgid="2543771964137091212">"Error, habilitar bloqueo de SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
+ <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
<item quantity="other">Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos para bloquear la tarjeta SIM.</item>
<item quantity="one">Te queda <xliff:g id="NUMBER_0">%d</xliff:g> intento para bloquear la tarjeta SIM.</item>
</plurals>
@@ -175,7 +176,7 @@
<string name="low_memory" product="watch" msgid="3479447988234030194">"El almacenamiento del reloj está lleno. Elimina algunos archivos para liberar espacio."</string>
<string name="low_memory" product="tv" msgid="6663680413790323318">"El espacio de almacenamiento de tu dispositivo Android TV está lleno. Elimina algunos archivos para liberar espacio."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"Se ha agotado el espacio de almacenamiento del teléfono. Elimina algunos archivos para liberar espacio."</string>
- <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoridad de certificación instalada}other{Autoridades de certificación instaladas}}"</string>
+ <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoridad de certificación instalada}many{Autoridades de certificación instaladas}other{Autoridades de certificación instaladas}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Por un tercero desconocido"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Por el administrador de tu perfil de trabajo"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Por <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
@@ -249,7 +250,7 @@
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Usa esta opción en la mayoría de los casos. Te permite realizar un seguimiento del progreso del informe, introducir más información sobre el problema y hacer capturas de pantalla. Es posible que se omitan algunas secciones menos utilizadas y que requieran más tiempo."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Informe completo"</string>
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"Utiliza esta opción para que la interferencia del sistema sea mínima cuando el dispositivo no responda o funcione demasiado lento, o bien cuando necesites todas las secciones del informe. No permite introducir más detalles ni hacer más capturas de pantalla."</string>
- <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{La captura de pantalla para el informe de errores se hará en # segundo.}other{La captura de pantalla para el informe de errores se hará en # segundos.}}"</string>
+ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{La captura de pantalla para el informe de errores se hará en # segundo.}many{La captura de pantalla para el informe de errores se hará en # segundos.}other{La captura de pantalla para el informe de errores se hará en # segundos.}}"</string>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Se ha hecho la captura de pantalla con el informe de errores"</string>
<string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"No se ha podido hacer la captura de pantalla con el informe de errores"</string>
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo Silencio"</string>
@@ -605,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Se ha cancelado la operación de huella digital."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"El usuario ha cancelado la operación de huella digital."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiados intentos. Vuelve a intentarlo más tarde."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Usa el bloqueo de pantalla."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Vuelve a intentarlo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se ha registrado ninguna huella digital."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string>
@@ -635,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visita un proveedor de reparaciones."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"No se puede crear tu modelo. Inténtalo de nuevo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Hay demasiada luz. Busca un sitio menos iluminado."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Prueba en un lugar con más luz"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Aleja el teléfono"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Acerca el teléfono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Sube el teléfono"</string>
@@ -1085,7 +1086,7 @@
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> quiere habilitar la exploración táctil. Cuando esta función esté activada, podrás escuchar o ver descripciones del contenido seleccionado o usar gestos para interactuar con el teléfono."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"Hace un mes"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Hace más de un mes"</string>
- <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Último día (#)}other{Últimos # días}}"</string>
+ <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Último día (#)}many{Últimos # días}other{Últimos # días}}"</string>
<string name="last_month" msgid="1528906781083518683">"El mes pasado"</string>
<string name="older" msgid="1645159827884647400">"Anterior"</string>
<string name="preposition_for_date" msgid="2780767868832729599">"<xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1112,14 +1113,14 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"en <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"en <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"en <xliff:g id="COUNT">%d</xliff:g>a"</string>
- <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}other{Hace # minutos}}"</string>
- <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}other{Hace # horas}}"</string>
- <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}other{Hace # días}}"</string>
- <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Hace # año}other{Hace # años}}"</string>
- <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}other{# minutos}}"</string>
- <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}other{# horas}}"</string>
- <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# día}other{# días}}"</string>
- <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# año}other{# años}}"</string>
+ <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Hace # minuto}many{Hace # minutos}other{Hace # minutos}}"</string>
+ <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Hace # hora}many{Hace # horas}other{Hace # horas}}"</string>
+ <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Hace # día}many{Hace # días}other{Hace # días}}"</string>
+ <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Hace # año}many{Hace # años}other{Hace # años}}"</string>
+ <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string>
+ <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string>
+ <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# día}many{# días}other{# días}}"</string>
+ <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# año}many{# años}other{# años}}"</string>
<string name="VideoView_error_title" msgid="5750686717225068016">"Incidencias con el vídeo"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Este vídeo no se puede transmitir al dispositivo."</string>
<string name="VideoView_error_text_unknown" msgid="7658683339707607138">"No se puede reproducir el vídeo."</string>
@@ -1168,7 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"no seleccionado"</string>
<string name="selected" msgid="6614607926197755875">"seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
- <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Una estrella de {max}}other{# estrellas de {max}}}"</string>
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Una estrella de {max}}many{# estrellas de {max}}other{# estrellas de {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar acción utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
@@ -1507,7 +1508,7 @@
<string name="skip_button_label" msgid="3566599811326688389">"Saltar"</string>
<string name="no_matches" msgid="6472699895759164599">"No hay coincidencias."</string>
<string name="find_on_page" msgid="5400537367077438198">"Buscar en la página"</string>
- <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# coincidencia}other{# de {total}}}"</string>
+ <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# coincidencia}many{# de {total}}other{# de {total}}}"</string>
<string name="action_mode_done" msgid="2536182504764803222">"Hecho"</string>
<string name="progress_erasing" msgid="6891435992721028004">"Borrando almacenamiento compartido…"</string>
<string name="share" msgid="4157615043345227321">"Compartir"</string>
@@ -1860,14 +1861,14 @@
<string name="data_saver_description" msgid="4995164271550590517">"Para reducir el uso de datos, Ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano. Si estás usando una aplicación, podrá acceder a datos, pero con menos frecuencia. Esto significa que es posible que, por ejemplo, algunas imágenes no se muestren hasta que las toques."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"¿Activar Ahorro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
- <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Durante un minuto (hasta las {formattedTime})}other{Durante # minutos (hasta las {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durante 1 min (hasta las {formattedTime})}other{Durante # min (hasta las {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Durante 1 hora (hasta las {formattedTime})}other{Durante # horas (hasta las {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Durante 1 h (hasta las {formattedTime})}other{Durante # h (hasta las {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Durante 1 minuto}other{Durante # minutos}}"</string>
- <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Durante 1 min}other{Durante # min}}"</string>
- <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Durante 1 hora}other{Durante # horas}}"</string>
- <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Durante 1 h}other{Durante # h}}"</string>
+ <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Durante un minuto (hasta las {formattedTime})}many{Durante # minutos (hasta las {formattedTime})}other{Durante # minutos (hasta las {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durante 1 min (hasta las {formattedTime})}many{Durante # min (hasta las {formattedTime})}other{Durante # min (hasta las {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Durante 1 hora (hasta las {formattedTime})}many{Durante # horas (hasta las {formattedTime})}other{Durante # horas (hasta las {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Durante 1 h (hasta las {formattedTime})}many{Durante # h (hasta las {formattedTime})}other{Durante # h (hasta las {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Durante 1 minuto}many{Durante # minutos}other{Durante # minutos}}"</string>
+ <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Durante 1 min}many{Durante # min}other{Durante # min}}"</string>
+ <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Durante 1 hora}many{Durante # horas}other{Durante # horas}}"</string>
+ <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Durante 1 h}many{Durante # h}other{Durante # h}}"</string>
<string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hasta <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
@@ -2000,7 +2001,7 @@
<string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Guardar en la función Autocompletar"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"El contenido no se puede autocompletar"</string>
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"No hay sugerencias de Autocompletar"</string>
- <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{1 sugerencia de Autocompletar}other{# sugerencias de Autocompletar}}"</string>
+ <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{1 sugerencia de Autocompletar}many{# sugerencias de Autocompletar}other{# sugerencias de Autocompletar}}"</string>
<string name="autofill_save_title" msgid="7719802414283739775">"¿Guardar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_type" msgid="3002460014579799605">"¿Guardar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_2types" msgid="3783270967447869241">"¿Guardar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
@@ -2110,7 +2111,7 @@
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"Presentación <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"El Bluetooth seguirá activado en el modo Avión"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"Cargando"</string>
- <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} y # archivo más}other{{file_name} y # archivos más}}"</string>
+ <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} y # archivo más}many{{file_name} y # archivos más}other{{file_name} y # archivos más}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"No hay sugerencias de personas con las que compartir"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Lista de aplicaciones"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Esta aplicación no tiene permiso para grabar, pero podría capturar audio con este dispositivo USB."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 8b8534866b67..bfb6aad7e729 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Sõrmejälje toiming tühistati."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Kasutaja tühistas sõrmejälje kasutamise."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Liiga palju katseid. Proovige hiljem uuesti."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Liiga palju katseid. Kasutage selle asemel ekraanilukku."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Proovige uuesti."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ühtegi sõrmejälge pole registreeritud."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Selles seadmes pole sõrmejäljeandurit."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Külastage remonditeenuse pakkujat."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Teie näomudelit ei saa luua. Proovige uuesti."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Liiga ere. Proovige hämaramat valgust."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Proovige parema valgustusega kohas"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Liigutage telefoni kaugemale"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Liigutage telefoni lähemale"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Liigutage telefoni kõrgemale"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 45cfaf10d697..4a56b59c8e9e 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Hatz-markaren eragiketa bertan behera utzi da."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Erabiltzaileak bertan behera utzi du hatz-marka bidezko eragiketa."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Saiakera gehiegi egin dituzu. Saiatu berriro geroago."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Saiakera gehiegi egin dira. Erabili pantailaren blokeoa."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Saiatu berriro."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ez da erregistratu hatz-markarik."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Gailu honek ez du hatz-marken sentsorerik."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Jarri harremanetan konponketak egiten dituen hornitzaile batekin."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Ezin da sortu aurpegi-eredua. Saiatu berriro."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Argi gehiegi dago. Joan toki ilunago batera."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Erabili argi gehiago"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Urrundu telefonoa"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Hurbildu telefonoa"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Igo telefonoa"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index df5bca5b2bb0..ea3b859195a8 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"عملکرد اثر انگشت لغو شد."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"کاربر عملیات اثر انگشت را لغو کرد"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"تلاش‌های زیادی انجام شده است. بعداً دوباره امتحان کنید."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تلاش‌های بیش‌ازحد. حالا از قفل صفحه استفاده کنید."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"دوباره امتحان کنید."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"اثر انگشتی ثبت نشده است."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"این دستگاه حسگر اثر انگشت ندارد."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"به ارائه‌دهنده خدمات تعمیر مراجعه کنید."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"مدل چهره ایجاد نشد. دوباره امتحان کنید."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"خیلی روشن است. روشنایی‌اش را ملایم‌تر کنید."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"نور را بیشتر کنید"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"تلفن را دورتر ببرید"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"تلفن را نزدیک‌تر بیاورید"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"تلفن را بالاتر ببرید"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 375d7afd4421..ff9e620ea2af 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Sormenjälkitoiminto peruutettiin."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Käyttäjä peruutti sormenjälkitoiminnon."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Liian monta yritystä. Yritä myöhemmin uudelleen."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Liian monta yritystä. Käytä näytön lukituksen avaustapaa."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Yritä uudelleen."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Sormenjälkiä ei ole otettu käyttöön."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Laitteessa ei ole sormenjälkitunnistinta."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Ota yhteys korjauspalveluun."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Kasvomallia ei voi luoda. Yritä uudelleen."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Liian kirkasta. Kokeile pehmeämpää valaistusta."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Kokeile kirkkaampaa valaistusta"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Vie puhelin kauemmas"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Tuo puhelin lähemmäs"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Siirrä puhelinta ylemmäs"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index baf6f6959a70..09ef190994ae 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -47,6 +47,7 @@
<string name="enablePin" msgid="2543771964137091212">"Opération infructueuse. Activez le verrouillage SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="one">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM soit verrouillée.</item>
+ <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
<item quantity="other">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM soit verrouillée.</item>
</plurals>
<string name="imei" msgid="2157082351232630390">"Code IIEM"</string>
@@ -175,7 +176,7 @@
<string name="low_memory" product="watch" msgid="3479447988234030194">"La mémoire de la montre est pleine. Supprimez des fichiers pour libérer de l\'espace."</string>
<string name="low_memory" product="tv" msgid="6663680413790323318">"L\'espace de stockage de l\'appareil Android TV est plein. Supprimez des fichiers pour libérer de l\'espace."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"La mémoire du téléphone est pleine. Veuillez supprimer des fichiers pour libérer de l\'espace."</string>
- <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autorité de certification installée}one{Autorité de certification installée}other{Autorités de certification installées}}"</string>
+ <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autorité de certification installée}one{Autorité de certification installée}many{Autorités de certification installées}other{Autorités de certification installées}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Par un tiers inconnu"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Par l\'administrateur de votre profil professionnel"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Par <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
@@ -249,7 +250,7 @@
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Utilisez cette option dans la plupart des circonstances. Elle vous permet de suivre la progression du rapport, d\'entrer plus d\'information sur le problème et d\'effectuer des saisies d\'écran. Certaines sections moins utilisées et dont le remplissage demande beaucoup de temps peuvent être omises."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Rapport complet"</string>
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"Utilisez cette option pour qu\'il y ait le moins d\'interférences système possible lorsque votre appareil ne répond pas ou qu\'il est trop lent, ou lorsque vous avez besoin de toutes les sections du rapport de bogue. Aucune capture d\'écran supplémentaire ne peut être capturée, et vous ne pouvez entrer aucune autre information."</string>
- <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Saisie d\'une capture d\'écran pour le rapport de bogue dans # seconde.}one{Saisie d\'une capture d\'écran pour le rapport de bogue dans # seconde.}other{Saisie d\'une capture d\'écran pour le rapport de bogue dans # secondes.}}"</string>
+ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Saisie d\'une capture d\'écran pour le rapport de bogue dans # seconde.}one{Saisie d\'une capture d\'écran pour le rapport de bogue dans # seconde.}many{Saisie d\'une capture d\'écran pour le rapport de bogue dans # secondes.}other{Saisie d\'une capture d\'écran pour le rapport de bogue dans # secondes.}}"</string>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Capture d\'écran prise avec le rapport de bogue"</string>
<string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Échec de la prise de capture d\'écran avec le rapport de bogue"</string>
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Mode silencieux"</string>
@@ -605,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale numérique annulée."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"L\'opération d\'empreinte digitale a été annulée par l\'utilisateur."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Trop de tentatives. Veuillez réessayer plus tard."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Réessayer."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string>
@@ -635,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Consultez un fournisseur de services de réparation."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Impossible de créer votre modèle facial. Réessayez."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Trop lumineux. Essayez un éclairage plus faible."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Essayez avec un éclairage plus fort"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Éloignez le téléphone"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Rapprochez le téléphone"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Tenez le téléphone plus haut"</string>
@@ -1085,7 +1086,7 @@
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> souhaite activer la fonctionnalité \"Explorer au toucher\". Lorsque celle-ci est activée, vous pouvez entendre ou voir les descriptions des éléments que vous sélectionnez, ou bien interagir avec le téléphone en effectuant certains gestes."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"Il y a 1 mois"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Il y a plus d\'un mois"</string>
- <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{# dernier jour}one{# dernier jour}other{# derniers jours}}"</string>
+ <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{# dernier jour}one{# dernier jour}many{# derniers jours}other{# derniers jours}}"</string>
<string name="last_month" msgid="1528906781083518683">"Le mois dernier"</string>
<string name="older" msgid="1645159827884647400">"Précédent"</string>
<string name="preposition_for_date" msgid="2780767868832729599">"le <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1112,14 +1113,14 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"dans <xliff:g id="COUNT">%d</xliff:g> a"</string>
- <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Il y a # minute}one{Il y a # minute}other{Il y a # minutes}}"</string>
- <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Il y a # heure}one{Il y a # heure}other{Il y a # heures}}"</string>
- <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Il y a # jour}one{Il y a # jour}other{Il y a # jours}}"</string>
- <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Il y a # an}one{Il y a # an}other{Il y a # ans}}"</string>
- <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minute}one{# minute}other{# minutes}}"</string>
- <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# heure}one{# heure}other{# heures}}"</string>
- <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# jour}one{# jour}other{# jours}}"</string>
- <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# an}one{# an}other{# ans}}"</string>
+ <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Il y a # minute}one{Il y a # minute}many{Il y a # minutes}other{Il y a # minutes}}"</string>
+ <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Il y a # heure}one{Il y a # heure}many{Il y a # heures}other{Il y a # heures}}"</string>
+ <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Il y a # jour}one{Il y a # jour}many{Il y a # jours}other{Il y a # jours}}"</string>
+ <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Il y a # an}one{Il y a # an}many{Il y a # ans}other{Il y a # ans}}"</string>
+ <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minute}one{# minute}many{# minutes}other{# minutes}}"</string>
+ <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# heure}one{# heure}many{# heures}other{# heures}}"</string>
+ <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# jour}one{# jour}many{# jours}other{# jours}}"</string>
+ <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# an}one{# an}many{# ans}other{# ans}}"</string>
<string name="VideoView_error_title" msgid="5750686717225068016">"Problème vidéo"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Impossible de lire cette vidéo en continu sur cet appareil."</string>
<string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Impossible de lire la vidéo."</string>
@@ -1168,7 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"non coché"</string>
<string name="selected" msgid="6614607926197755875">"sélectionné"</string>
<string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
- <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Une étoile sur {max}}one{# étoile sur {max}}other{# étoiles sur {max}}}"</string>
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Une étoile sur {max}}one{# étoile sur {max}}many{# d\'étoiles sur {max}}other{# étoiles sur {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"en cours"</string>
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Continuer avec %1$s"</string>
@@ -1507,7 +1508,7 @@
<string name="skip_button_label" msgid="3566599811326688389">"Ignorer"</string>
<string name="no_matches" msgid="6472699895759164599">"Aucune partie"</string>
<string name="find_on_page" msgid="5400537367077438198">"Rechercher sur la page"</string>
- <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondance}one{# sur {total}}other{# sur {total}}}"</string>
+ <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondance}one{# sur {total}}many{# sur {total}}other{# sur {total}}}"</string>
<string name="action_mode_done" msgid="2536182504764803222">"Terminé"</string>
<string name="progress_erasing" msgid="6891435992721028004">"Effacement du stockage partagé en cours…"</string>
<string name="share" msgid="4157615043345227321">"Partager"</string>
@@ -1860,14 +1861,14 @@
<string name="data_saver_description" msgid="4995164271550590517">"Pour aider à diminuer l\'utilisation des données, la fonctionnalité Économiseur de données empêche certaines applications d\'envoyer ou de recevoir des données en arrière-plan. Une application que vous utilisez actuellement peut accéder à des données, mais peut le faire moins souvent. Cela peut signifier, par exemple, que les images ne s\'affichent pas jusqu\'à ce que vous les touchiez."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string>
- <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Pendant une minute (jusqu\'à {formattedTime})}one{Pendant # minute (jusqu\'à {formattedTime})}other{Pendant # minutes (jusqu\'à {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Pendant 1 m (jusqu\'à {formattedTime})}one{Pendant # m (jusqu\'à {formattedTime})}other{Pendant # m (jusqu\'à {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Pendant 1 heure (jusqu\'à {formattedTime})}one{Pendant # heure (jusqu\'à {formattedTime})}other{Pendant # heures (jusqu\'à {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Pendant 1 h (jusqu\'à {formattedTime})}one{Pendant # h (jusqu\'à {formattedTime})}other{Pendant # h (jusqu\'à {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Pendant une minute}one{Pendant # minute}other{Pendant # minutes}}"</string>
- <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Pendant 1 m}one{Pendant # m}other{Pendant # m}}"</string>
- <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Pendant 1 heure}one{Pendant # heure}other{Pendant # heures}}"</string>
- <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Pendant 1 h}one{Pendant # h}other{Pendant # h}}"</string>
+ <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Pendant une minute (jusqu\'à {formattedTime})}one{Pendant # minute (jusqu\'à {formattedTime})}many{Pendant # minutes (jusqu\'à {formattedTime})}other{Pendant # minutes (jusqu\'à {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Pendant 1 m (jusqu\'à {formattedTime})}one{Pendant # m (jusqu\'à {formattedTime})}many{Pendant # m (jusqu\'à {formattedTime})}other{Pendant # m (jusqu\'à {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Pendant 1 heure (jusqu\'à {formattedTime})}one{Pendant # heure (jusqu\'à {formattedTime})}many{Pendant # heures (jusqu\'à {formattedTime})}other{Pendant # heures (jusqu\'à {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Pendant 1 h (jusqu\'à {formattedTime})}one{Pendant # h (jusqu\'à {formattedTime})}many{Pendant # h (jusqu\'à {formattedTime})}other{Pendant # h (jusqu\'à {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Pendant une minute}one{Pendant # minute}many{Pendant # minutes}other{Pendant # minutes}}"</string>
+ <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Pendant 1 m}one{Pendant # m}many{Pendant # m}other{Pendant # m}}"</string>
+ <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Pendant 1 heure}one{Pendant # heure}many{Pendant # heures}other{Pendant # heures}}"</string>
+ <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Pendant 1 h}one{Pendant # h}many{Pendant # h}other{Pendant # h}}"</string>
<string name="zen_mode_until_next_day" msgid="1403042784161725038">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
@@ -2000,7 +2001,7 @@
<string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Enregistrer pour le remplissage automatique"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Le contenu ne peut pas être entré automatiquement"</string>
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Aucune suggestion de remplissage automatique"</string>
- <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Une suggestion de remplissage automatique}one{# suggestion de remplissage automatique}other{# suggestions de remplissage automatique}}"</string>
+ <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Une suggestion de remplissage automatique}one{# suggestion de remplissage automatique}many{# suggestions de remplissage automatique}other{# suggestions de remplissage automatique}}"</string>
<string name="autofill_save_title" msgid="7719802414283739775">"Enregistrer sous "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_type" msgid="3002460014579799605">"Enregistrer <xliff:g id="TYPE">%1$s</xliff:g> sous "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> sous "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
@@ -2110,7 +2111,7 @@
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"Présentation <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"Le Bluetooth restera activé en mode Avion"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"Chargement en cours…"</string>
- <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # fichier}one{{file_name} + # fichier}other{{file_name} + # fichiers}}"</string>
+ <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # fichier}one{{file_name} + # fichier}many{{file_name} + # fichiers}other{{file_name} + # fichiers}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Aucune recommandation de personnes avec lesquelles effectuer un partage"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Liste des applications"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Cette application n\'a pas été autorisée à effectuer des enregistrements, mais elle pourrait capturer du contenu audio par l\'intermédiaire de cet appareil USB."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 95d06f44854f..487f29096c03 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -47,6 +47,7 @@
<string name="enablePin" msgid="2543771964137091212">"Échec de l\'opération. Veuillez activer le verrouillage de la carte SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="one">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM ne soit verrouillée.</item>
+ <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
<item quantity="other">Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM ne soit verrouillée.</item>
</plurals>
<string name="imei" msgid="2157082351232630390">"Code IMEI"</string>
@@ -175,7 +176,7 @@
<string name="low_memory" product="watch" msgid="3479447988234030194">"La mémoire de la montre est saturée. Veuillez supprimer des fichiers pour libérer de l\'espace."</string>
<string name="low_memory" product="tv" msgid="6663680413790323318">"L\'espace de stockage de l\'appareil Android TV est saturé. Supprimez certains fichiers pour libérer de l\'espace."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"La mémoire du téléphone est pleine. Veuillez supprimer des fichiers pour libérer de l\'espace."</string>
- <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autorité de certification installée}one{Autorité de certification installée}other{Autorités de certification installées}}"</string>
+ <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autorité de certification installée}one{Autorité de certification installée}many{Autorités de certification installées}other{Autorités de certification installées}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Par un tiers inconnu"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Par l\'administrateur de votre profil professionnel"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Par <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
@@ -249,7 +250,7 @@
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Utilisez cette option dans la plupart des circonstances. Elle vous permet de suivre la progression du rapport, de saisir plus d\'informations sur le problème et d\'effectuer des captures d\'écran. Certaines sections moins utilisées et dont le remplissage demande beaucoup de temps peuvent être omises."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Rapport complet"</string>
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"Utilisez cette option pour qu\'il y ait le moins d\'interférences système possible lorsque votre appareil ne répond pas ou qu\'il est trop lent, ou lorsque vous avez besoin de toutes les sections du rapport de bug. Aucune capture d\'écran supplémentaire ne peut être prise, et vous ne pouvez saisir aucune autre information."</string>
- <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Capture d\'écran pour le rapport de bug dans # seconde.}one{Capture d\'écran pour le rapport de bug dans # seconde.}other{Capture d\'écran pour le rapport de bug dans # secondes.}}"</string>
+ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Capture d\'écran pour le rapport de bug dans # seconde.}one{Capture d\'écran pour le rapport de bug dans # seconde.}many{Capture d\'écran pour le rapport de bug dans # secondes.}other{Capture d\'écran pour le rapport de bug dans # secondes.}}"</string>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Capture d\'écran avec rapport de bug effectuée"</string>
<string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Échec de la capture d\'écran avec le rapport de bug"</string>
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Mode silencieux"</string>
@@ -605,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale annulée."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Opération d\'authentification par empreinte digitale annulée par l\'utilisateur."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Trop de tentatives. Veuillez réessayer plus tard."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Veuillez réessayer."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil."</string>
@@ -635,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Contactez un réparateur."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Impossible de créer l\'empreinte faciale. Réessayez."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Trop lumineux. Essayez de baisser la lumière."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Essayez un éclairage plus lumineux"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Éloignez le téléphone."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Rapprochez le téléphone"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Déplacez le téléphone vers le haut"</string>
@@ -1085,7 +1086,7 @@
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> souhaite activer la fonctionnalité \"Explorer au toucher\". Lorsque celle-ci est activée, vous pouvez entendre ou voir les descriptions des éléments que vous sélectionnez, ou bien interagir avec le téléphone en effectuant certains gestes."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"Il y a 1 mois"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Il y a plus d\'un mois"</string>
- <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Dernier jour (#)}one{Dernier jour (#)}other{# derniers jours}}"</string>
+ <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Dernier jour (#)}one{Dernier jour (#)}many{# derniers jours}other{# derniers jours}}"</string>
<string name="last_month" msgid="1528906781083518683">"Le mois dernier"</string>
<string name="older" msgid="1645159827884647400">"Préc."</string>
<string name="preposition_for_date" msgid="2780767868832729599">"le <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1112,14 +1113,14 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"dans <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"dans <xliff:g id="COUNT">%d</xliff:g> j"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"dans <xliff:g id="COUNT">%d</xliff:g> an"</string>
- <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Il y a # minute}one{Il y a # minute}other{Il y a # minutes}}"</string>
- <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Il y a # heure}one{Il y a # heure}other{Il y a # heures}}"</string>
- <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Il y a # jour}one{Il y a # jour}other{Il y a # jours}}"</string>
- <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Il y a # an}one{Il y a # an}other{Il y a # ans}}"</string>
- <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minute}one{# minute}other{# minutes}}"</string>
- <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# heure}one{# heure}other{# heures}}"</string>
- <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# jour}one{# jour}other{# jours}}"</string>
- <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# an}one{# an}other{# ans}}"</string>
+ <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Il y a # minute}one{Il y a # minute}many{Il y a # minutes}other{Il y a # minutes}}"</string>
+ <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Il y a # heure}one{Il y a # heure}many{Il y a # heures}other{Il y a # heures}}"</string>
+ <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Il y a # jour}one{Il y a # jour}many{Il y a # jours}other{Il y a # jours}}"</string>
+ <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Il y a # an}one{Il y a # an}many{Il y a # ans}other{Il y a # ans}}"</string>
+ <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minute}one{# minute}many{# minutes}other{# minutes}}"</string>
+ <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# heure}one{# heure}many{# heures}other{# heures}}"</string>
+ <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# jour}one{# jour}many{# jours}other{# jours}}"</string>
+ <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# an}one{# an}many{# ans}other{# ans}}"</string>
<string name="VideoView_error_title" msgid="5750686717225068016">"Problème vidéo"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Impossible de lire cette vidéo en streaming sur cet appareil."</string>
<string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Impossible de lire la vidéo."</string>
@@ -1168,7 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"désactivé"</string>
<string name="selected" msgid="6614607926197755875">"sélectionné"</string>
<string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
- <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 étoile sur {max}}one{# étoile sur {max}}other{# étoiles sur {max}}}"</string>
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 étoile sur {max}}one{# étoile sur {max}}many{# étoiles sur {max}}other{# étoiles sur {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"en cours"</string>
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Terminer l\'action avec %1$s"</string>
@@ -1507,7 +1508,7 @@
<string name="skip_button_label" msgid="3566599811326688389">"Ignorer"</string>
<string name="no_matches" msgid="6472699895759164599">"Aucune correspondance"</string>
<string name="find_on_page" msgid="5400537367077438198">"Rechercher sur la page"</string>
- <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondance}one{# sur {total}}other{# sur {total}}}"</string>
+ <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondance}one{# sur {total}}many{# sur {total}}other{# sur {total}}}"</string>
<string name="action_mode_done" msgid="2536182504764803222">"OK"</string>
<string name="progress_erasing" msgid="6891435992721028004">"Suppression de l\'espace de stockage partagé…"</string>
<string name="share" msgid="4157615043345227321">"Partager"</string>
@@ -1860,14 +1861,14 @@
<string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation des données, l\'Économiseur de données empêche certaines applis d\'envoyer ou de recevoir des données en arrière-plan. Les applis que vous utiliserez pourront toujours accéder aux données, mais le feront moins fréquemment. Par exemple, les images pourront ne pas s\'afficher tant que vous n\'aurez pas appuyé dessus."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'Économiseur de données ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string>
- <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Pendant 1 minute (jusqu\'à {formattedTime})}one{Pendant # minute (jusqu\'à {formattedTime})}other{Pendant # minutes (jusqu\'à {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Pendant 1 min (jusqu\'à {formattedTime})}one{Pendant # min (jusqu\'à {formattedTime})}other{Pendant # min (jusqu\'à {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Pendant 1 heure (jusqu\'à {formattedTime})}one{Pendant # heure (jusqu\'à {formattedTime})}other{Pendant # heures (jusqu\'à {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Pendant 1 h (jusqu\'à {formattedTime})}one{Pendant # h (jusqu\'à {formattedTime})}other{Pendant # h (jusqu\'à {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Pendant 1 minute}one{Pendant # minute}other{Pendant # minutes}}"</string>
- <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Pendant 1 min}one{Pendant # min}other{Pendant # min}}"</string>
- <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Pendant 1 heure}one{Pendant # heure}other{Pendant # heures}}"</string>
- <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Pendant 1 h}one{Pendant # h}other{Pendant # h}}"</string>
+ <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Pendant 1 minute (jusqu\'à {formattedTime})}one{Pendant # minute (jusqu\'à {formattedTime})}many{Pendant # minutes (jusqu\'à {formattedTime})}other{Pendant # minutes (jusqu\'à {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Pendant 1 min (jusqu\'à {formattedTime})}one{Pendant # min (jusqu\'à {formattedTime})}many{Pendant # min (jusqu\'à {formattedTime})}other{Pendant # min (jusqu\'à {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Pendant 1 heure (jusqu\'à {formattedTime})}one{Pendant # heure (jusqu\'à {formattedTime})}many{Pendant # heures (jusqu\'à {formattedTime})}other{Pendant # heures (jusqu\'à {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Pendant 1 h (jusqu\'à {formattedTime})}one{Pendant # h (jusqu\'à {formattedTime})}many{Pendant # h (jusqu\'à {formattedTime})}other{Pendant # h (jusqu\'à {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Pendant 1 minute}one{Pendant # minute}many{Pendant # minutes}other{Pendant # minutes}}"</string>
+ <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Pendant 1 min}one{Pendant # min}many{Pendant # min}other{Pendant # min}}"</string>
+ <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Pendant 1 heure}one{Pendant # heure}many{Pendant # heures}other{Pendant # heures}}"</string>
+ <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Pendant 1 h}one{Pendant # h}many{Pendant # h}other{Pendant # h}}"</string>
<string name="zen_mode_until_next_day" msgid="1403042784161725038">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
@@ -2000,7 +2001,7 @@
<string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Enregistrer pour la saisie automatique"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Le contenu ne peut pas être saisi automatiquement"</string>
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Aucune suggestion de saisie automatique"</string>
- <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{1 suggestion de saisie automatique}one{# suggestion de saisie automatique}other{# suggestions de saisie automatique}}"</string>
+ <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{1 suggestion de saisie automatique}one{# suggestion de saisie automatique}many{# suggestions de saisie automatique}other{# suggestions de saisie automatique}}"</string>
<string name="autofill_save_title" msgid="7719802414283739775">"Enregistrer dans "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ?"</string>
<string name="autofill_save_title_with_type" msgid="3002460014579799605">"Enregistrer la <xliff:g id="TYPE">%1$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ?"</string>
<string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ?"</string>
@@ -2110,7 +2111,7 @@
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"Présentation <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"Le Bluetooth restera activé en mode Avion"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"Chargement…"</string>
- <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # fichier}one{{file_name} + # fichier}other{{file_name} + # fichiers}}"</string>
+ <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # fichier}one{{file_name} + # fichier}many{{file_name} + # fichiers}other{{file_name} + # fichiers}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Aucune recommandation de personnes avec lesquelles effectuer un partage"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Liste des applications"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Cette application n\'a pas reçu l\'autorisation d\'enregistrer des contenus audio, mais peut le faire via ce périphérique USB."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 2dd092d37a6c..111392691472 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Cancelouse a operación da impresión dixital."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"O usuario cancelou a operación da impresión dixital."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiados intentos. Téntao de novo máis tarde."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Mellor usa o bloqueo de pantalla."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Téntao de novo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Non se rexistraron impresións dixitais."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo non ten sensor de impresión dixital."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visita un provedor de reparacións."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Non se puido crear o modelo facial. Téntao de novo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Hai demasiada iluminación. Proba cunha máis suave."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Proba con máis luz"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Afasta o teléfono"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Achega o teléfono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Sube o teléfono"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index d17030282bcb..eec52724e5ee 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ફિંગરપ્રિન્ટ ઓપરેશન રદ કર્યું."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ફિંગરપ્રિન્ટ ચકાસવાની પ્રક્રિયા વપરાશકર્તાએ રદ કરી."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ઘણા બધા પ્રયત્નો. પછીથી ફરી પ્રયાસ કરો."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ઘણા બધા પ્રયાસો. વિકલ્પ તરીકે સ્ક્રીન લૉકનો ઉપયોગ કરો."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"કોઈ ફિંગરપ્રિન્ટની નોંધણી કરવામાં આવી નથી."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"રિપેર કરવાની સેવા આપતા પ્રદાતાની મુલાકાત લો."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"તમારા ચહેરાનું મૉડલ ન બનાવી શકાય. ફરી પ્રયાસ કરો."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"અતિશય પ્રકાશિત. થોડો હળવો પ્રકાશ અજમાવી જુઓ."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"વધુ પ્રકાશિત લાઇટિંગ અજમાવો"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ફોનને વધુ દૂર લઈ જાઓ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ફોનને વધુ નજીક લાવો"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ફોનને વધુ ઊંચે લઈ જાઓ"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index d97049866d78..780616fb5646 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -302,7 +302,7 @@
<string name="permgroupdesc_sms" msgid="5726462398070064542">"मैसेज (एसएमएस) भेजें और देखें"</string>
<string name="permgrouplab_storage" msgid="17339216290379241">"फ़ाइलें"</string>
<string name="permgroupdesc_storage" msgid="5378659041354582769">"अपने डिवाइस में मौजूद फ़ाइलों का ऐक्सेस दें"</string>
- <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"संगीत और ऑडियो के ऐक्सेस"</string>
+ <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"संगीत और ऑडियो"</string>
<string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"आपके डिवाइस पर संगीत और ऑडियो को ऐक्सेस करने की अनुमति"</string>
<string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"फ़ोटो और वीडियो के ऐक्सेस"</string>
<string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"आपके डिवाइस पर फ़ोटो और वीडियो को ऐक्सेस करने की अनुमति"</string>
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"फ़िंगरप्रिंट ऑपरेशन रोक दिया गया."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि की कार्रवाई रद्द कर दी है."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"बहुत ज़्यादा प्रयास कर लिए गए हैं. बाद में फिर से प्रयास करें."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"इससे ज़्यादा बार कोशिश नहीं की जा सकती. इसके बजाय, स्क्रीन लॉक का इस्तेमाल करें."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"फिर से कोशिश करें."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"फ़िंगरप्रिंट सेंसर को रिपेयर करने की सेवा देने वाली कंपनी से संपर्क करें."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"चेहरे का माॅडल नहीं बन सका. फिर से कोशिश करें."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"बहुत रोशनी है. हल्की रोशनी आज़माएं."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"बेहतर रोशनी में कोशिश करें"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"फ़ोन को दूर ले जाएं"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"फ़ोन को नज़दीक लाएं"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"फ़ोन को थोड़ा और ऊपर ले जाएं"</string>
@@ -1855,8 +1855,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"आपके व्यवस्थापक ने अपडेट किया है"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"आपके व्यवस्थापक ने हटा दिया है"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ठीक है"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, यह बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, कुछ खास सुविधाओं, और कुछ खास तरह के इंटरनेट कनेक्शन इस्तेमाल करने से डिवाइस को रोकता है या इन्हें बंद कर देता है."</string>
- <string name="battery_saver_description" msgid="8518809702138617167">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, यह बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, कुछ खास सुविधाओं, और कुछ खास तरह के इंटरनेट कनेक्शन इस्तेमाल करने से डिवाइस को रोकता है या इन्हें बंद कर देता है."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, इस मोड में बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और कुछ खास सुविधाएं कम या बंद हो जाती हैं. कुछ इंटरनेट कनेक्शन भी पूरी तरह काम नहीं करते."</string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, इस मोड में बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और कुछ सुविधाएं सीमित या बंद हो जाती हैं. कुछ इंटरनेट कनेक्शन भी पूरी तरह काम नहीं करते."</string>
<string name="data_saver_description" msgid="4995164271550590517">"डेटा खर्च को कम करने के लिए, डेटा बचाने की सेटिंग कुछ ऐप्लिकेशन को बैकग्राउंड में डेटा भेजने या डेटा पाने से रोकती है. फ़िलहाल, जिस ऐप्लिकेशन का इस्तेमाल किया जा रहा है वह डेटा ऐक्सेस कर सकता है, लेकिन ऐसा कभी-कभी ही हो पाएगा. उदाहरण के लिए, इमेज तब तक दिखाई नहीं देंगी, जब तक उन पर टैप नहीं किया जाएगा."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा बचाने की सेटिंग चालू करें?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"चालू करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 1a0b1c6f02ff..fbc47e2b6787 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -606,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja otiska prsta otkazana je."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Radnju s otiskom prsta otkazao je korisnik."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Umjesto toga upotrijebite zaključavanje zaslona."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Pokušajte ponovo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registriran nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor otiska prsta."</string>
@@ -636,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Posjetite davatelja usluga popravaka."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Izrada modela lica nije uspjela. Pokušajte ponovo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Presvijetlo je. Pokušajte sa slabijim svjetlom."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Pokušajte s jačim osvjetljenjem"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Udaljite telefon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Približite telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Pomaknite telefon prema gore"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 24dc9b6eb35a..897904dab7a4 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Ujjlenyomattal kapcsolatos művelet megszakítva"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Az ujjlenyomattal kapcsolatos műveletet a felhasználó megszakította."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Túl sok próbálkozás. Próbálja újra később."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Túl sok próbálkozás. Használja inkább a képernyőzárat."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Próbálkozzon újra."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nincsenek regisztrált ujjlenyomatok."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Keresse fel a szervizt."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nem lehet létrehozni az arcmodellt. Próbálja újra."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Túl világos. Próbálja kevésbé erős világítással."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Próbálja jobb megvilágítás mellett"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Tartsa távolabb a telefont"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Tartsa közelebb a telefont"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Emelje magasabbra a telefont"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index b9206c3f4947..fc8c3e1d464e 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Իսկորոշումը մատնահետքի միջոցով չեղարկվեց:"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Մատնահետքով նույնականացման գործողությունը չեղարկվել է օգտատիրոջ կողմից:"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Չափից շատ փորձ եք կատարել: Փորձեք նորից քիչ հետո:"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Փորձեք նորից:"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Գրանցված մատնահետք չկա:"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Այս սարքը չունի մատնահետքերի սկաներ։"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Այցելեք սպասարկման կենտրոն։"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Չհաջողվեց ստեղծել ձեր դեմքի մոդելը։ Նորից փորձեք։"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Շատ լուսավոր է։ Փորձեք ավելի թեթև լուսավորություն։"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Ավելի պայծառ դարձրեք լուսավորությունը"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Փոքր-ինչ հեռու պահեք հեռախոսը"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Մոտեցրեք հեռախոսը"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Բարձրացրեք հեռախոսը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 30f98fd096f9..af1b0bdf8fba 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operasi sidik jari dibatalkan."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operasi sidik jari dibatalkan oleh pengguna."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Terlalu banyak upaya. Coba lagi nanti."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Terlalu banyak upaya gagal. Gunakan kunci layar."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Coba lagi."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tidak ada sidik jari yang terdaftar."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Perangkat ini tidak memiliki sensor sidik jari."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Kunjungi penyedia reparasi."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Tidak dapat membuat model wajah Anda. Coba lagi."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Terlalu terang. Coba cahaya yang lebih lembut."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Coba pencahayaan yang lebih cerah"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Jauhkan ponsel"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Dekatkan ponsel"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Gerakkan ponsel ke atas"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 20bf9bfc319f..69e3127410cf 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Hætt við fingrafarsaðgerð."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Notandi hætti við að nota fingrafar."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Of margar tilraunir. Reyndu aftur síðar."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Of margar tilraunir. Notaðu skjálás í staðinn."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Reyndu aftur."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Engin fingraför hafa verið skráð."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Þetta tæki er ekki með fingrafaralesara."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Þú verður að fara á verkstæði."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Ekki tekst að búa til andlitslíkan. Reyndu aftur."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Of bjart. Prófaðu mýkri lýsingu."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Prófaðu sterkari lýsingu"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Færðu símann lengra frá"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Færðu símann nær"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Færðu símann hærra"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 330100c53b67..bf18581f407f 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -46,6 +46,7 @@
<string name="needPuk2" msgid="7032612093451537186">"Digita il PUK2 per sbloccare la SIM."</string>
<string name="enablePin" msgid="2543771964137091212">"Operazione non riuscita; attiva blocco SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
+ <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
<item quantity="other">Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM venga bloccata.</item>
<item quantity="one">Hai ancora <xliff:g id="NUMBER_0">%d</xliff:g> tentativo a disposizione prima che la SIM venga bloccata.</item>
</plurals>
@@ -175,7 +176,7 @@
<string name="low_memory" product="watch" msgid="3479447988234030194">"La memoria dell\'orologio è piena. Elimina alcuni file per liberare spazio."</string>
<string name="low_memory" product="tv" msgid="6663680413790323318">"Lo spazio di archiviazione del dispositivo Android TV è pieno. Elimina alcuni file per liberare spazio."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"Spazio di archiviazione del telefono esaurito. Elimina alcuni file per liberare spazio."</string>
- <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autorità di certificazione installata}other{Autorità di certificazione installate}}"</string>
+ <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autorità di certificazione installata}many{Autorità di certificazione installate}other{Autorità di certificazione installate}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Da una terza parte sconosciuta"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Dall\'amministratore del tuo profilo di lavoro"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Da <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
@@ -249,7 +250,7 @@
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Utilizza questa opzione nella maggior parte dei casi. Ti consente di monitorare l\'avanzamento della segnalazione, di inserire maggiori dettagli relativi al problema e di acquisire screenshot. Potrebbero essere omesse alcune sezioni meno utilizzate il cui inserimento nella segnalazione richiede molto tempo."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Report completo"</string>
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"Utilizza questa opzione per ridurre al minimo l\'interferenza di sistema quando il dispositivo non risponde, è troppo lento oppure quando ti servono tutte le sezioni della segnalazione. Non puoi inserire altri dettagli o acquisire altri screenshot."</string>
- <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Lo screenshot per la segnalazione di bug verrà acquisito tra # secondo.}other{Lo screenshot per la segnalazione di bug verrà acquisito tra # secondi.}}"</string>
+ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Lo screenshot per la segnalazione di bug verrà acquisito tra # secondo.}many{Lo screenshot per la segnalazione di bug verrà acquisito tra # secondi.}other{Lo screenshot per la segnalazione di bug verrà acquisito tra # secondi.}}"</string>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Screenshot con segnalazione di bug effettuato correttamente"</string>
<string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Impossibile acquisire screenshot con segnalazione di bug"</string>
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modalità silenziosa"</string>
@@ -605,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operazione associata all\'impronta annullata."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operazione di autenticazione dell\'impronta annullata dall\'utente."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Troppi tentativi. Riprova più tardi."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Troppi tentativi. Usa il blocco schermo."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Riprova."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nessuna impronta digitale registrata."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Questo dispositivo non dispone di sensore di impronte."</string>
@@ -635,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Contatta un fornitore di servizi di riparazione."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Impossibile creare il modello del volto. Riprova."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Troppa luce. Prova con una luce più soft."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Prova con più luce"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Allontana il telefono"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Avvicina il telefono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Sposta il telefono più in alto"</string>
@@ -1085,7 +1086,7 @@
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vuole attivare la funzione Esplora al tocco. Quando la funzione Esplora al tocco è attiva, puoi ascoltare o visualizzare le descrizioni di ciò che stai toccando oppure interagire con il telefono tramite gesti."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"1 mese fa"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Oltre 1 mese fa"</string>
- <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Ultimo giorno}other{Ultimi # giorni}}"</string>
+ <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Ultimo giorno}many{Ultimi # giorni}other{Ultimi # giorni}}"</string>
<string name="last_month" msgid="1528906781083518683">"Ultimo mese"</string>
<string name="older" msgid="1645159827884647400">"Precedente"</string>
<string name="preposition_for_date" msgid="2780767868832729599">"<xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1112,14 +1113,14 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"tra <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"tra <xliff:g id="COUNT">%d</xliff:g> g"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"tra <xliff:g id="COUNT">%d</xliff:g> a"</string>
- <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto fa}other{# minuti fa}}"</string>
- <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ora fa}other{# ore fa}}"</string>
- <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# giorno fa}other{# giorni fa}}"</string>
- <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{# anno fa}other{# anni fa}}"</string>
- <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}other{# minuti}}"</string>
- <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# ora}other{# ore}}"</string>
- <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# giorno}other{# giorni}}"</string>
- <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# anno}other{# anni}}"</string>
+ <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto fa}many{# minuti fa}other{# minuti fa}}"</string>
+ <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# ora fa}many{# ore fa}other{# ore fa}}"</string>
+ <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# giorno fa}many{# giorni fa}other{# giorni fa}}"</string>
+ <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{# anno fa}many{# anni fa}other{# anni fa}}"</string>
+ <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}many{# minuti}other{# minuti}}"</string>
+ <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# ora}many{# ore}other{# ore}}"</string>
+ <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# giorno}many{# giorni}other{# giorni}}"</string>
+ <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# anno}many{# anni}other{# anni}}"</string>
<string name="VideoView_error_title" msgid="5750686717225068016">"Problemi video"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Questo video non è valido per lo streaming su questo dispositivo."</string>
<string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Impossibile riprodurre il video."</string>
@@ -1168,7 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"deselezionato"</string>
<string name="selected" msgid="6614607926197755875">"selezionato"</string>
<string name="not_selected" msgid="410652016565864475">"non selezionato"</string>
- <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Una stella su {max}}other{# stelle su {max}}}"</string>
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Una stella su {max}}many{# stelle su {max}}other{# stelle su {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"In corso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'azione con"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completamento azione con %1$s"</string>
@@ -1507,7 +1508,7 @@
<string name="skip_button_label" msgid="3566599811326688389">"Salta"</string>
<string name="no_matches" msgid="6472699895759164599">"Nessuna corrispondenza"</string>
<string name="find_on_page" msgid="5400537367077438198">"Trova nella pagina"</string>
- <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# corrispondenza}other{# di {total}}}"</string>
+ <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# corrispondenza}many{# di {total}}other{# di {total}}}"</string>
<string name="action_mode_done" msgid="2536182504764803222">"Fine"</string>
<string name="progress_erasing" msgid="6891435992721028004">"Cancellazione archivio condiviso…"</string>
<string name="share" msgid="4157615043345227321">"Condividi"</string>
@@ -1860,14 +1861,14 @@
<string name="data_saver_description" msgid="4995164271550590517">"Per contribuire a ridurre l\'utilizzo dei dati, la funzionalità Risparmio dati impedisce ad alcune app di inviare o ricevere dati in background. Un\'app in uso può accedere ai dati, ma potrebbe farlo con meno frequenza. Per esempio, è possibile che le immagini non vengano visualizzate finché non le tocchi."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Attivare Risparmio dati?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Attiva"</string>
- <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Per un minuto (fino alle ore {formattedTime})}other{Per # minuti (fino alle ore {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Per 1 min (fino alle ore {formattedTime})}other{Per # min (fino alle ore {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Per 1 ora (fino alle ore {formattedTime})}other{Per # ore (fino alle ore {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Per 1 h (fino alle ore {formattedTime})}other{Per # h (fino alle ore {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Per un minuto}other{Per # minuti}}"</string>
- <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Per 1 min}other{Per # min}}"</string>
- <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Per 1 ora}other{Per # ore}}"</string>
- <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Per 1 h}other{Per # h}}"</string>
+ <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Per un minuto (fino alle ore {formattedTime})}many{Per # minuti (fino alle ore {formattedTime})}other{Per # minuti (fino alle ore {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Per 1 min (fino alle ore {formattedTime})}many{Per # min (fino alle ore {formattedTime})}other{Per # min (fino alle ore {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Per 1 ora (fino alle ore {formattedTime})}many{Per # ore (fino alle ore {formattedTime})}other{Per # ore (fino alle ore {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Per 1 h (fino alle ore {formattedTime})}many{Per # h (fino alle ore {formattedTime})}other{Per # h (fino alle ore {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Per un minuto}many{Per # minuti}other{Per # minuti}}"</string>
+ <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Per 1 min}many{Per # min}other{Per # min}}"</string>
+ <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Per 1 ora}many{Per # ore}other{Per # ore}}"</string>
+ <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Per 1 h}many{Per # h}other{Per # h}}"</string>
<string name="zen_mode_until_next_day" msgid="1403042784161725038">"Fino a: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Fino a <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Fino a <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (prossima sveglia)"</string>
@@ -2000,7 +2001,7 @@
<string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Salva per Compilazione automatica"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Impossibile compilare automaticamente i contenuti"</string>
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Nessun suggerimento di Compilazione automatica"</string>
- <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Un suggerimento di compilazione automatica}other{# suggerimenti di compilazione automatica}}"</string>
+ <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Un suggerimento di compilazione automatica}many{# suggerimenti di compilazione automatica}other{# suggerimenti di compilazione automatica}}"</string>
<string name="autofill_save_title" msgid="7719802414283739775">"Vuoi salvare su "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_type" msgid="3002460014579799605">"Vuoi salvare la <xliff:g id="TYPE">%1$s</xliff:g> su "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Vuoi salvare <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> su "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
@@ -2110,7 +2111,7 @@
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"Presentazione <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"Il Bluetooth rimane attivo durante l\'uso della modalità aereo"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"Caricamento"</string>
- <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # file}other{{file_name} + # file}}"</string>
+ <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # file}many{{file_name} + # file}other{{file_name} + # file}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Nessuna persona consigliata per la condivisione"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Elenco di app"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"A questa app non è stata concessa l\'autorizzazione di registrazione, ma l\'app potrebbe acquisire l\'audio tramite questo dispositivo USB."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 40a3a7dcaf7e..30c694222b62 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -607,8 +607,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"פעולת טביעת האצבע בוטלה."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"פעולת טביעת האצבע בוטלה על ידי המשתמש."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"יותר מדי ניסיונות. יש לנסות שוב מאוחר יותר."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"בוצעו יותר מדי ניסיונות. יש להשתמש בנעילת המסך במקום זאת."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"כדאי לנסות שוב."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"לא נסרקו טביעות אצבע."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"במכשיר הזה אין חיישן טביעות אצבע."</string>
@@ -637,7 +636,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"צריך ליצור קשר עם ספק תיקונים."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"לא ניתן ליצור את התבנית לזיהוי הפנים. יש לנסות שוב."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"בהירה מדי. צריך תאורה עדינה יותר."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"כדאי לנסות בתאורה חזקה יותר"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"צריך להרחיק את הטלפון"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"צריך לקרב את הטלפון"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"צריך להגביה את הטלפון"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index b1373c6876da..8733542c1578 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋の操作をキャンセルしました。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"指紋の操作がユーザーによりキャンセルされました。"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"所定の回数以上間違えました。しばらくしてからもう一度お試しください。"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"もう一度お試しください。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"指紋が登録されていません。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"このデバイスには指紋認証センサーがありません。"</string>
@@ -635,7 +634,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"修理業者に調整を依頼してください。"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"顔モデルを作成できません。もう一度お試しください。"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"明るすぎます。もっと暗い場所でお試しください。"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"もっと明るい場所でお試しください"</string>
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"十分な光がありません"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"スマートフォンをもっと離してください"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"スマートフォンをもっと近づけてください"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"スマートフォンをもっと上げてください"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 090fc1fb5271..2b4262c41be5 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"თითის ანაბეჭდის აღების ოპერაცია გაუქმდა."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"თითის ანაბეჭდის ოპერაცია გააუქმა მომხმარებელმა."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ძალიან ბევრი მცდელობა იყო. სცადეთ მოგვიანებით."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"მეტისმეტად ბევრი მცდელობა იყო. სანაცვლოდ გამოიყენეთ ეკრანის დაბლოკვა."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ხელახლა სცადეთ"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"თითის ანაბეჭდები რეგისტრირებული არ არის."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ეწვიეთ შეკეთების სერვისის პროვაიდერს."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"თქვენი სახის მოდელი ვერ იქმნება. ცადეთ ხელახლა."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"მეტისმეტად ნათელია. ცადეთ უფრო სუსტი განათება."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"ცადეთ უფრო ძლიერი განათება"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"გაწიეთ ტელეფონი უფრო შორს"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"მიიტანეთ ტელეფონი უფრო ახლოს"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"აწიეთ ტელეფონი ზემოთ"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index afd0bf86afd1..4a37cc55b4a9 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -635,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Жөндеу қызметіне барыңыз."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Бет үлгісі жасалмады. Қайталап көріңіз."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Тым ашық. Күңгірттеу жарық керек."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Жарық деңгейін арттырыңыз."</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Телефонды алшақ ұстаңыз."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Телефонды жақынырақ ұстаңыз."</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Телефонды жоғарырақ ұстаңыз."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index a878eb308e05..573892537342 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"បានបោះបង់ប្រតិបត្តិការស្នាមម្រាមដៃ។"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ប្រតិបត្តិការ​ស្នាម​ម្រាម​ដៃ​ត្រូវ​បាន​បោះ​បង់​ដោយ​អ្នក​ប្រើប្រាស់។"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ព្យាយាមចូលច្រើនពេកហើយ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ព្យាយាម​ច្រើនដងពេក។ សូមប្រើការចាក់សោ​អេក្រង់ជំនួសវិញ។"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ព្យាយាមម្ដងទៀត។"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"មិន​មាន​ការ​ចុះឈ្មោះស្នាម​ម្រាមដៃទេ។"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ឧបករណ៍នេះ​មិនមាន​ឧបករណ៍ចាប់​ស្នាមម្រាមដៃទេ។"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ទាក់ទងក្រុមហ៊ុន​ផ្ដល់ការជួសជុល។"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"មិនអាចបង្កើតគំរូមុខរបស់អ្នកបានទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ភ្លឺពេក។ សូមសាកល្បង​ប្រើ​ពន្លឺស្រាលជាងនេះ។"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"សាកល្បងប្រើ​ពន្លឺភ្លឺជាងនេះ"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ដាក់​ទូរសព្ទឱ្យឆ្ងាយ​ជាងមុន"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ដាក់​ទូរសព្ទ​ឱ្យជិត​ជាងមុន"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ដាក់​ទូរសព្ទ​ឱ្យខ្ពស់​ជាងមុន"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 1bcc7051e373..61c941555896 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆ."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ಬಳಕೆದಾರರು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಪಡಿಸಿದ್ದಾರೆ."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ಹಲವಾರು ಪ್ರಯತ್ನಗಳು. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಬದಲಾಗಿ ಪರದೆಲಾಕ್ ಬಳಸಿ."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ಯಾವುದೇ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಅನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ಈ ಸಾಧನವು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌‌ ಅನ್ನು ಹೊಂದಿಲ್ಲ."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ರಿಪೇರಿ ಮಾಡುವವರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ಫೇಸ್ ಮಾಡೆಲ್ ರಚಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ತುಂಬಾ ಪ್ರಕಾಶಮಾನವಾಗಿದೆ ಮಂದ ಪ್ರಕಾಶಮಾನವಿರುವ ಲೈಟ್ ಬಳಸಿ"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"ಪ್ರಕಾಶಮಾನವಾದ ಲೈಟಿಂಗ್ ಬಳಸಿ"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ಫೋನ್ ಅನ್ನು ದೂರಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ಫೋನ್ ಅನ್ನು ಸಮೀಪಕ್ಕೆ ತನ್ನಿ"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ಫೋನ್ ಅನ್ನು ಮೇಲಕ್ಕೆ ಎತ್ತಿ ಹಿಡಿಯಿರಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index a08d2aa0c946..32fbe92dd6b3 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"지문 인식 작업이 취소되었습니다."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"사용자가 지문 인식 작업을 취소했습니다."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"시도 횟수가 너무 많습니다. 나중에 다시 시도하세요."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"시도 횟수가 너무 많습니다. 화면 잠금을 대신 사용하세요."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"다시 시도해 보세요."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"등록된 지문이 없습니다."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"기기에 지문 센서가 없습니다."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"수리업체에 방문하세요."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"얼굴 모델을 만들 수 없습니다. 다시 시도해 주세요."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"너무 밝습니다. 조명 밝기를 조금 낮춰보세요."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"조명을 밝게 해 보세요."</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"휴대전화를 얼굴에서 더 멀리 떨어뜨려 주세요."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"휴대전화를 얼굴에 더 가까이 가져와 주세요."</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"휴대전화를 위로 이동하세요"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 7572c4b622b3..b1673fdffdd8 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Манжа изи иш-аракети жокко чыгарылды."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Манжа изи операциясын колдонуучу жокко чыгарды."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Аракеттер өтө көп болду. Бир аздан кийин кайталап көрүңүз."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Өтө көп жолу аракет кылдыңыз. Экранды кулпулоо функциясын колдонуңуз."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Кайра бир аракеттениңиз."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бир да манжа изи катталган эмес."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бул түзмөктө манжа изинин сенсору жок."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Тейлөө кызматына кайрылыңыз."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Жүзүңүздүн үлгүсү түзүлгөн жок. Кайталаңыз."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Өтө жарык. Жарыктыкты азайтып көрүңүз."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Жарыгыраак жерге туруңуз"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Телефонду алыстатыңыз"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Телефонду жакындатыңыз"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Телефонду жогору жылдырыңыз"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 8bfe9e2cb098..3cdebdd97335 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ຍົກ​ເລີກ​ການ​ດຳ​ເນີນ​ການ​ລາຍ​ນີ້ວ​ມື​ແລ້ວ."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ຜູ້ໃຊ້ໄດ້ຍົກເລີກຄຳສັ່ງລາຍນິ້ວມືແລ້ວ."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ມີ​ຄວາມ​ພະ​ຍາ​ຍາມ​ຫຼາຍ​ຄັ້ງ​ເກີນ​ໄປ. ລອງ​ໃໝ່​ພາຍ​ຫຼັງ."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາໃຊ້ການລອກໜ້າຈໍແທນ."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ລອງໃໝ່ອີກຄັ້ງ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ບໍ່ມີການລົງທະບຽນລາຍນິ້ວມື."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ກະລຸນາໄປຫາຜູ້ໃຫ້ບໍລິການສ້ອມແປງ."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ບໍ່ສາມາດສ້າງຮູບແບບໃບໜ້າຂອງທ່ານໄດ້. ກະລຸນາລອງໃໝ່."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ແຈ້ງເກີນໄປ. ລອງຄ່ອຍແສງໄຟລົງ."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"ກະລຸນາລອງໃຊ້ສະພາບແສງທີ່ແຈ້ງຂຶ້ນ"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ເລື່ອນໂທລະສັບອອກໄປໄກຂຶ້ນ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ເລື່ອນໂທລະສັບເຂົ້າໄປໃກ້ຂຶ້ນ"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ຍົກໂທລະສັບໃຫ້ສູງຂຶ້ນ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index afa77e684b9b..73ce68a4ed7d 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -607,8 +607,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Piršto antspaudo operacija atšaukta."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Piršto antspaudo operaciją atšaukė naudotojas."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Per daug bandymų. Vėliau bandykite dar kartą."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Per daug bandymų. Naudokite ekrano užraktą."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Bandykite dar kartą."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neužregistruota jokių kontrolinių kodų."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šiame įrenginyje nėra kontrolinio kodo jutiklio."</string>
@@ -637,7 +636,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Apsilankykite pas taisymo paslaugos teikėją."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nepavyko sukurti veido modelio. Band. dar kartą."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Per šviesu. Išbandykite mažesnį apšvietimą."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Išbandykite šviesesnį apšvietimą"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Laikykite telefoną toliau"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Laikykite telefoną arčiau"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Laikykite telefoną aukščiau"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index d44c435fe370..8429919d5999 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -606,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Nospieduma darbība neizdevās."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Lietotājs atcēla pirksta nospieduma darbību."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Pārāk daudz mēģinājumu. Vēlāk mēģiniet vēlreiz."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Pārāk daudz mēģinājumu. Izmantojiet ekrāna bloķēšanu."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Mēģiniet vēlreiz."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nav reģistrēts neviens pirksta nospiedums."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šajā ierīcē nav pirksta nospieduma sensora."</string>
@@ -636,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Sazinieties ar remonta pakalpojumu sniedzēju."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nevar izveidot sejas modeli. Mēģiniet vēlreiz."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Pārāk spilgts. Izmēģiniet maigāku apgaismojumu."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Izmēģiniet spožāku apgaismojumu"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Pārvietojiet tālruni tālāk."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Pārvietojiet tālruni tuvāk."</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Paceliet tālruni augstāk."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index e74bc07084cb..80934d0e163e 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Операцијата со отпечаток се откажа."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Корисникот ја откажа потврдата со отпечаток."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Премногу обиди. Обидете се повторно подоцна."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Премногу обиди. Користете заклучување екран."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Обидете се повторно."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Не се запишани отпечатоци."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Уредов нема сензор за отпечатоци."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Однесете го на поправка."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Не може да создаде модел на лик. Обидете се пак."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Премногу светла. Пробајте со послабо осветлување."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Пробајте со посилно осветлување"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Оддалечете го телефонот"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Доближете го телефонот"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Кренете го телефонот погоре"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 70b706a2ba3b..743772583713 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ഫിംഗർപ്രിന്റ് പ്രവർത്തനം റദ്ദാക്കി."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ഉപയോക്താവ് റദ്ദാക്കിയ ഫിംഗർപ്രിന്‍റ് പ്രവർത്തനം."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"നിരവധി ശ്രമങ്ങൾ. പകരം സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"വീണ്ടും ശ്രമിക്കൂ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"വിരലടയാളങ്ങൾ എൻറോൾ ചെയ്തിട്ടില്ല."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസറില്ല."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"റിപ്പയർ കേന്ദ്രം സന്ദർശിക്കുക."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"മുഖ മോഡൽ സൃഷ്ടിക്കാനാകുന്നില്ല. വീണ്ടും ശ്രമിക്കൂ."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"വളരെയധികം തെളിച്ചം. സൗമ്യതയേറിയ പ്രകാശം ശ്രമിക്കൂ."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"കൂടുതൽ വെളിച്ചമുള്ളയിടത്ത് പരീക്ഷിച്ച് നോക്കൂ"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ഫോൺ കൂടുതൽ ദൂരേയ്ക്ക് നീക്കുക"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ഫോൺ അടുത്തേക്ക് നീക്കുക"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ഫോൺ മുകളിലേക്ക് ഉയർത്തുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 4e50f56759c9..19352807a3ca 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Хурууны хээний бүртгэл амжилтгүй боллоо."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Хэрэглэгч хурууны хээний баталгаажуулалтыг цуцалсан байна."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Хэтэрхий олон оролдлоо. Түр хүлээгээд дахин оролдоно уу."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Хэт олон удаа оролдлоо. Оронд нь дэлгэцийн түгжээ ашиглана уу."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Дахин оролдно уу."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бүртгүүлсэн хурууны хээ алга."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Засварын үйлчилгээ үзүүлэгчид зочилно уу."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Нүүрний загвар үүсгэж чадсангүй. Дахин оролдоно уу."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Хэт цайвар байна. Гэрэл багатай газар оролдоно уу."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Гэрэлтэй орчинд туршина уу"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Утсаа холдуулна уу"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Утсаа ойртуулна уу"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Утсаа дээшлүүлнэ үү"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 371f7766f782..ac9f2890b753 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिंट ऑपरेशन रद्द झाले."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"खूप प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"खूप जास्त प्रयत्न. त्याऐवजी स्क्रीन लॉक वापरा."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोणत्याही फिंगरप्रिंटची नोंद झाली नाही"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"दुरुस्तीच्या सेवा पुरवठादाराला भेट द्या."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"फेस मॉडेल तयार करू शकत नाही. पुन्हा प्रयत्न करा."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"खूप प्रखर. आणखी सौम्य प्रकाश वापरून पहा."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"आणखी प्रखर प्रकाश वापरून पहा"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"फोन आणखी दूर हलवा"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"फोन आणखी जवळ हलवा"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"फोन आणखी वर हलवा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 2179308d1e22..94be0fc2797e 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Pengendalian cap jari dibatalkan."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Pengendalian cap jari dibatalkan oleh pengguna."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Terlalu banyak percubaan. Cuba sebentar lagi."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Terlalu banyak percubaan. Gunakan kunci skrin."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Cuba lagi."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tiada cap jari didaftarkan."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Peranti ini tiada penderia cap jari."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Lawati penyedia pembaikan."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Tidak dapat membuat model wajah anda. Cuba lagi."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Terlalu terang. Cuba pencahayaan yang lebih lembut."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Cuba pencahayaan yang lebih cerah"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Jauhkan telefon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Dekatkan telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Tinggikan lagi telefon"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index ebaa96751d1a..0a11698d100e 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"လက်ဗွေယူခြင်း ပယ်ဖျက်လိုက်သည်။"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"လက်ဗွေဖြင့် အထောက်အထားစိစစ်ခြင်းကို အသုံးပြုသူက ပယ်ဖျက်ထားသည်။"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ကြိုးစာမှု အကြိမ်များနေ၏။ နောက်မှ ထပ်မံကြိုးစားပါ။"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ကြိုးပမ်းမှုအကြိမ်ရေ များလွန်းသည်။ ဖန်သားပြင်လော့ခ်ချခြင်းကို အစားထိုးသုံးပါ။"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ပြန်ကြိုးစားပါ"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"မည်သည့် လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ။"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ဤစက်တွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ။"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ပြုပြင်ရေး ဝန်ဆောင်မှုပေးသူထံသို့ သွားပါ။"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"သင့်မျက်နှာနမူနာ ပြုလုပ်၍မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"အလွန် လင်းသည်။ အလင်းလျှော့ကြည့်ပါ။"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"ပိုလင်းအောင် လုပ်ကြည့်ပါ"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ဖုန်းကို အဝေးသို့ခွာပါ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ဖုန်းကို အနားသို့ပိုတိုးပါ"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ဖုန်းကို ပိုမြှင့်လိုက်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 120c08ad004d..e4398592be15 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrykk-operasjonen ble avbrutt."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrykk-operasjonen ble avbrutt av brukeren."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"For mange forsøk. Prøv på nytt senere."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"For mange forsøk. Bruk skjermlås i stedet."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Prøv på nytt."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ingen fingeravtrykk er registrert."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enheten har ikke fingeravtrykkssensor."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Gå til en reparasjonsleverandør."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Kan ikke lage ansiktsmodell. Prøv på nytt."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"For lyst. Prøv svakere belysning."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Prøv sterkere belysning"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Flytt telefonen lengre unna"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Flytt telefonen nærmere"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Flytt telefonen høyere"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index d8f8077ed430..b3647cee3aa6 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिन्ट सञ्चालन रद्द गरियो।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो।"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"धेरै प्रयासहरू। केहि समय पछि पुन: प्रयास गर्नुहोला"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"निकै धेरै पटक प्रयास गरिसकिएको छ। बरु स्क्रिन लक प्रयोग गर्नुहोस्।"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"पुन: प्रयास गर्नुहोला।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कुनै पनि फिंगरप्रिन्ट दर्ता गरिएको छैन।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"यो डिभाइसमा कुनै पनि फिंगरप्रिन्ट सेन्सर छैन।"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"फिंगरप्रिन्ट सेन्सर मर्मत गर्ने सेवा प्रदायक कम्पनीमा सम्पर्क गर्नुहोस्।"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"तपाईंको फेस मोडेल सिर्जना गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ज्यादै चम्किलो। अझ मधुरो प्रकाश प्रयोग गरी हेर्नु…"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"अझ उज्यालो ठाउँमा गएर फोटो खिची हेर्नुहोस्"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"फोन अझै पर सार्नुहोस्"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"फोन अझै नजिक सार्नुहोस्"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"फोन अझ माथि उठाउनुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 3ff13f6964d0..92ea1c48f318 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Vingerafdrukbewerking geannuleerd."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vingerafdrukverificatie geannuleerd door gebruiker."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Te veel pogingen. Probeer het later opnieuw."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Te veel pogingen. Gebruik in plaats daarvan de schermvergrendeling."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Probeer het opnieuw."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukken geregistreerd."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dit apparaat heeft geen vingerafdruksensor."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Ga naar een reparateur."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Kan gezichtsmodel niet maken. Probeer het opnieuw."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Overbelicht. Probeer een minder felle belichting."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Probeer fellere verlichting"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Houd de telefoon verder weg"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Houd de telefoon dichterbij"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Houd de telefoon hoger"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 29391d64129c..e42a7e306ad0 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରାଗଲା।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ଉପଯୋଗକର୍ତ୍ତା ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରିଛନ୍ତି।"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ବହୁତ ପ୍ରୟାସ କରାଗଲା। ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ଅନେକଗୁଡ଼ିଏ ପ୍ରଚେଷ୍ଟା। ଏହା ପରିବର୍ତ୍ତେ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"କୌଣସି ଆଙ୍ଗୁଠି ଚିହ୍ନ ପଞ୍ଜୀକୃତ ହୋଇନାହିଁ।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ଏହି ଡିଭାଇସ୍‌ରେ ଟିପଚିହ୍ନ ସେନ୍‍ସର୍ ନାହିଁ।"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ଏକ ମରାମତି କେନ୍ଦ୍ରକୁ ଭିଜିଟ୍ କରନ୍ତୁ।"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ଫେସର ମଡେଲ ତିଆରି କରାଯାଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କର।"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ଅତ୍ୟଧିକ ଉଜ୍ଵଳ। କମ୍ ଉଜ୍ବଳକରଣରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"ଉଜ୍ଜ୍ୱଳ ଲାଇଟ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ଫୋନକୁ ଟିକେ ଦୂରକୁ ନିଅନ୍ତୁ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ଫୋନକୁ ପାଖକୁ ଆଣନ୍ତୁ"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ଫୋନକୁ ଉପରକୁ ମୁଭ କରନ୍ତୁ"</string>
@@ -2044,7 +2044,7 @@
<string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"ଶର୍ଟକଟ୍‍ ରିଷ୍ଟୋର୍‍ କରିହେଲା ନାହିଁ, କାରଣ ଏହି ଆପ୍‍ର ସ୍ୱାକ୍ଷର ମେଳ ହେଉନାହିଁ"</string>
<string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"ଶର୍ଟକଟ୍‍ ରିଷ୍ଟୋର୍‍ କରିହେଲା ନାହିଁ"</string>
<string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"ଶର୍ଟକଟ୍‍ ଅକ୍ଷମ କରାଯାଇଛି"</string>
- <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ଅନଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
+ <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ଅନଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
<string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"କୌଣସିମତେ ଖୋଲନ୍ତୁ"</string>
<string name="harmful_app_warning_title" msgid="8794823880881113856">"ହାନିକାରକ ଆପ୍‌ ଚିହ୍ନଟ ହୋଇଛି"</string>
<string name="log_access_confirmation_title" msgid="2343578467290592708">"ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 0433cf4e7195..500f37db3b66 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ. ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸਦੀ ਬਜਾਏ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ।"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ਕੋਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਰਜ ਨਹੀਂ ਕੀਤੇ ਗਏ।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ।"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ਮੁਰੰਮਤ ਪ੍ਰਦਾਨਕ \'ਤੇ ਜਾਓ।"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ਤੁਹਾਡੇ ਚਿਹਰੇ ਦਾ ਮਾਡਲ ਨਹੀਂ ਬਣਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ਬਹੁਤ ਜ਼ਿਆਦਾ ਚਮਕ। ਹਲਕੀ ਚਮਕ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"ਤੇਜ਼ ਰੋਸ਼ਨੀ ਕਰ ਕੇ ਦੇਖੋ"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ਫ਼ੋਨ ਨੂੰ ਦੂਰ ਲਿਜਾਓ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ਫ਼ੋਨ ਨੇੜੇ ਲਿਜਾਓ"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ਫ਼ੋਨ ਨੂੰ ਥੋੜ੍ਹਾ ਉੱਤੇ ਲਿਜਾਓ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ed1e709064af..be71f359b64e 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -607,8 +607,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Odczyt odcisku palca został anulowany."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Odczyt odcisku palca został anulowany przez użytkownika."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Zbyt wiele prób. Spróbuj ponownie później."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Zbyt wiele prób. Użyj blokady ekranu."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Spróbuj ponownie."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nie zarejestrowano odcisków palców."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"To urządzenie nie jest wyposażone w czytnik linii papilarnych."</string>
@@ -637,7 +636,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Odwiedź serwis."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nie można utworzyć modelu twarzy. Spróbuj ponownie."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Zbyt jasno. Spróbuj przy słabszym świetle."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Spróbuj w jaśniejszym świetle"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Odsuń telefon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Przybliż telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Przesuń telefon wyżej"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index ce63b94f29e3..ad944505ef23 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -47,6 +47,7 @@
<string name="enablePin" msgid="2543771964137091212">"Falha. Ative o bloqueio do chip/R-UIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="one">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
+ <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
<item quantity="other">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
</plurals>
<string name="imei" msgid="2157082351232630390">"IMEI"</string>
@@ -175,7 +176,7 @@
<string name="low_memory" product="watch" msgid="3479447988234030194">"Armazenamento do relógio cheio. Exclua alguns arquivos para liberar espaço."</string>
<string name="low_memory" product="tv" msgid="6663680413790323318">"O armazenamento do dispositivo Android TV está cheio. Exclua alguns arquivos para liberar espaço."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"O armazenamento do telefone está cheio. Exclua alguns arquivos para liberar espaço."</string>
- <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoridade certificadora instalada}one{Autoridade certificadora instalada}other{Autoridades certificadoras instaladas}}"</string>
+ <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoridade certificadora instalada}one{Autoridade certificadora instalada}many{Autoridades certificadoras instaladas}other{Autoridades certificadoras instaladas}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Por terceiros desconhecidos"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Pelo administrador do seu perfil de trabalho"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Por <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
@@ -249,7 +250,7 @@
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Use este recurso na maioria das circunstâncias. Ele permite que você acompanhe o progresso do relatório, informe mais detalhes sobre o problema e faça capturas de tela. É possível que ele omita algumas seções menos utilizadas que levam muito tempo na emissão dos relatórios."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Relatório completo"</string>
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"Use esta opção para ter o mínimo de interferência do sistema quando seu dispositivo não estiver respondendo ou estiver muito lento, ou quando você precisar de todas as seções de relatórios. Ela não permite que você informe mais detalhes ou faça capturas de tela adicionais."</string>
- <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Capturas de tela para o relatório do bug vão ser feitas em # segundo.}one{Capturas de tela para o relatório do bug vão ser feitas em # segundo.}other{Capturas de tela para o relatório do bug vão ser feitas em # segundos.}}"</string>
+ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Capturas de tela para o relatório do bug vão ser feitas em # segundo.}one{Capturas de tela para o relatório do bug vão ser feitas em # segundo.}many{Capturas de tela para o relatório do bug vão ser feitas em # segundos.}other{Capturas de tela para o relatório do bug vão ser feitas em # segundos.}}"</string>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Captura de tela com o relatório do bug concluída"</string>
<string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Falha ao capturar a tela com o relatório do bug"</string>
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo silencioso"</string>
@@ -605,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo usuário."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Excesso de tentativas. Tente novamente mais tarde."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tente novamente."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
@@ -635,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Entre em contato com uma assistência técnica."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Falha ao criar o modelo de rosto. Tente de novo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Muito iluminado. Diminua a iluminação."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Use uma iluminação mais intensa"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Afaste o smartphone"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Aproxime o smartphone do seu rosto"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mova o smartphone para cima"</string>
@@ -1085,7 +1086,7 @@
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> quer ativar o Explorar por toque. Com ele, você pode ouvir ou ver descrições do que está sob seu dedo e interagir com o telefone por gestos."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"1 mês atrás"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Antes de 1 mês atrás"</string>
- <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{No último # dia}one{No último # dia}other{Nos últimos # dias}}"</string>
+ <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{No último # dia}one{No último # dia}many{Nos últimos # dias}other{Nos últimos # dias}}"</string>
<string name="last_month" msgid="1528906781083518683">"Mês passado"</string>
<string name="older" msgid="1645159827884647400">"Mais antigos"</string>
<string name="preposition_for_date" msgid="2780767868832729599">"em <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1112,14 +1113,14 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"em <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"em <xliff:g id="COUNT">%d</xliff:g> dias"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"em <xliff:g id="COUNT">%d</xliff:g>a"</string>
- <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto atrás}one{# minuto atrás}other{# minutos atrás}}"</string>
- <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hora atrás}one{# hora atrás}other{# horas atrás}}"</string>
- <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dia atrás}one{# dia atrás}other{# dias atrás}}"</string>
- <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{# ano atrás}one{# ano atrás}other{# anos atrás}}"</string>
- <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}one{# minuto}other{# minutos}}"</string>
- <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}one{# hora}other{# horas}}"</string>
- <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# dia}one{# dia}other{# dias}}"</string>
- <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# ano}one{# ano}other{# anos}}"</string>
+ <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto atrás}one{# minuto atrás}many{# minutos atrás}other{# minutos atrás}}"</string>
+ <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hora atrás}one{# hora atrás}many{# horas atrás}other{# horas atrás}}"</string>
+ <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dia atrás}one{# dia atrás}many{# dias atrás}other{# dias atrás}}"</string>
+ <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{# ano atrás}one{# ano atrás}many{# anos atrás}other{# anos atrás}}"</string>
+ <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}one{# minuto}many{# minutos}other{# minutos}}"</string>
+ <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}one{# hora}many{# horas}other{# horas}}"</string>
+ <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# dia}one{# dia}many{# dias}other{# dias}}"</string>
+ <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# ano}one{# ano}many{# anos}other{# anos}}"</string>
<string name="VideoView_error_title" msgid="5750686717225068016">"Problema com o vídeo"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Este vídeo não é válido para transmissão neste dispositivo."</string>
<string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Não é possível reproduzir este vídeo."</string>
@@ -1168,7 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
- <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 de {max} estrelas}one{# de {max} estrelas}other{# de {max} estrelas}}"</string>
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 de {max} estrelas}one{# de {max} estrelas}many{# de {max} estrelas}other{# de {max} estrelas}}"</string>
<string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
@@ -1507,7 +1508,7 @@
<string name="skip_button_label" msgid="3566599811326688389">"Pular"</string>
<string name="no_matches" msgid="6472699895759164599">"Não encontrado"</string>
<string name="find_on_page" msgid="5400537367077438198">"Localizar na página"</string>
- <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondência}one{# de {total}}other{# de {total}}}"</string>
+ <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondência}one{# de {total}}many{# de {total}}other{# de {total}}}"</string>
<string name="action_mode_done" msgid="2536182504764803222">"Concluído"</string>
<string name="progress_erasing" msgid="6891435992721028004">"Limpando armazenamento compartilhado…"</string>
<string name="share" msgid="4157615043345227321">"Compartilhar"</string>
@@ -1860,14 +1861,14 @@
<string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você está usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não apareçam até você tocar nelas."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar a Economia de dados?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string>
- <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Por um minuto (até {formattedTime})}one{Por # minuto (até {formattedTime})}other{Por # minutos (até {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Por 1min (até {formattedTime})}one{Por #min (até {formattedTime})}other{Por #min (até {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Por 1 hora (até {formattedTime})}one{Por # hora (até {formattedTime})}other{Por # horas (até {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Por 1h (até {formattedTime})}one{Por #h (até {formattedTime})}other{Por #h (até {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Por um minuto}one{Por # minuto}other{Por # minutos}}"</string>
- <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Por 1min}one{Por #min}other{Por #min}}"</string>
- <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Por 1 hora}one{Por # hora}other{Por # horas}}"</string>
- <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Por 1h}one{Por #h}other{Por #h}}"</string>
+ <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Por um minuto (até {formattedTime})}one{Por # minuto (até {formattedTime})}many{Por # minutos (até {formattedTime})}other{Por # minutos (até {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Por 1min (até {formattedTime})}one{Por #min (até {formattedTime})}many{Por #min (até {formattedTime})}other{Por #min (até {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Por 1 hora (até {formattedTime})}one{Por # hora (até {formattedTime})}many{Por # horas (até {formattedTime})}other{Por # horas (até {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Por 1h (até {formattedTime})}one{Por #h (até {formattedTime})}many{Por #h (até {formattedTime})}other{Por #h (até {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Por um minuto}one{Por # minuto}many{Por # minutos}other{Por # minutos}}"</string>
+ <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Por 1min}one{Por #min}many{Por #min}other{Por #min}}"</string>
+ <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Por 1 hora}one{Por # hora}many{Por # horas}other{Por # horas}}"</string>
+ <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Por 1h}one{Por #h}many{Por #h}other{Por #h}}"</string>
<string name="zen_mode_until_next_day" msgid="1403042784161725038">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
@@ -2000,7 +2001,7 @@
<string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Salvar no Preenchimento automático"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Não é possível preencher os conteúdos automaticamente"</string>
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Sem sugestões de preenchimento automático"</string>
- <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Uma sugestão de preenchimento automático}one{# sugestão de preenchimento automático}other{# sugestões de preenchimento automático}}"</string>
+ <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Uma sugestão de preenchimento automático}one{# sugestão de preenchimento automático}many{# sugestões de preenchimento automático}other{# sugestões de preenchimento automático}}"</string>
<string name="autofill_save_title" msgid="7719802414283739775">"Salvar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_type" msgid="3002460014579799605">"Salvar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Salvar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
@@ -2110,7 +2111,7 @@
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"Apresentação em <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"O Bluetooth permanecerá ativado no modo avião"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"Carregando"</string>
- <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # arquivo}one{{file_name} + # arquivo}other{{file_name} + # arquivos}}"</string>
+ <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # arquivo}one{{file_name} + # arquivo}many{{file_name} + # arquivos}other{{file_name} + # arquivos}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Não há sugestões de pessoas para compartilhar"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Lista de apps"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Este app não tem permissão de gravação, mas pode capturar áudio pelo dispositivo USB."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 55cb68bf1e1f..000d3f7c4bf3 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -46,6 +46,7 @@
<string name="needPuk2" msgid="7032612093451537186">"Introduza o PUK2 para desbloquear o cartão SIM."</string>
<string name="enablePin" msgid="2543771964137091212">"Ação sem êxito. Ative o bloqueio do SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
+ <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
<item quantity="other">Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar bloqueado.</item>
<item quantity="one">Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar bloqueado.</item>
</plurals>
@@ -175,7 +176,7 @@
<string name="low_memory" product="watch" msgid="3479447988234030194">"O armazenamento de visualizações está cheio. Elimine alguns ficheiros para libertar espaço."</string>
<string name="low_memory" product="tv" msgid="6663680413790323318">"O armazenamento do dispositivo Android TV está cheio. Elimine alguns ficheiros para libertar espaço."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"O armazenamento do telemóvel está cheio. Elimine alguns ficheiros para libertar espaço."</string>
- <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoridade de certificação instalada}other{Autoridades de certificação instaladas}}"</string>
+ <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoridade de certificação instalada}many{Autoridades de certificação instaladas}other{Autoridades de certificação instaladas}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Por um terceiro desconhecido"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Pelo gestor do seu perfil de trabalho"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Por <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
@@ -249,7 +250,7 @@
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Utilize esta opção na maioria das circunstâncias. Permite monitorizar o progresso do relatório, introduzir mais detalhes acerca do problema e tirar capturas de ecrã. Pode omitir algumas secções menos utilizadas que demoram muito tempo a comunicar."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Relatório completo"</string>
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"Utilize esta opção para uma interferência mínima do sistema quando o dispositivo não responder ou estiver demasiado lento, ou quando precisar de todas as secções de relatório. Não permite introduzir mais detalhes ou tirar capturas de ecrã adicionais."</string>
- <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{A fazer uma captura de ecrã do relatório de erro dentro de # segundo.}other{A fazer uma captura de ecrã do relatório de erro dentro de # segundos.}}"</string>
+ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{A fazer uma captura de ecrã do relatório de erro dentro de # segundo.}many{A fazer uma captura de ecrã do relatório de erro dentro de # segundos.}other{A fazer uma captura de ecrã do relatório de erro dentro de # segundos.}}"</string>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Captura de ecrã tirada com o relatório de erro."</string>
<string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Falha ao fazer captura de ecrã com o relatório de erro."</string>
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo silencioso"</string>
@@ -605,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo utilizador."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiadas tentativas. Tente novamente mais tarde."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiadas tentativas. Em alternativa, use o bloqueio de ecrã."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tente novamente."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem sensor de impressões digitais."</string>
@@ -635,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visite um fornecedor de serviços de reparação."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Impossível criar modelo de rosto. Tente novamente."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Demasiado clara. Experimente uma luz mais suave."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Experimente um local com mais luz"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Afaste ainda mais o telemóvel"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Aproxime o telemóvel do rosto"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mova o telemóvel mais para cima"</string>
@@ -1085,7 +1086,7 @@
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> pretende ativar a funcionalidade Explorar Através do Toque. Quando a funcionalidade Explorar Através do Toque estiver ativada, pode ouvir ou visualizar descrições sobre o que está por baixo do seu dedo ou executar gestos para interagir com o telemóvel."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"Há 1 mês"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Há mais de 1 mês"</string>
- <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{# dia anterior}other{# dias anteriores}}"</string>
+ <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{# dia anterior}many{# dias anteriores}other{# dias anteriores}}"</string>
<string name="last_month" msgid="1528906781083518683">"Último mês"</string>
<string name="older" msgid="1645159827884647400">"Mais antiga"</string>
<string name="preposition_for_date" msgid="2780767868832729599">"a <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1112,14 +1113,14 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"em <xliff:g id="COUNT">%d</xliff:g> h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"em <xliff:g id="COUNT">%d</xliff:g> d"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"em <xliff:g id="COUNT">%d</xliff:g> a"</string>
- <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Há # minuto}other{Há # minutos}}"</string>
- <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Há # hora}other{Há # horas}}"</string>
- <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Há # dia}other{Há # dias}}"</string>
- <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Há # ano}other{Há # anos}}"</string>
- <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}other{# minutos}}"</string>
- <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}other{# horas}}"</string>
- <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# dia}other{# dias}}"</string>
- <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# ano}other{# anos}}"</string>
+ <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{Há # minuto}many{Há # minutos}other{Há # minutos}}"</string>
+ <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{Há # hora}many{Há # horas}other{Há # horas}}"</string>
+ <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{Há # dia}many{Há # dias}other{Há # dias}}"</string>
+ <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{Há # ano}many{Há # anos}other{Há # anos}}"</string>
+ <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}many{# minutos}other{# minutos}}"</string>
+ <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string>
+ <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# dia}many{# dias}other{# dias}}"</string>
+ <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# ano}many{# anos}other{# anos}}"</string>
<string name="VideoView_error_title" msgid="5750686717225068016">"Problema com o vídeo"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Este vídeo não é válido para transmissão neste aparelho."</string>
<string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Não é possível reproduzir este vídeo."</string>
@@ -1168,7 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"não selecionado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
- <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Uma estrela de {max}}other{# estrelas de {max}}}"</string>
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{Uma estrela de {max}}many{# estrelas de {max}}other{# estrelas de {max}}}"</string>
<string name="in_progress" msgid="2149208189184319441">"em curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Concluir ação utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir ação utilizando %1$s"</string>
@@ -1507,7 +1508,7 @@
<string name="skip_button_label" msgid="3566599811326688389">"Ignorar"</string>
<string name="no_matches" msgid="6472699895759164599">"Sem correspondências"</string>
<string name="find_on_page" msgid="5400537367077438198">"Localizar na página"</string>
- <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondência}other{# de {total}}}"</string>
+ <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondência}many{# de {total}}other{# de {total}}}"</string>
<string name="action_mode_done" msgid="2536182504764803222">"Concluído"</string>
<string name="progress_erasing" msgid="6891435992721028004">"A apagar o armazenamento partilhado…"</string>
<string name="share" msgid="4157615043345227321">"Partilhar"</string>
@@ -1860,14 +1861,14 @@
<string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir a utilização de dados, a Poupança de dados impede que algumas apps enviem ou recebam dados em segundo plano. Uma determinada app que esteja a utilizar atualmente pode aceder aos dados, mas é possível que o faça com menos frequência. Isto pode significar, por exemplo, que as imagens não são apresentadas até que toque nas mesmas."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar a Poupança de dados?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string>
- <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Durante um minuto (até à[s] {formattedTime})}other{Durante # minutos (até à[s] {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durante 1 min (até à[s] {formattedTime})}other{Durante # min (até à[s] {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Durante 1 hora (até à[s] {formattedTime})}other{Durante # horas (até à[s] {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Durante 1 h (até à[s] {formattedTime})}other{Durante # h (até à[s] {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Durante um minuto}other{Durante # minutos}}"</string>
- <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Durante 1 min}other{Durante # min}}"</string>
- <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Durante 1 hora}other{Durante # horas}}"</string>
- <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Durante 1 h}other{Durante # h}}"</string>
+ <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Durante um minuto (até à[s] {formattedTime})}many{Durante # minutos (até à[s] {formattedTime})}other{Durante # minutos (até à[s] {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durante 1 min (até à[s] {formattedTime})}many{Durante # min (até à[s] {formattedTime})}other{Durante # min (até à[s] {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Durante 1 hora (até à[s] {formattedTime})}many{Durante # horas (até à[s] {formattedTime})}other{Durante # horas (até à[s] {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Durante 1 h (até à[s] {formattedTime})}many{Durante # h (até à[s] {formattedTime})}other{Durante # h (até à[s] {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Durante um minuto}many{Durante # minutos}other{Durante # minutos}}"</string>
+ <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Durante 1 min}many{Durante # min}other{Durante # min}}"</string>
+ <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Durante 1 hora}many{Durante # horas}other{Durante # horas}}"</string>
+ <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Durante 1 h}many{Durante # h}other{Durante # h}}"</string>
<string name="zen_mode_until_next_day" msgid="1403042784161725038">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
@@ -2000,7 +2001,7 @@
<string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Guardar para o Preenchimento automático"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Não é possível preencher automaticamente o conteúdo"</string>
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Sem sugestões do preenchimento automático"</string>
- <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Uma sugestão do preenchimento automático}other{# sugestões de preenchimento automático}}"</string>
+ <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Uma sugestão do preenchimento automático}many{# sugestões de preenchimento automático}other{# sugestões de preenchimento automático}}"</string>
<string name="autofill_save_title" msgid="7719802414283739775">"Pretende guardar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_type" msgid="3002460014579799605">"Pretende guardar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Pretende guardar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
@@ -2110,7 +2111,7 @@
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"Apresentação <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"O Bluetooth continuará ativado durante o modo de avião."</string>
<string name="car_loading_profile" msgid="8219978381196748070">"A carregar…"</string>
- <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # ficheiro}other{{file_name} + # ficheiros}}"</string>
+ <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # ficheiro}many{{file_name} + # ficheiros}other{{file_name} + # ficheiros}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Não existem pessoas recomendadas com quem partilhar"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Lista de aplicações"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Esta app não recebeu autorização de gravação, mas pode capturar áudio através deste dispositivo USB."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index ce63b94f29e3..ad944505ef23 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -47,6 +47,7 @@
<string name="enablePin" msgid="2543771964137091212">"Falha. Ative o bloqueio do chip/R-UIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="one">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
+ <item quantity="many">You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM is locked.</item>
<item quantity="other">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
</plurals>
<string name="imei" msgid="2157082351232630390">"IMEI"</string>
@@ -175,7 +176,7 @@
<string name="low_memory" product="watch" msgid="3479447988234030194">"Armazenamento do relógio cheio. Exclua alguns arquivos para liberar espaço."</string>
<string name="low_memory" product="tv" msgid="6663680413790323318">"O armazenamento do dispositivo Android TV está cheio. Exclua alguns arquivos para liberar espaço."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"O armazenamento do telefone está cheio. Exclua alguns arquivos para liberar espaço."</string>
- <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoridade certificadora instalada}one{Autoridade certificadora instalada}other{Autoridades certificadoras instaladas}}"</string>
+ <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoridade certificadora instalada}one{Autoridade certificadora instalada}many{Autoridades certificadoras instaladas}other{Autoridades certificadoras instaladas}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Por terceiros desconhecidos"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Pelo administrador do seu perfil de trabalho"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Por <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
@@ -249,7 +250,7 @@
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Use este recurso na maioria das circunstâncias. Ele permite que você acompanhe o progresso do relatório, informe mais detalhes sobre o problema e faça capturas de tela. É possível que ele omita algumas seções menos utilizadas que levam muito tempo na emissão dos relatórios."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Relatório completo"</string>
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"Use esta opção para ter o mínimo de interferência do sistema quando seu dispositivo não estiver respondendo ou estiver muito lento, ou quando você precisar de todas as seções de relatórios. Ela não permite que você informe mais detalhes ou faça capturas de tela adicionais."</string>
- <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Capturas de tela para o relatório do bug vão ser feitas em # segundo.}one{Capturas de tela para o relatório do bug vão ser feitas em # segundo.}other{Capturas de tela para o relatório do bug vão ser feitas em # segundos.}}"</string>
+ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Capturas de tela para o relatório do bug vão ser feitas em # segundo.}one{Capturas de tela para o relatório do bug vão ser feitas em # segundo.}many{Capturas de tela para o relatório do bug vão ser feitas em # segundos.}other{Capturas de tela para o relatório do bug vão ser feitas em # segundos.}}"</string>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Captura de tela com o relatório do bug concluída"</string>
<string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Falha ao capturar a tela com o relatório do bug"</string>
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo silencioso"</string>
@@ -605,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo usuário."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Excesso de tentativas. Tente novamente mais tarde."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tente novamente."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
@@ -635,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Entre em contato com uma assistência técnica."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Falha ao criar o modelo de rosto. Tente de novo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Muito iluminado. Diminua a iluminação."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Use uma iluminação mais intensa"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Afaste o smartphone"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Aproxime o smartphone do seu rosto"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mova o smartphone para cima"</string>
@@ -1085,7 +1086,7 @@
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> quer ativar o Explorar por toque. Com ele, você pode ouvir ou ver descrições do que está sob seu dedo e interagir com o telefone por gestos."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"1 mês atrás"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Antes de 1 mês atrás"</string>
- <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{No último # dia}one{No último # dia}other{Nos últimos # dias}}"</string>
+ <string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{No último # dia}one{No último # dia}many{Nos últimos # dias}other{Nos últimos # dias}}"</string>
<string name="last_month" msgid="1528906781083518683">"Mês passado"</string>
<string name="older" msgid="1645159827884647400">"Mais antigos"</string>
<string name="preposition_for_date" msgid="2780767868832729599">"em <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -1112,14 +1113,14 @@
<string name="duration_hours_shortest_future" msgid="2979276794547981674">"em <xliff:g id="COUNT">%d</xliff:g>h"</string>
<string name="duration_days_shortest_future" msgid="3392722163935571543">"em <xliff:g id="COUNT">%d</xliff:g> dias"</string>
<string name="duration_years_shortest_future" msgid="5537464088352970388">"em <xliff:g id="COUNT">%d</xliff:g>a"</string>
- <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto atrás}one{# minuto atrás}other{# minutos atrás}}"</string>
- <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hora atrás}one{# hora atrás}other{# horas atrás}}"</string>
- <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dia atrás}one{# dia atrás}other{# dias atrás}}"</string>
- <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{# ano atrás}one{# ano atrás}other{# anos atrás}}"</string>
- <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}one{# minuto}other{# minutos}}"</string>
- <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}one{# hora}other{# horas}}"</string>
- <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# dia}one{# dia}other{# dias}}"</string>
- <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# ano}one{# ano}other{# anos}}"</string>
+ <string name="duration_minutes_relative" msgid="8620337701051015593">"{count,plural, =1{# minuto atrás}one{# minuto atrás}many{# minutos atrás}other{# minutos atrás}}"</string>
+ <string name="duration_hours_relative" msgid="4836449961693180253">"{count,plural, =1{# hora atrás}one{# hora atrás}many{# horas atrás}other{# horas atrás}}"</string>
+ <string name="duration_days_relative" msgid="621965767567258302">"{count,plural, =1{# dia atrás}one{# dia atrás}many{# dias atrás}other{# dias atrás}}"</string>
+ <string name="duration_years_relative" msgid="8731202348869424370">"{count,plural, =1{# ano atrás}one{# ano atrás}many{# anos atrás}other{# anos atrás}}"</string>
+ <string name="duration_minutes_relative_future" msgid="5259574171747708115">"{count,plural, =1{# minuto}one{# minuto}many{# minutos}other{# minutos}}"</string>
+ <string name="duration_hours_relative_future" msgid="6670440478481140565">"{count,plural, =1{# hora}one{# hora}many{# horas}other{# horas}}"</string>
+ <string name="duration_days_relative_future" msgid="8870658635774250746">"{count,plural, =1{# dia}one{# dia}many{# dias}other{# dias}}"</string>
+ <string name="duration_years_relative_future" msgid="8855853883925918380">"{count,plural, =1{# ano}one{# ano}many{# anos}other{# anos}}"</string>
<string name="VideoView_error_title" msgid="5750686717225068016">"Problema com o vídeo"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3782449246085134720">"Este vídeo não é válido para transmissão neste dispositivo."</string>
<string name="VideoView_error_text_unknown" msgid="7658683339707607138">"Não é possível reproduzir este vídeo."</string>
@@ -1168,7 +1169,7 @@
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
- <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 de {max} estrelas}one{# de {max} estrelas}other{# de {max} estrelas}}"</string>
+ <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1 de {max} estrelas}one{# de {max} estrelas}many{# de {max} estrelas}other{# de {max} estrelas}}"</string>
<string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
@@ -1507,7 +1508,7 @@
<string name="skip_button_label" msgid="3566599811326688389">"Pular"</string>
<string name="no_matches" msgid="6472699895759164599">"Não encontrado"</string>
<string name="find_on_page" msgid="5400537367077438198">"Localizar na página"</string>
- <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondência}one{# de {total}}other{# de {total}}}"</string>
+ <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondência}one{# de {total}}many{# de {total}}other{# de {total}}}"</string>
<string name="action_mode_done" msgid="2536182504764803222">"Concluído"</string>
<string name="progress_erasing" msgid="6891435992721028004">"Limpando armazenamento compartilhado…"</string>
<string name="share" msgid="4157615043345227321">"Compartilhar"</string>
@@ -1860,14 +1861,14 @@
<string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você está usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não apareçam até você tocar nelas."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar a Economia de dados?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string>
- <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Por um minuto (até {formattedTime})}one{Por # minuto (até {formattedTime})}other{Por # minutos (até {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Por 1min (até {formattedTime})}one{Por #min (até {formattedTime})}other{Por #min (até {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Por 1 hora (até {formattedTime})}one{Por # hora (até {formattedTime})}other{Por # horas (até {formattedTime})}}"</string>
- <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Por 1h (até {formattedTime})}one{Por #h (até {formattedTime})}other{Por #h (até {formattedTime})}}"</string>
- <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Por um minuto}one{Por # minuto}other{Por # minutos}}"</string>
- <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Por 1min}one{Por #min}other{Por #min}}"</string>
- <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Por 1 hora}one{Por # hora}other{Por # horas}}"</string>
- <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Por 1h}one{Por #h}other{Por #h}}"</string>
+ <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Por um minuto (até {formattedTime})}one{Por # minuto (até {formattedTime})}many{Por # minutos (até {formattedTime})}other{Por # minutos (até {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Por 1min (até {formattedTime})}one{Por #min (até {formattedTime})}many{Por #min (até {formattedTime})}other{Por #min (até {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Por 1 hora (até {formattedTime})}one{Por # hora (até {formattedTime})}many{Por # horas (até {formattedTime})}other{Por # horas (até {formattedTime})}}"</string>
+ <string name="zen_mode_duration_hours_summary_short" msgid="687919813833347945">"{count,plural, =1{Por 1h (até {formattedTime})}one{Por #h (até {formattedTime})}many{Por #h (até {formattedTime})}other{Por #h (até {formattedTime})}}"</string>
+ <string name="zen_mode_duration_minutes" msgid="2340007982276569054">"{count,plural, =1{Por um minuto}one{Por # minuto}many{Por # minutos}other{Por # minutos}}"</string>
+ <string name="zen_mode_duration_minutes_short" msgid="2435756450204526554">"{count,plural, =1{Por 1min}one{Por #min}many{Por #min}other{Por #min}}"</string>
+ <string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{Por 1 hora}one{Por # hora}many{Por # horas}other{Por # horas}}"</string>
+ <string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{Por 1h}one{Por #h}many{Por #h}other{Por #h}}"</string>
<string name="zen_mode_until_next_day" msgid="1403042784161725038">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
@@ -2000,7 +2001,7 @@
<string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Salvar no Preenchimento automático"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Não é possível preencher os conteúdos automaticamente"</string>
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Sem sugestões de preenchimento automático"</string>
- <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Uma sugestão de preenchimento automático}one{# sugestão de preenchimento automático}other{# sugestões de preenchimento automático}}"</string>
+ <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{Uma sugestão de preenchimento automático}one{# sugestão de preenchimento automático}many{# sugestões de preenchimento automático}other{# sugestões de preenchimento automático}}"</string>
<string name="autofill_save_title" msgid="7719802414283739775">"Salvar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_type" msgid="3002460014579799605">"Salvar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Salvar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
@@ -2110,7 +2111,7 @@
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"Apresentação em <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"O Bluetooth permanecerá ativado no modo avião"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"Carregando"</string>
- <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # arquivo}one{{file_name} + # arquivo}other{{file_name} + # arquivos}}"</string>
+ <string name="file_count" msgid="3220018595056126969">"{count,plural, =1{{file_name} + # arquivo}one{{file_name} + # arquivo}many{{file_name} + # arquivos}other{{file_name} + # arquivos}}"</string>
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Não há sugestões de pessoas para compartilhar"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Lista de apps"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Este app não tem permissão de gravação, mas pode capturar áudio pelo dispositivo USB."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 1e050e1ce7c5..142b278d608f 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -606,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operațiunea privind amprenta a fost anulată."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operațiunea privind amprenta a fost anulată de utilizator."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Prea multe încercări. Încercați din nou mai târziu."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Prea multe încercări. Folosește blocarea ecranului."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Încercați din nou."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nu au fost înregistrate amprente."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string>
@@ -636,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vizitați un furnizor de servicii de reparații."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nu se poate crea modelul facial. Reîncercați."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Prea luminos. Încercați o lumină mai slabă."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Încercați o lumină mai puternică"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Mutați telefonul mai departe"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Mutați telefonul mai aproape"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mutați telefonul mai sus"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0f21c9002da5..7b47179591ac 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -607,8 +607,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Операция с отпечатком отменена."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Операция с отпечатком пальца отменена пользователем."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Слишком много попыток. Повторите позже."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Слишком много попыток. Используйте другой способ разблокировки экрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Повторите попытку."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Нет отсканированных отпечатков пальцев"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На этом устройстве нет сканера отпечатков пальцев."</string>
@@ -637,7 +636,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Обратитесь в сервисный центр."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Невозможно создать модель лица. Повторите попытку."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Слишком светло. Сделайте освещение менее ярким."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Сделайте освещение ярче"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Переместите телефон дальше от лица"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Переместите телефон ближе к лицу"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Переместите телефон выше"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index e96e0e4ed9c6..f1f812768017 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ඇඟිලි සලකුණු මෙහෙයුම අවලංගු කරන ලදී."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"පරිශීලක විසින් ඇඟිලි සලකුණු මෙහෙයුම අවසන් කරන ලදී."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"උත්සාහයන් ඉතා වැඩි ගණනකි. කරුණාකර පසුව නැවත උත්සාහ කරන්න."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"උත්සාහ ගණන ඉතා වැඩියි. ඒ වෙනුවට තිර අගුල භාවිත කරන්න."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ඇඟිලි සලකුණු ඇතුළත් කර නොමැත."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"අළුත්වැඩියා සැපයුම්කරුවෙකු බලන්න."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ඔබගේ මුහුණු ආකෘතිය තැනිය නොහැකිය. නැවත උත්සාහ කරන්න."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"දීප්තිය වැඩියි. තවත් මඳ ආලෝකය උත්සාහ කරන්න."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"තවත් දීප්තිමත් ආලෝකය උත්සාහ කරන්න"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"දුරකථනය තවත් ඈතට ගෙන යන්න"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"දුරකථනය තවත් සමීපයට ගෙන එන්න"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"දුරකථනය තවත් ඉහළට ගෙන යන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 566f448414e8..ce3137c78d29 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -607,8 +607,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operácia týkajúca sa odtlačku prsta bola zrušená"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Overenie odtlačku prsta zrušil používateľ."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Príliš veľa pokusov. Skúste to neskôr."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Príliš veľa pokusov. Použite radšej zámku obrazovky."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Skúste to znova"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neregistrovali ste žiadne odtlačky prstov."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zariadenie nemá senzor odtlačkov prstov."</string>
@@ -637,7 +636,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Navštívte poskytovateľa opráv."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Model tváre sa nedá vytvoriť. Skúste to znova."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Príliš veľa svetla. Skúste jemnejšie osvetlenie."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Skúste lepšie osvetlenie"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Oddiaľte telefón"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Priblížte telefón"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Posuňte telefón vyššie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index af74ebaa7188..3a56d6d974c8 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -607,8 +607,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Dejanje s prstnim odtisom je bilo preklicano."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Dejanje s prstnim odtisom je preklical uporabnik."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Preveč poskusov. Poskusite znova pozneje."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Preveč poskusov. Odklenite z zaklepanjem zaslona."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Poskusite znova."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ni registriranih prstnih odtisov."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ta naprava nima tipala prstnih odtisov."</string>
@@ -637,7 +636,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Obiščite ponudnika popravil."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Modela obraza ni mogoče ustvariti. Poskusite znova."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Presvetlo. Poskusite z blažjo osvetlitvijo."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Poskusite z močnejšo osvetlitvijo."</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Telefon nekoliko odmaknite."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Bolj približajte telefon."</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Telefon premaknite višje."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index c359aad622bd..ab7c68bd576f 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operacioni i gjurmës së gishtit u anulua."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Veprimi i gjurmës së gishtit u anulua nga përdoruesi."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Keni bërë shumë tentativa. Provo përsëri më vonë."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Shumë përpjekje. Përdor më mirë kyçjen e ekranit."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Provo përsëri."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nuk ka asnjë gjurmë gishti të regjistruar."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vizito një ofrues të shërbimit të riparimit."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Modeli i fytyrës nuk krijohet. Provo sërish."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Me shumë ndriçim. Provo një ndriçim më të butë."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Provo një ndriçim më të fortë"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Lëvize telefonin më larg"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Lëvize telefonin më afër"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Lëvize telefonin më lart"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 6d29046be1b4..6220219027be 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -606,8 +606,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Радња са отиском прста је отказана."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Корисник је отказао радњу са отиском прста."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Превише покушаја. Пробајте поново касније."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Превише покушаја. Користите закључавање екрана уместо тога."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Пробајте поново."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Није регистрован ниједан отисак прста."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Овај уређај нема сензор за отисак прста."</string>
@@ -636,7 +635,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Посетите добављача за поправке."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Прављење модела лица није успело. Пробајте поново."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Превише је светло. Пробајте са слабијим осветљењем."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Пробајте са јачим осветљењем"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Удаљите телефон"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Приближите телефон"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Померите телефон нагоре"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 1ee319298249..26a496d89e41 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrycksåtgärden avbröts."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrycksåtgärden avbröts av användaren."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Du har gjort för många försök. Försök igen senare."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"För många försök. Använd låsskärmen i stället."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Försök igen."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Inga fingeravtryck har registrerats."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Besök ett reparationsställe."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Ansiktsmodellen kunde inte skapas. Försök igen."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Det är för ljust. Testa lägre belysning."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Testa med bättre belysning"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Flytta telefonen längre bort"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"För telefonen närmare"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Höj telefonen"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8659b6a630c5..09083ea3f9ed 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Mchakato wa alama ya kidole umeghairiwa."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Mtumiaji ameghairi uthibitishaji wa alama ya kidole."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Majaribio mengi mno. Jaribu tena baadaye."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Umejaribu mara nyingi mno. Badala yake, tumia mbinu ya kufunga skrini."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Jaribu tena."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hakuna alama za vidole zilizojumuishwa."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kifaa hiki hakina kitambua alama ya kidole."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Tembelea mtoa huduma za urekebishaji."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Imeshindwa kuunda muundo wa uso wako. Jaribu tena."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Inang\'aa mno. Jaribu mwangaza hafifu"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Jaribu kuongeza mwangaza"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Sogeza simu mbali kiasi"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Sogeza simu karibu"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Sogeza simu juu zaidi"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 611eadf032ab..ad493821985c 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"கைரேகை செயல்பாடு ரத்துசெய்யப்பட்டது."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"பயனர், கைரேகை உறுதிப்படுத்துதலை ரத்துசெய்தார்."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"அதிகமான முயற்சிகள். பிறகு முயற்சிக்கவும்."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"பலமுறை முயன்றுவிட்டீர்கள். இதற்குப் பதிலாகத் திரைப்பூட்டைப் பயன்படுத்தவும்."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"மீண்டும் முயற்சிக்கவும்."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"கைரேகைப் பதிவுகள் எதுவும் இல்லை."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"பழுதுபார்ப்புச் சேவை வழங்குநரைத் தொடர்புகொள்ளவும்."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"முகத் தோற்றம் பதிவாகவில்லை. மீண்டும் முயலவும்."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"அதிக ஒளிர்வு. மிதமான ஒளியில் முயலவும்."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"பிரகாசமான ஒளியில் முயலவும்"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"மொபைலை முகத்தில் இருந்து தள்ளிப் பிடிக்கவும்"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"மொபைலை அருகில் நகர்த்தவும்"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"மொபைலை மேலே நகர்த்தவும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index a56a6287754c..fe0efad371bc 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"వేలిముద్ర యాక్టివిటీ రద్దయింది."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"వేలిముద్ర చర్యని వినియోగదారు రద్దు చేశారు."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"చాలా ఎక్కువ ప్రయత్నాలు చేశారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్‌ను ఉపయోగించండి."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"వేలిముద్రలు నమోదు చేయబడలేదు."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ ఎంపిక లేదు."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"రిపెయిర్ ప్రొవైడర్‌ను సందర్శించండి."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"మీ ఫేస్‌మోడల్ క్రియేషన్ కుదరదు. మళ్లీ ట్రై చేయండి."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"వెలుతురు అధికంగా ఉంది. తక్కువ ఉండేలా చూడండి."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"ప్రకాశవంతమైన లైటింగ్‌లో ట్రై చేయండి"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ఫోన్‌ను కాస్త దూరంగా జరపండి"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ఫోన్‌ను దగ్గరగా పట్టుకోండి"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ఫోన్‌ను పైకి పట్టుకోండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index a51876c3d309..c60db37d50b6 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ยกเลิกการทำงานของลายนิ้วมือ"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ผู้ใช้ยกเลิกการทำงานของลายนิ้วมือ"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"ดำเนินการหลายครั้งเกินไป ลองอีกครั้งในภายหลัง"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ลองหลายครั้งเกินไป ใช้การล็อกหน้าจอแทน"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ลองอีกครั้ง"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ไม่มีลายนิ้วมือที่ลงทะเบียน"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"โปรดติดต่อผู้ให้บริการซ่อม"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"สร้างรูปแบบใบหน้าไม่ได้ โปรดลองอีกครั้ง"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"สว่างเกินไป ลองหาตำแหน่งที่แสงน้อยกว่านี้"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"ลองหาตำแหน่งที่สว่างขึ้น"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"ถือโทรศัพท์ให้ห่างกว่านี้"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ถือโทรศัพท์ให้ใกล้กว่านี้"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ยกโทรศัพท์ให้สูงขึ้น"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index faacade6a268..4f8e7a3e3a8a 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Nakansela ang operasyong ginagamitan ng fingerprint."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Kinansela ng user ang operasyon sa fingerprint."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Napakaraming pagtatangka. Subukan ulit sa ibang pagkakataon."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Masyadong maraming pagsubok. Gamitin na lang ang lock ng screen."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Subukang muli."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Walang naka-enroll na fingerprint."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Walang sensor ng fingerprint ang device na ito."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Bumisita sa provider ng pag-aayos."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Hindi magawa ang iyong face model. Subukan ulit."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Masyadong maliwanag. Subukang bawasan ang liwanag."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Subukan sa mas maliwanag"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Ilayo pa ang telepono"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Ilapit pa ang telepono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Itaas pa ang telepono"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index af503ca07ced..d2c795823982 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Parmak izi işlemi iptal edildi."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Parmak izi işlemi kullanıcı tarafından iptal edildi."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Çok fazla deneme yapıldı. Daha sonra tekrar deneyin."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Çok fazla deneme yapıldı. Bunun yerine ekran kilidini kullanın."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tekrar deneyin."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Parmak izi kaydedilmedi."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda parmak izi sensörü yok."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Bir onarım hizmeti sağlayıcıyı ziyaret edin."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Yüzünüzün modeli oluşturulamıyor. Tekrar deneyin."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Çok parlak. Parlaklığı daha az bir ışıklandırma deneyin."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Daha parlak ışıkta deneyin"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Telefonu uzaklaştırın"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Telefonu yaklaştırın"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Telefonu daha yukarı kaldırın"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index bac234ad72bf..9223d9a66637 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -607,8 +607,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Дію з відбитком пальця скасовано."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Користувач скасував дію з відбитком пальця."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Забагато спроб. Спробуйте пізніше."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Забагато спроб. Використайте натомість розблокування екрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Повторіть спробу."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Відбитки пальців не зареєстровано."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На цьому пристрої немає сканера відбитків пальців."</string>
@@ -637,7 +636,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Зверніться до постачальника послуг із ремонту."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Модель обличчя не створено. Повторіть спробу."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Занадто яскраво. Потрібно менше світла."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Потрібно більше світла"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Тримайте телефон далі від обличчя"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Тримайте телефон ближче до обличчя"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Підніміть телефон вище"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 7db1619f9ab1..5c6c5555d36a 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"فنگر پرنٹ کی کارروائی منسوخ ہوگئی۔"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"صارف نے فنگر پرنٹ کی کارروائی منسوخ کر دی۔"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"کافی زیادہ کوششیں کی گئیں۔ بعد میں دوبارہ کوشش کریں۔"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"کافی زیادہ کوششیں۔ اس کے بجائے اسکرین لاک کا استعمال کریں۔"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"کوئی فنگر پرنٹ مندرج شدہ نہیں ہے۔"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے۔"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ایک مرمت فراہم کنندہ کو ملاحظہ کریں۔"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"آپکے چہرے کا ماڈل تخلیق نہیں ہو سکا۔ پھر کوشش کریں۔"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"کافی روشنی ہے۔ ہلکی روشنی میں آزمائیں۔"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"تیز روشنی میں آزمائیں"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"فون کو تھوڑا دور کریں"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"فون کو تھوڑا قریب کریں"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"فون کو تھوڑا اوپر لے جائیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 8f4cceb1c3ee..99e2a5759bc0 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Barmoq izi tekshiruvi bekor qilindi."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Barmoq izi amali foydalanuvchi tomonidan bekor qilindi"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Urinishlar soni ko‘payib ketdi. Keyinroq qayta urinib ko‘ring."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Juda koʻp urinildi. Ekran qulfi orqali urining."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Qayta urinib ko‘ring."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hech qanday barmoq izi qayd qilinmagan."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu qurilmada barmoq izi skaneri mavjud emas."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Xizmat koʻrsatish markaziga murojaat qiling."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Yuzingiz modeli yaratilmadi. Qayta urining."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Juda yorqin. Biroz soyaroq joy tanlang."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Atrofingizni yanada yoriting"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Telefonni biroz uzoqroq tuting"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Telefonni yaqinroq tuting"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Telefonni teparoq tuting"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 40eb56d6a914..965e520ba417 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Thao tác dùng dấu vân tay bị hủy."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Người dùng đã hủy thao tác dùng dấu vân tay."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Quá nhiều lần thử. Hãy thử lại sau."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Bạn đã thử quá nhiều lần. Hãy dùng phương thức khoá màn hình."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Thử lại."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Chưa đăng ký vân tay."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Thiết bị này không có cảm biến vân tay."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Hãy liên hệ với một nhà cung cấp dịch vụ sửa chữa."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Không thể tạo mẫu khuôn mặt của bạn. Hãy thử lại."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Quá sáng. Hãy thử giảm độ sáng."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Hãy thử tăng độ sáng"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Đưa điện thoại ra xa hơn"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Đưa điện thoại lại gần hơn"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Nâng điện thoại lên cao hơn"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 066600b96d7c..02f0ac81b67f 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指纹操作已取消。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"用户取消了指纹操作。"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"尝试次数过多,请稍后重试。"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"尝试次数过多,请通过屏幕锁定功能解锁。"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"请重试。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未注册任何指纹。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此设备没有指纹传感器。"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"请联系维修服务提供商。"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"无法创建您的脸部模型,请重试。"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"亮度过高,请尝试使用较柔和的亮度。"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"请尝试调亮光线"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"请将手机拿远一点"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"请将手机拿近一点"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"请将手机举高一点"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index a1c0921f7ddf..40e313c24cfd 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋操作已取消。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"使用者已取消指紋操作。"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"嘗試次數過多,請稍後再試。"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"再試一次。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未註冊任何指紋"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此裝置沒有指紋感應器。"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"請諮詢維修服務供應商。"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"無法建立面部模型,請再試一次。"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"影像太亮。請嘗試在更暗的環境下使用。"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"請試用更充足的光線"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"請將手機移開一點"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"請將手機移近一點"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"請將手機向上移"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index cb8f7bf18415..147b38bc57d7 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋作業已取消。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"使用者已取消指紋驗證作業。"</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"嘗試次數過多,請稍後再試。"</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"請再試一次。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未登錄任何指紋。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"這個裝置沒有指紋感應器。"</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"請洽詢維修供應商。"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"無法建立臉部模型,請再試一次。"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"亮度過高,請嘗試使用較柔和的照明方式。"</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"請採用更明亮的光源"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"請將手機拿遠一點"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"請將手機拿近一點"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"請將手機舉高一點"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 5ae40dc1babe..723e0b2900b4 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -605,8 +605,7 @@
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Ukusebenza kwezigxivizo zeminwe kukhanseliwe."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Umsebenzi wezigxivizo zomunwe ukhanselwe umsebenzisi."</string>
<string name="fingerprint_error_lockout" msgid="7853461265604738671">"Imizamo eminingi kakhulu. Zama futhi emuva kwesikhathi."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Imizamo eminingi kakhulu. Sebenzisa ukukhiya isikrini kunalokho."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Zama futhi."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Azikho izigxivizo zeminwe ezibhalisiwe."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Le divayisi ayinayo inzwa yezigxivizo zeminwe."</string>
@@ -635,7 +634,8 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vakashela umhlinzeki wokulungisa."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Ayikwazi ukusungula imodeli yobuso bakho. Zama futhi."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Kukhanya kakhulu. Zama ukukhanya okuthambile."</string>
- <string name="face_acquired_too_dark" msgid="7919016380747701228">"Zama ukukhanyisa okukhudlwana"</string>
+ <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
+ <skip />
<string name="face_acquired_too_close" msgid="4453646176196302462">"Yisa ifoni kude"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Sondeza ifoni eduze"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Yisa ifoni phezulu"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index c7153fcf8609..69c504364544 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4509,6 +4509,8 @@
</attr>
</declare-styleable>
<declare-styleable name="EditText">
+ <!-- Enables styling shortcuts, e.g. Ctrl+B for bold. This is off by default. -->
+ <attr name="enableTextStylingShortcuts" format="boolean" />
</declare-styleable>
<declare-styleable name="FastScroll">
<!-- Drawable used for the scroll bar thumb. -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 082acbe3443a..4ddbf58757b8 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -70,6 +70,12 @@
<item type="id" name="cut" />
<item type="id" name="copy" />
<item type="id" name="paste" />
+ <!-- Editor action that makes selected text bold. -->
+ <item type="id" name="bold" />
+ <!-- Editor action that makes selected text italic. -->
+ <item type="id" name="italic" />
+ <!-- Editor action that makes selected text underline. -->
+ <item type="id" name="underline" />
<item type="id" name="copyUrl" />
<item type="id" name="selectTextMode" />
<item type="id" name="switchInputMethod" />
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index ad7d03e43d29..89741effb945 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -115,9 +115,13 @@
<public name="handwritingBoundsOffsetRight" />
<public name="handwritingBoundsOffsetBottom" />
<public name="accessibilityDataPrivate" />
+ <public name="enableTextStylingShortcuts" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01cd0000">
+ <public name="bold" />
+ <public name="italic" />
+ <public name="underline" />
</staging-public-group>
<staging-public-group type="style" first-id="0x01cc0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8488b6865440..ef33a22dacc7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1797,7 +1797,7 @@
<!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] -->
<string name="face_acquired_too_bright">Too bright. Try gentler lighting.</string>
<!-- Message shown during face acquisition when the image is too dark [CHAR LIMIT=50] -->
- <string name="face_acquired_too_dark">Try brighter lighting</string>
+ <string name="face_acquired_too_dark">Not enough light</string>
<!-- Message shown during face acquisition when the user is too close to sensor [CHAR LIMIT=50] -->
<string name="face_acquired_too_close">Move phone farther away</string>
<!-- Message shown during face acquisition when the user is too far from sensor [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e5db96a034f7..069bfe78fa05 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4288,6 +4288,7 @@
<java-symbol type="id" name="conversation_icon_badge_ring" />
<java-symbol type="id" name="conversation_icon_badge_bg" />
<java-symbol type="id" name="expand_button_container" />
+ <java-symbol type="id" name="expand_button_a11y_container" />
<java-symbol type="id" name="expand_button_touch_container" />
<java-symbol type="id" name="messaging_group_content_container" />
<java-symbol type="id" name="expand_button_and_content_container" />
diff --git a/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
index 2a3da05eabb3..625c66a4c60e 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
@@ -17,9 +17,11 @@
package android.app;
import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
import android.os.Parcel;
import android.test.AndroidTestCase;
+import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -70,4 +72,18 @@ public class NotificationChannelGroupTest {
assertEquals(NotificationChannelGroup.MAX_TEXT_LENGTH,
fromParcel.getDescription().length());
}
+
+ @Test
+ public void testNullableFields() {
+ NotificationChannelGroup group = new NotificationChannelGroup("my_group_01", null);
+
+ Parcel parcel = Parcel.obtain();
+ group.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ NotificationChannelGroup fromParcel =
+ NotificationChannelGroup.CREATOR.createFromParcel(parcel);
+ assertEquals(group.getId(), fromParcel.getId());
+ assertTrue(TextUtils.isEmpty(fromParcel.getName()));
+ }
}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index a2d4bafef41c..401193357f6b 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -33,6 +33,7 @@ import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
+import android.app.Application;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.app.ResourcesManager;
@@ -46,6 +47,7 @@ import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
import android.content.Context;
import android.content.Intent;
+import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -178,6 +180,85 @@ public class ActivityThreadTest {
}
@Test
+ public void testOverrideScale() throws Exception {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+ final Application app = activity.getApplication();
+ final ActivityThread activityThread = activity.getActivityThread();
+ final IApplicationThread appThread = activityThread.getApplicationThread();
+ final DisplayMetrics originalAppMetrics = new DisplayMetrics();
+ originalAppMetrics.setTo(app.getResources().getDisplayMetrics());
+ final Configuration originalAppConfig =
+ new Configuration(app.getResources().getConfiguration());
+ final DisplayMetrics originalActivityMetrics = new DisplayMetrics();
+ originalActivityMetrics.setTo(activity.getResources().getDisplayMetrics());
+ final Configuration originalActivityConfig =
+ new Configuration(activity.getResources().getConfiguration());
+
+ final Configuration newConfig = new Configuration(originalAppConfig);
+ newConfig.seq = BASE_SEQ + 1;
+ newConfig.smallestScreenWidthDp++;
+
+ final float originalScale = CompatibilityInfo.getOverrideInvertedScale();
+ float scale = 0.5f;
+ CompatibilityInfo.setOverrideInvertedScale(scale);
+ try {
+ // Send process level config change.
+ ClientTransaction transaction = newTransaction(activityThread, null);
+ transaction.addCallback(ConfigurationChangeItem.obtain(new Configuration(newConfig)));
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertScreenScale(scale, app, originalAppConfig, originalAppMetrics);
+ // The activity's config doesn't change because ConfigurationChangeItem is process level
+ // that won't affect activity's override config.
+ assertEquals(originalActivityConfig.densityDpi,
+ activity.getResources().getConfiguration().densityDpi);
+
+ scale = 0.8f;
+ CompatibilityInfo.setOverrideInvertedScale(scale);
+ // Send activity level config change.
+ newConfig.seq++;
+ newConfig.smallestScreenWidthDp++;
+ transaction = newTransaction(activityThread, activity.getActivityToken());
+ transaction.addCallback(ActivityConfigurationChangeItem.obtain(
+ new Configuration(newConfig)));
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertScreenScale(scale, activity, originalActivityConfig, originalActivityMetrics);
+ } finally {
+ CompatibilityInfo.setOverrideInvertedScale(originalScale);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> restoreConfig(activityThread, originalAppConfig));
+ }
+ assertScreenScale(originalScale, app, originalAppConfig, originalAppMetrics);
+ }
+
+ private static void assertScreenScale(float scale, Context context,
+ Configuration origConfig, DisplayMetrics origMetrics) {
+ final int expectedDpi = (int) (origConfig.densityDpi * scale + .5f);
+ final float expectedDensity = origMetrics.density * scale;
+ final int expectedWidthPixels = (int) (origMetrics.widthPixels * scale + .5f);
+ final int expectedHeightPixels = (int) (origMetrics.heightPixels * scale + .5f);
+ final Configuration expectedConfig = new Configuration(origConfig);
+ CompatibilityInfo.scaleConfiguration(scale, expectedConfig);
+ final Rect expectedBounds = expectedConfig.windowConfiguration.getBounds();
+ final Rect expectedAppBounds = expectedConfig.windowConfiguration.getAppBounds();
+ final Rect expectedMaxBounds = expectedConfig.windowConfiguration.getMaxBounds();
+
+ final Configuration currentConfig = context.getResources().getConfiguration();
+ final DisplayMetrics currentMetrics = context.getResources().getDisplayMetrics();
+ assertEquals(expectedDpi, currentConfig.densityDpi);
+ assertEquals(expectedDpi, currentMetrics.densityDpi);
+ assertEquals(expectedDensity, currentMetrics.density, 0.001f);
+ assertEquals(expectedWidthPixels, currentMetrics.widthPixels);
+ assertEquals(expectedHeightPixels, currentMetrics.heightPixels);
+ assertEquals(expectedBounds, currentConfig.windowConfiguration.getBounds());
+ assertEquals(expectedAppBounds, currentConfig.windowConfiguration.getAppBounds());
+ assertEquals(expectedMaxBounds, currentConfig.windowConfiguration.getMaxBounds());
+ }
+
+ @Test
public void testHandleActivityConfigurationChanged() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
@@ -459,19 +540,17 @@ public class ActivityThreadTest {
} finally {
// Make sure to reset the process config to prevent side effects to other
// tests.
- Configuration activityThreadConfig = activityThread.getConfiguration();
- activityThreadConfig.seq = originalAppConfig.seq - 1;
-
- Configuration resourceManagerConfig = ResourcesManager.getInstance()
- .getConfiguration();
- resourceManagerConfig.seq = originalAppConfig.seq - 1;
-
- activityThread.updatePendingConfiguration(originalAppConfig);
- activityThread.handleConfigurationChanged(originalAppConfig);
+ restoreConfig(activityThread, originalAppConfig);
}
});
}
+ private static void restoreConfig(ActivityThread thread, Configuration originalConfig) {
+ thread.getConfiguration().seq = originalConfig.seq - 1;
+ ResourcesManager.getInstance().getConfiguration().seq = originalConfig.seq - 1;
+ thread.handleConfigurationChanged(originalConfig);
+ }
+
@Test
public void testActivityOrientationChanged_DoesntOverrideVirtualDisplayOrientation() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceBatteryListenerTest.kt b/core/tests/coretests/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
new file mode 100644
index 000000000000..e3b3ea7492c5
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.input
+
+import android.hardware.BatteryState
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import com.android.server.testutils.any
+import java.util.concurrent.Executor
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+import kotlin.test.fail
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.doAnswer
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoJUnitRunner
+
+/**
+ * Tests for [InputManager.InputDeviceBatteryListener].
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:InputDeviceBatteryListenerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class InputDeviceBatteryListenerTest {
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ private lateinit var testLooper: TestLooper
+ private var registeredListener: IInputDeviceBatteryListener? = null
+ private val monitoredDevices = mutableListOf<Int>()
+ private lateinit var executor: Executor
+ private lateinit var inputManager: InputManager
+
+ @Mock
+ private lateinit var iInputManagerMock: IInputManager
+
+ @Before
+ fun setUp() {
+ testLooper = TestLooper()
+ executor = HandlerExecutor(Handler(testLooper.looper))
+ registeredListener = null
+ monitoredDevices.clear()
+ inputManager = InputManager.resetInstance(iInputManagerMock)
+
+ // Handle battery listener registration.
+ doAnswer {
+ val deviceId = it.getArgument(0) as Int
+ val listener = it.getArgument(1) as IInputDeviceBatteryListener
+ if (registeredListener != null &&
+ registeredListener!!.asBinder() != listener.asBinder()) {
+ // There can only be one registered battery listener per process.
+ fail("Trying to register a new listener when one already exists")
+ }
+ if (monitoredDevices.contains(deviceId)) {
+ fail("Trying to start monitoring a device that was already being monitored")
+ }
+ monitoredDevices.add(deviceId)
+ registeredListener = listener
+ null
+ }.`when`(iInputManagerMock).registerBatteryListener(anyInt(), any())
+
+ // Handle battery listener being unregistered.
+ doAnswer {
+ val deviceId = it.getArgument(0) as Int
+ val listener = it.getArgument(1) as IInputDeviceBatteryListener
+ if (registeredListener == null ||
+ registeredListener!!.asBinder() != listener.asBinder()) {
+ fail("Trying to unregister a listener that is not registered")
+ }
+ if (!monitoredDevices.remove(deviceId)) {
+ fail("Trying to stop monitoring a device that is not being monitored")
+ }
+ if (monitoredDevices.isEmpty()) {
+ registeredListener = null
+ }
+ }.`when`(iInputManagerMock).unregisterBatteryListener(anyInt(), any())
+ }
+
+ @After
+ fun tearDown() {
+ InputManager.clearInstance()
+ }
+
+ private fun notifyBatteryStateChanged(
+ deviceId: Int,
+ isPresent: Boolean = true,
+ status: Int = BatteryState.STATUS_FULL,
+ capacity: Float = 1.0f,
+ eventTime: Long = 12345L
+ ) {
+ registeredListener!!.onBatteryStateChanged(deviceId, isPresent, status, capacity, eventTime)
+ }
+
+ @Test
+ fun testListenerIsNotifiedCorrectly() {
+ var callbackCount = 0
+
+ // Add a battery listener to monitor battery changes.
+ inputManager.addInputDeviceBatteryListener(1 /*deviceId*/, executor) {
+ deviceId: Int, eventTime: Long, batteryState: BatteryState ->
+ callbackCount++
+ assertEquals(1, deviceId)
+ assertEquals(true, batteryState.isPresent)
+ assertEquals(BatteryState.STATUS_DISCHARGING, batteryState.status)
+ assertEquals(0.5f, batteryState.capacity)
+ assertEquals(8675309L, eventTime)
+ }
+
+ // Adding the listener should register the callback with InputManagerService.
+ assertNotNull(registeredListener)
+ assertTrue(monitoredDevices.contains(1))
+
+ // Notifying battery change for a different device should not trigger the listener.
+ notifyBatteryStateChanged(deviceId = 2)
+ testLooper.dispatchAll()
+ assertEquals(0, callbackCount)
+
+ // Notifying battery change for the registered device will notify the listener.
+ notifyBatteryStateChanged(1 /*deviceId*/, true /*isPresent*/,
+ BatteryState.STATUS_DISCHARGING, 0.5f /*capacity*/, 8675309L /*eventTime*/)
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount)
+ }
+
+ @Test
+ fun testMultipleListeners() {
+ // Set up two callbacks.
+ var callbackCount1 = 0
+ var callbackCount2 = 0
+ val callback1 = InputManager.InputDeviceBatteryListener { _, _, _ -> callbackCount1++ }
+ val callback2 = InputManager.InputDeviceBatteryListener { _, _, _ -> callbackCount2++ }
+
+ // Monitor battery changes for three devices. The first callback monitors devices 1 and 3,
+ // while the second callback monitors devices 2 and 3.
+ inputManager.addInputDeviceBatteryListener(1 /*deviceId*/, executor, callback1)
+ assertEquals(1, monitoredDevices.size)
+ inputManager.addInputDeviceBatteryListener(2 /*deviceId*/, executor, callback2)
+ assertEquals(2, monitoredDevices.size)
+ inputManager.addInputDeviceBatteryListener(3 /*deviceId*/, executor, callback1)
+ assertEquals(3, monitoredDevices.size)
+ inputManager.addInputDeviceBatteryListener(3 /*deviceId*/, executor, callback2)
+ assertEquals(3, monitoredDevices.size)
+
+ // Notifying battery change for each of the devices should trigger the registered callbacks.
+ notifyBatteryStateChanged(deviceId = 1)
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount1)
+ assertEquals(0, callbackCount2)
+
+ notifyBatteryStateChanged(deviceId = 2)
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount1)
+ assertEquals(1, callbackCount2)
+
+ notifyBatteryStateChanged(deviceId = 3)
+ testLooper.dispatchNext()
+ testLooper.dispatchNext()
+ assertEquals(2, callbackCount1)
+ assertEquals(2, callbackCount2)
+
+ // Stop monitoring devices 1 and 2.
+ inputManager.removeInputDeviceBatteryListener(1 /*deviceId*/, callback1)
+ assertEquals(2, monitoredDevices.size)
+ inputManager.removeInputDeviceBatteryListener(2 /*deviceId*/, callback2)
+ assertEquals(1, monitoredDevices.size)
+
+ // Ensure device 3 continues to be monitored.
+ notifyBatteryStateChanged(deviceId = 3)
+ testLooper.dispatchNext()
+ testLooper.dispatchNext()
+ assertEquals(3, callbackCount1)
+ assertEquals(3, callbackCount2)
+
+ // Stop monitoring all devices.
+ inputManager.removeInputDeviceBatteryListener(3 /*deviceId*/, callback1)
+ assertEquals(1, monitoredDevices.size)
+ inputManager.removeInputDeviceBatteryListener(3 /*deviceId*/, callback2)
+ assertEquals(0, monitoredDevices.size)
+ }
+
+ @Test
+ fun testAdditionalListenersNotifiedImmediately() {
+ var callbackCount1 = 0
+ var callbackCount2 = 0
+ val callback1 = InputManager.InputDeviceBatteryListener { _, _, _ -> callbackCount1++ }
+ val callback2 = InputManager.InputDeviceBatteryListener { _, _, _ -> callbackCount2++ }
+
+ // Add a battery listener and send the latest battery state.
+ inputManager.addInputDeviceBatteryListener(1 /*deviceId*/, executor, callback1)
+ assertEquals(1, monitoredDevices.size)
+ notifyBatteryStateChanged(deviceId = 1)
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount1)
+
+ // Add a second listener for the same device that already has the latest battery state.
+ inputManager.addInputDeviceBatteryListener(1 /*deviceId*/, executor, callback2)
+ assertEquals(1, monitoredDevices.size)
+
+ // Ensure that this listener is notified immediately.
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount2)
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
index e13a3323d31a..3ecc7ff17114 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -110,11 +110,10 @@ public class InputDeviceLightsManagerTest {
}
private InputDevice createInputDevice(int id) {
- return new InputDevice(id, 0 /* generation */, 0 /* controllerNumber */, "name",
- 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
- 0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */,
- InputDeviceCountryCode.INVALID, false /* hasVibrator */, false /* hasMicrophone */,
- false /* hasButtonUnderpad */, false /* hasSensor */, false /* hasBattery */);
+ return new InputDevice.Builder()
+ .setId(id)
+ .setName("Test Device " + id)
+ .build();
}
private void mockLights(Light[] lights) throws Exception {
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
index 54ee4349c3e1..6cf2314fea45 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java
@@ -35,7 +35,6 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.view.InputDevice;
@@ -73,10 +72,7 @@ public class InputDeviceSensorManagerTest {
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
- private TestLooper mTestLooper;
- private ContextWrapper mContextSpy;
private InputManager mInputManager;
- private InputDeviceSensorManager mSensorManager;
private IInputSensorEventListener mIInputSensorEventListener;
private final Object mLock = new Object();
@@ -84,11 +80,10 @@ public class InputDeviceSensorManagerTest {
@Before
public void setUp() throws Exception {
- mTestLooper = new TestLooper();
- mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+ final Context context = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
- when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
+ when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
@@ -104,7 +99,7 @@ public class InputDeviceSensorManagerTest {
when(mIInputManagerMock.registerSensorListener(any())).thenReturn(true);
- mInputManager = mContextSpy.getSystemService(InputManager.class);
+ mInputManager = context.getSystemService(InputManager.class);
}
@After
@@ -145,13 +140,11 @@ public class InputDeviceSensorManagerTest {
}
private InputDevice createInputDeviceWithSensor(int id) {
- InputDevice d = new InputDevice(id, 0 /* generation */, 0 /* controllerNumber */, "name",
- 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
- 0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */,
- InputDeviceCountryCode.INVALID, false /* hasVibrator */, false /* hasMicrophone */,
- false /* hasButtonUnderpad */, true /* hasSensor */, false /* hasBattery */);
- assertTrue(d.hasSensor());
- return d;
+ return new InputDevice.Builder()
+ .setId(id)
+ .setName("Test Device " + id)
+ .setHasSensor(true)
+ .build();
}
private InputSensorInfo createInputSensorInfo(int id, int type) {
@@ -164,8 +157,8 @@ public class InputDeviceSensorManagerTest {
}
private InputDevice getSensorDevice(int[] deviceIds) {
- for (int i = 0; i < deviceIds.length; i++) {
- InputDevice device = mInputManager.getInputDevice(deviceIds[i]);
+ for (int deviceId : deviceIds) {
+ InputDevice device = mInputManager.getInputDevice(deviceId);
if (device.hasSensor()) {
return device;
}
@@ -176,7 +169,7 @@ public class InputDeviceSensorManagerTest {
@Test
public void getInputDeviceSensors_withExpectedType() throws Exception {
InputDevice device = getSensorDevice(mInputManager.getInputDeviceIds());
- assertTrue(device != null);
+ assertNotNull(device);
SensorManager sensorManager = device.getSensorManager();
List<Sensor> accelList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
@@ -197,7 +190,7 @@ public class InputDeviceSensorManagerTest {
public void getInputDeviceSensors_withUnexpectedType() throws Exception {
InputDevice device = getSensorDevice(mInputManager.getInputDeviceIds());
- assertTrue(device != null);
+ assertNotNull(device);
SensorManager sensorManager = device.getSensorManager();
List<Sensor> gameRotationList = sensorManager.getSensorList(
@@ -213,7 +206,7 @@ public class InputDeviceSensorManagerTest {
@Test
public void testInputDeviceSensorListener() throws Exception {
InputDevice device = getSensorDevice(mInputManager.getInputDeviceIds());
- assertTrue(device != null);
+ assertNotNull(device);
SensorManager sensorManager = device.getSensorManager();
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
deleted file mode 100644
index f027e0b50c18..000000000000
--- a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
+++ /dev/null
@@ -1,155 +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.internal.app
-
-import android.content.ComponentName
-import android.content.pm.PackageManager
-import android.os.Bundle
-import android.service.chooser.ChooserTarget
-import android.view.View
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.internal.R
-import com.android.internal.app.chooser.SelectableTargetInfo
-import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator
-import com.android.server.testutils.any
-import com.android.server.testutils.mock
-import com.android.server.testutils.whenever
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.doNothing
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-@RunWith(AndroidJUnit4::class)
-class ChooserListAdapterTest {
- private val packageManager = mock<PackageManager> {
- whenever(resolveActivity(any(), anyInt())).thenReturn(mock())
- }
- private val context = InstrumentationRegistry.getInstrumentation().getContext()
- private val resolverListController = mock<ResolverListController>()
- private val chooserListCommunicator = mock<ChooserListAdapter.ChooserListCommunicator> {
- whenever(maxRankedTargets).thenReturn(0)
- }
- private val selectableTargetInfoCommunicator =
- mock<SelectableTargetInfoCommunicator> {
- whenever(targetIntent).thenReturn(mock())
- }
- private val chooserActivityLogger = mock<ChooserActivityLogger>()
-
- private val testSubject = ChooserListAdapter(
- context,
- emptyList(),
- emptyArray(),
- emptyList(),
- false,
- resolverListController,
- chooserListCommunicator,
- selectableTargetInfoCommunicator,
- packageManager,
- chooserActivityLogger
- )
-
- @Test
- fun testDirectShareTargetLoadingIconIsStarted() {
- val view = createView()
- val viewHolder = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolder
- val targetInfo = createSelectableTargetInfo()
- val iconTask = mock<ChooserListAdapter.LoadDirectShareIconTask> {
- doNothing().whenever(this).loadIcon()
- }
- testSubject.setTestLoadDirectShareTaskProvider(
- mock {
- whenever(get()).thenReturn(iconTask)
- }
- )
- testSubject.testViewBind(view, targetInfo, 0)
-
- verify(iconTask, times(1)).loadIcon()
- verify(iconTask, times(1)).setViewHolder(viewHolder)
- }
-
- @Test
- fun testOnlyOneTaskPerTarget() {
- val view = createView()
- val viewHolderOne = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolderOne
- val targetInfo = createSelectableTargetInfo()
- val iconTaskOne = mock<ChooserListAdapter.LoadDirectShareIconTask> {
- doNothing().whenever(this).loadIcon()
- }
- val testTaskProvider = mock<ChooserListAdapter.LoadDirectShareIconTaskProvider> {
- whenever(get()).thenReturn(iconTaskOne)
- }
- testSubject.setTestLoadDirectShareTaskProvider(
- testTaskProvider
- )
- testSubject.testViewBind(view, targetInfo, 0)
-
- val viewHolderTwo = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolderTwo
- whenever(testTaskProvider.get()).thenReturn(mock())
-
- testSubject.testViewBind(view, targetInfo, 0)
-
- verify(iconTaskOne, times(1)).loadIcon()
- verify(iconTaskOne, times(1)).setViewHolder(viewHolderOne)
- verify(iconTaskOne, times(1)).setViewHolder(viewHolderTwo)
- verify(testTaskProvider, times(1)).get()
- }
-
- private fun createSelectableTargetInfo(): SelectableTargetInfo =
- SelectableTargetInfo(
- context,
- null,
- createChooserTarget(),
- 1f,
- selectableTargetInfoCommunicator,
- null
- )
-
- private fun createChooserTarget(): ChooserTarget =
- ChooserTarget(
- "Title",
- null,
- 1f,
- ComponentName("package", "package.Class"),
- Bundle()
- )
-
- private fun createView(): View {
- val view = FrameLayout(context)
- TextView(context).apply {
- id = R.id.text1
- view.addView(this)
- }
- TextView(context).apply {
- id = R.id.text2
- view.addView(this)
- }
- ImageView(context).apply {
- id = R.id.icon
- view.addView(this)
- }
- return view
- }
-}
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index c117816352c7..a186aca4648f 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -246,6 +246,9 @@ key 217 SEARCH
key 224 BRIGHTNESS_DOWN
key 225 BRIGHTNESS_UP
key 226 HEADSETHOOK
+key 228 KEYBOARD_BACKLIGHT_TOGGLE
+key 229 KEYBOARD_BACKLIGHT_DOWN
+key 230 KEYBOARD_BACKLIGHT_UP
key 256 BUTTON_1
key 257 BUTTON_2
@@ -415,6 +418,9 @@ key 583 ASSIST
key usage 0x0c0067 WINDOW
key usage 0x0c006F BRIGHTNESS_UP
key usage 0x0c0070 BRIGHTNESS_DOWN
+key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP
+key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN
+key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE
key usage 0x0c0173 MEDIA_AUDIO_TRACK
key usage 0x0c019C PROFILE_SWITCH
key usage 0x0c01A2 ALL_APPS
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index b5409b7d78af..43679364b443 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -876,8 +876,12 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
pkg = info.getTaskInfo().baseActivity.getPackageName();
}
Rect bounds = info.getTaskInfo().getConfiguration().windowConfiguration.getBounds();
+ boolean running = info.getTaskInfo().isRunning;
+ boolean visible = info.getTaskInfo().isVisible;
+ boolean focused = info.getTaskInfo().isFocused;
pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener
- + " wmMode=" + windowingMode + " pkg=" + pkg + " bounds=" + bounds);
+ + " wmMode=" + windowingMode + " pkg=" + pkg + " bounds=" + bounds
+ + " running=" + running + " visible=" + visible + " focused=" + focused);
}
pw.println();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING b/libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING
new file mode 100644
index 000000000000..8dd1369ecbb2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "ironwood-postsubmit": [
+ {
+ "name": "WMShellFlickerTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.IwTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 99b8885acdef..b5a575499a3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -170,7 +170,8 @@ public class Bubble implements BubbleViewProvider {
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
- int taskId, @Nullable final String locus, Executor mainExecutor) {
+ int taskId, @Nullable final String locus, Executor mainExecutor,
+ final Bubbles.BubbleMetadataFlagListener listener) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
mMetadataShortcutId = shortcutInfo.getId();
@@ -188,11 +189,12 @@ public class Bubble implements BubbleViewProvider {
mShowBubbleUpdateDot = false;
mMainExecutor = mainExecutor;
mTaskId = taskId;
+ mBubbleMetadataFlagListener = listener;
}
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final BubbleEntry entry,
- @Nullable final Bubbles.BubbleMetadataFlagListener listener,
+ final Bubbles.BubbleMetadataFlagListener listener,
final Bubbles.PendingIntentCanceledListener intentCancelListener,
Executor mainExecutor) {
mKey = entry.getKey();
@@ -830,6 +832,7 @@ public class Bubble implements BubbleViewProvider {
pw.print(" desiredHeight: "); pw.println(getDesiredHeightString());
pw.print(" suppressNotif: "); pw.println(shouldSuppressNotification());
pw.print(" autoExpand: "); pw.println(shouldAutoExpand());
+ pw.print(" bubbleMetadataFlagListener null: " + (mBubbleMetadataFlagListener == null));
if (mExpandedView != null) {
mExpandedView.dump(pw);
}
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 d63c25d07485..0dfba3464f23 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
@@ -309,6 +309,7 @@ public class BubbleController implements ConfigurationChangeListener {
protected void onInit() {
mBubbleData.setListener(mBubbleDataListener);
mBubbleData.setSuppressionChangedListener(this::onBubbleMetadataFlagChanged);
+ mDataRepository.setSuppressionChangedListener(this::onBubbleMetadataFlagChanged);
mBubbleData.setPendingIntentCancelledListener(bubble -> {
if (bubble.getBubbleIntent() == null) {
@@ -1336,19 +1337,7 @@ public class BubbleController implements ConfigurationChangeListener {
}
mSysuiProxy.updateNotificationBubbleButton(bubble.getKey());
}
-
}
- mSysuiProxy.getPendingOrActiveEntry(bubble.getKey(), (entry) -> {
- mMainExecutor.execute(() -> {
- if (entry != null) {
- final String groupKey = entry.getStatusBarNotification().getGroupKey();
- if (getBubblesInGroup(groupKey).isEmpty()) {
- // Time to potentially remove the summary
- mSysuiProxy.notifyMaybeCancelSummary(bubble.getKey());
- }
- }
- });
- });
}
mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index c64133f0b668..af31391fec96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -158,7 +158,6 @@ public class BubbleData {
@Nullable
private Listener mListener;
- @Nullable
private Bubbles.BubbleMetadataFlagListener mBubbleMetadataFlagListener;
private Bubbles.PendingIntentCanceledListener mCancelledListener;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index 97560f44fb06..3a5961462c87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -25,6 +25,7 @@ import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LA
import android.content.pm.UserInfo
import android.os.UserHandle
import android.util.Log
+import com.android.wm.shell.bubbles.Bubbles.BubbleMetadataFlagListener
import com.android.wm.shell.bubbles.storage.BubbleEntity
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.bubbles.storage.BubbleVolatileRepository
@@ -47,6 +48,13 @@ internal class BubbleDataRepository(
private val ioScope = CoroutineScope(Dispatchers.IO)
private var job: Job? = null
+ // For use in Bubble construction.
+ private lateinit var bubbleMetadataFlagListener: BubbleMetadataFlagListener
+
+ fun setSuppressionChangedListener(listener: BubbleMetadataFlagListener) {
+ bubbleMetadataFlagListener = listener
+ }
+
/**
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously.
@@ -197,7 +205,8 @@ internal class BubbleDataRepository(
entity.title,
entity.taskId,
entity.locus,
- mainExecutor
+ mainExecutor,
+ bubbleMetadataFlagListener
)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 453b34eb445c..b3104b518440 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -276,8 +276,6 @@ public interface Bubbles {
void notifyInvalidateNotifications(String reason);
- void notifyMaybeCancelSummary(String key);
-
void updateNotificationBubbleButton(String key);
void onStackExpandChanged(boolean shouldExpand);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 47f1e2e18255..96efeeb0c5eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -96,7 +96,8 @@ public class DisplayLayout {
/**
* Different from {@link #equals(Object)}, this method compares the basic geometry properties
- * of two {@link DisplayLayout} objects including width, height, rotation, density and cutout.
+ * of two {@link DisplayLayout} objects including width, height, rotation, density, cutout and
+ * insets.
* @return {@code true} if the given {@link DisplayLayout} is identical geometry wise.
*/
public boolean isSameGeometry(@NonNull DisplayLayout other) {
@@ -104,7 +105,8 @@ public class DisplayLayout {
&& mHeight == other.mHeight
&& mRotation == other.mRotation
&& mDensityDpi == other.mDensityDpi
- && Objects.equals(mCutout, other.mCutout);
+ && Objects.equals(mCutout, other.mCutout)
+ && Objects.equals(mStableInsets, other.mStableInsets);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index b7656dec5d6a..18ce3642335d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -82,7 +82,7 @@ import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.SplitscreenPipMixedHandler;
+import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
import com.android.wm.shell.unfold.UnfoldAnimationController;
@@ -220,13 +220,14 @@ public abstract class WMShellModule {
Context context,
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
+ Optional<RecentTasksController> recentTasksController,
WindowDecorViewModel<?> windowDecorViewModel) {
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
ShellInit init = FreeformComponents.isFreeformEnabled(context)
? shellInit
: null;
- return new FreeformTaskListener<>(init, shellTaskOrganizer,
+ return new FreeformTaskListener<>(init, shellTaskOrganizer, recentTasksController,
windowDecorViewModel);
}
@@ -332,6 +333,7 @@ public abstract class WMShellModule {
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
+ DisplayInsetsController displayInsetsController,
Optional<OneHandedController> oneHandedController,
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create(
@@ -340,7 +342,7 @@ public abstract class WMShellModule {
pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
- oneHandedController, mainExecutor));
+ displayInsetsController, oneHandedController, mainExecutor));
}
@WMSingleton
@@ -483,13 +485,13 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
- static SplitscreenPipMixedHandler provideSplitscreenPipMixedHandler(
+ static DefaultMixedHandler provideDefaultMixedHandler(
ShellInit shellInit,
Optional<SplitScreenController> splitScreenOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
Transitions transitions) {
- return new SplitscreenPipMixedHandler(shellInit, splitScreenOptional,
- pipTouchHandlerOptional, transitions);
+ return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
+ pipTouchHandlerOptional);
}
//
@@ -618,7 +620,7 @@ public abstract class WMShellModule {
@ShellCreateTriggerOverride
@Provides
static Object provideIndependentShellComponentsToCreate(
- SplitscreenPipMixedHandler splitscreenPipMixedHandler,
+ DefaultMixedHandler defaultMixedHandler,
Optional<DesktopModeController> desktopModeController) {
return new Object();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
index 64cec2a270ea..8993d549964c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
@@ -50,7 +50,7 @@ public class DesktopMode {
Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result);
return result != 0;
- } catch (Settings.SettingNotFoundException e) {
+ } catch (Exception e) {
ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 4697a0184eb4..b59fe1818780 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -258,12 +258,12 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
break;
case ACTION_DRAG_ENTERED:
pd.dragLayout.show();
- pd.dragLayout.update(event);
break;
case ACTION_DRAG_LOCATION:
pd.dragLayout.update(event);
break;
case ACTION_DROP: {
+ pd.dragLayout.update(event);
return handleDrop(event, pd);
}
case ACTION_DRAG_EXITED: {
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 8dcdda1895e6..1baac718ee95 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
@@ -28,12 +28,15 @@ import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.io.PrintWriter;
+import java.util.Optional;
/**
* {@link ShellTaskOrganizer.TaskListener} for {@link
@@ -46,6 +49,7 @@ public class FreeformTaskListener<T extends AutoCloseable>
private static final String TAG = "FreeformTaskListener";
private final ShellTaskOrganizer mShellTaskOrganizer;
+ private final Optional<RecentTasksController> mRecentTasksOptional;
private final WindowDecorViewModel<T> mWindowDecorationViewModel;
private final SparseArray<State<T>> mTasks = new SparseArray<>();
@@ -60,9 +64,11 @@ public class FreeformTaskListener<T extends AutoCloseable>
public FreeformTaskListener(
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
+ Optional<RecentTasksController> recentTasksController,
WindowDecorViewModel<T> windowDecorationViewModel) {
mShellTaskOrganizer = shellTaskOrganizer;
mWindowDecorationViewModel = windowDecorationViewModel;
+ mRecentTasksOptional = recentTasksController;
if (shellInit != null) {
shellInit.addInitCallback(this::onInit, this);
}
@@ -83,6 +89,12 @@ public class FreeformTaskListener<T extends AutoCloseable>
mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
t.apply();
}
+
+ if (DesktopMode.IS_SUPPORTED && taskInfo.isVisible) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "Adding active freeform task: #%d", taskInfo.taskId);
+ mRecentTasksOptional.ifPresent(rt -> rt.addActiveFreeformTask(taskInfo.taskId));
+ }
}
private State<T> createOrUpdateTaskState(RunningTaskInfo taskInfo, SurfaceControl leash) {
@@ -111,6 +123,12 @@ public class FreeformTaskListener<T extends AutoCloseable>
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
+ if (DesktopMode.IS_SUPPORTED) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "Removing active freeform task: #%d", taskInfo.taskId);
+ mRecentTasksOptional.ifPresent(rt -> rt.removeActiveFreeformTask(taskInfo.taskId));
+ }
+
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
// Save window decorations of closing tasks so that we can hand them over to the
// transition system if this method happens before the transition. In case where the
@@ -131,6 +149,14 @@ public class FreeformTaskListener<T extends AutoCloseable>
if (state.mWindowDecoration != null) {
mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
}
+
+ if (DesktopMode.IS_SUPPORTED) {
+ if (taskInfo.isVisible) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "Adding active freeform task: #%d", taskInfo.taskId);
+ mRecentTasksOptional.ifPresent(rt -> rt.addActiveFreeformTask(taskInfo.taskId));
+ }
+ }
}
private State<T> updateTaskInfo(RunningTaskInfo taskInfo) {
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 297c79e86ad1..f170e774739f 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
@@ -966,7 +966,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// Re-set the PIP bounds to none.
mPipBoundsState.setBounds(new Rect());
mPipUiEventLoggerLogger.setTaskInfo(null);
- mMainExecutor.executeDelayed(() -> mPipMenuController.detach(), 0);
+ mPipMenuController.detach();
mLeash = null;
if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 6c9a6b64c864..61fafb54d989 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
@@ -49,6 +49,7 @@ import android.os.UserManager;
import android.util.Pair;
import android.util.Size;
import android.view.DisplayInfo;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowManagerGlobal;
import android.window.WindowContainerTransaction;
@@ -64,6 +65,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
@@ -135,6 +137,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private PipTransitionController mPipTransitionController;
private TaskStackListenerImpl mTaskStackListener;
private PipParamsChangedForwarder mPipParamsChangedForwarder;
+ private DisplayInsetsController mDisplayInsetsController;
private Optional<OneHandedController> mOneHandedController;
private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
@@ -338,6 +341,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
+ DisplayInsetsController displayInsetsController,
Optional<OneHandedController> oneHandedController,
ShellExecutor mainExecutor) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
@@ -351,7 +355,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
- oneHandedController, mainExecutor)
+ displayInsetsController, oneHandedController, mainExecutor)
.mImpl;
}
@@ -374,6 +378,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
+ DisplayInsetsController displayInsetsController,
Optional<OneHandedController> oneHandedController,
ShellExecutor mainExecutor
) {
@@ -407,6 +412,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mEnterAnimationDuration = mContext.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
mPipParamsChangedForwarder = pipParamsChangedForwarder;
+ mDisplayInsetsController = displayInsetsController;
shellInit.addInitCallback(this::onInit, this);
}
@@ -549,6 +555,16 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
});
+ mDisplayInsetsController.addInsetsChangedListener(mPipBoundsState.getDisplayId(),
+ new DisplayInsetsController.OnInsetsChangedListener() {
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ onDisplayChanged(
+ mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId()),
+ false /* saveRestoreSnapFraction */);
+ }
+ });
+
mOneHandedController.ifPresent(controller -> {
controller.registerTransitionCallback(
new OneHandedTransitionCallback() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
index 85441af9a870..5aa3c4e2abef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
@@ -1,2 +1,3 @@
# WM shell sub-module TV pip owner
galinap@google.com
+bronger@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 93c75299a64b..3fef82352728 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -32,7 +32,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM_SHELL),
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
- WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_STARTING_WINDOW),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 7b42350b1365..27bc1a189086 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -43,6 +43,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
@@ -52,6 +53,7 @@ import com.android.wm.shell.util.SplitBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -82,6 +84,15 @@ public class RecentTasksController implements TaskStackListenerCallback,
private final Map<Integer, SplitBounds> mTaskSplitBoundsMap = new HashMap<>();
/**
+ * Set of taskId's that have been launched in freeform mode.
+ * This includes tasks that are currently running, visible and in freeform mode. And also
+ * includes tasks that are running in the background, are no longer visible, but at some point
+ * were visible to the user.
+ * This is used to decide which freeform apps belong to the user's desktop.
+ */
+ private final HashSet<Integer> mActiveFreeformTasks = new HashSet<>();
+
+ /**
* Creates {@link RecentTasksController}, returns {@code null} if the feature is not
* supported.
*/
@@ -206,6 +217,22 @@ public class RecentTasksController implements TaskStackListenerCallback,
notifyRecentTasksChanged();
}
+ /**
+ * Mark a task with given {@code taskId} as active in freeform
+ */
+ public void addActiveFreeformTask(int taskId) {
+ mActiveFreeformTasks.add(taskId);
+ notifyRecentTasksChanged();
+ }
+
+ /**
+ * Remove task with given {@code taskId} from active freeform tasks
+ */
+ public void removeActiveFreeformTask(int taskId) {
+ mActiveFreeformTasks.remove(taskId);
+ notifyRecentTasksChanged();
+ }
+
@VisibleForTesting
void notifyRecentTasksChanged() {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed");
@@ -273,6 +300,9 @@ public class RecentTasksController implements TaskStackListenerCallback,
rawMapping.put(taskInfo.taskId, taskInfo);
}
+ boolean desktopModeActive = DesktopMode.isActive(mContext);
+ ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
+
// Pull out the pairs as we iterate back in the list
ArrayList<GroupedRecentTaskInfo> recentTasks = new ArrayList<>();
for (int i = 0; i < rawList.size(); i++) {
@@ -282,16 +312,31 @@ public class RecentTasksController implements TaskStackListenerCallback,
continue;
}
+ if (desktopModeActive && mActiveFreeformTasks.contains(taskInfo.taskId)) {
+ // Freeform tasks will be added as a separate entry
+ freeformTasks.add(taskInfo);
+ continue;
+ }
+
final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
- if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(pairedTaskId)) {
+ if (!desktopModeActive && pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
+ pairedTaskId)) {
final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
- recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo,
+ recentTasks.add(GroupedRecentTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
- recentTasks.add(new GroupedRecentTaskInfo(taskInfo));
+ recentTasks.add(GroupedRecentTaskInfo.forSingleTask(taskInfo));
}
}
+
+ // Add a special entry for freeform tasks
+ if (!freeformTasks.isEmpty()) {
+ // First task is added separately
+ recentTasks.add(0, GroupedRecentTaskInfo.forFreeformTasks(
+ freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0])));
+ }
+
return recentTasks;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 872916197854..9206afb00e27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -147,7 +147,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final DragAndDropController mDragAndDropController;
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
- private final SplitscreenEventLogger mLogger;
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
@@ -186,7 +185,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mDragAndDropController = dragAndDropController;
mTransitions = transitions;
mTransactionPool = transactionPool;
- mLogger = new SplitscreenEventLogger();
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
@@ -221,7 +219,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
protected StageCoordinator createStageCoordinator() {
return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
+ mDisplayInsetsController, mTransitions, mTransactionPool,
mIconProvider, mMainExecutor, mRecentTasksOptional);
}
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 fbbc6d36d980..9c9841f33f88 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
@@ -250,14 +250,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, SplitscreenEventLogger logger,
+ TransactionPool transactionPool,
IconProvider iconProvider, ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
mTaskOrganizer = taskOrganizer;
- mLogger = logger;
+ mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
@@ -301,7 +301,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayController displayController, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger, ShellExecutor mainExecutor,
+ ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks) {
mContext = context;
mDisplayId = displayId;
@@ -316,7 +316,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
this::onTransitionAnimationComplete, this);
- mLogger = logger;
+ mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
mDisplayController.addDisplayWindowListener(this);
@@ -581,8 +581,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
IRemoteAnimationFinishedCallback finishedCallback,
SurfaceControl.Transaction t) {
if (apps == null || apps.length == 0) {
- onRemoteAnimationFinished(apps);
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+ setDividerVisibility(true, t);
t.apply();
+ onRemoteAnimationFinished(apps);
try {
adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
} catch (RemoteException e) {
@@ -595,7 +597,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
nonApps = ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
getDividerBarLegacyTarget());
- updateSurfaceBounds(mSplitLayout, t, false);
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
setDividerVisibility(true, t);
for (int i = 0; i < apps.length; ++i) {
if (apps[i].mode == MODE_OPENING) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
index d325d161ac53..28be0efc38f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
@@ -1,2 +1,3 @@
# WM shell sub-module TV splitscreen owner
galinap@google.com
+bronger@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index 9285e845a158..46d2a5a11671 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -34,7 +34,6 @@ import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.splitscreen.SplitscreenEventLogger;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -111,7 +110,7 @@ public class TvSplitScreenController extends SplitScreenController {
return new TvStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool,
- new SplitscreenEventLogger(), mIconProvider, mMainExecutor, mMainHandler,
+ mIconProvider, mMainExecutor, mMainHandler,
mRecentTasksOptional, mSystemWindows);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
index 6595beb59adb..4d563fbb7f04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -30,7 +30,6 @@ import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitScreenConstants;
import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.splitscreen.SplitscreenEventLogger;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.transition.Transitions;
@@ -48,13 +47,13 @@ public class TvStageCoordinator extends StageCoordinator
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, SplitscreenEventLogger logger,
+ TransactionPool transactionPool,
IconProvider iconProvider, ShellExecutor mainExecutor,
Handler mainHandler,
Optional<RecentTasksController> recentTasks,
SystemWindows systemWindows) {
super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
- displayInsetsController, transitions, transactionPool, logger, iconProvider,
+ displayInsetsController, transitions, transactionPool, iconProvider,
mainExecutor, recentTasks);
mTvSplitMenuController = new TvSplitMenuController(context, this,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index e26c259b2397..bcf4fbda0e0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -35,10 +35,14 @@ import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageCoordinator;
+import com.android.wm.shell.sysui.ShellInit;
import java.util.ArrayList;
+import java.util.Optional;
/**
* A handler for dealing with transitions involving multiple other handlers. For example: an
@@ -47,8 +51,8 @@ import java.util.ArrayList;
public class DefaultMixedHandler implements Transitions.TransitionHandler {
private final Transitions mPlayer;
- private final PipTransitionController mPipHandler;
- private final StageCoordinator mSplitHandler;
+ private PipTransitionController mPipHandler;
+ private StageCoordinator mSplitHandler;
private static class MixedTransition {
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
@@ -77,13 +81,22 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
mTransition = transition;
}
}
+
private final ArrayList<MixedTransition> mActiveTransitions = new ArrayList<>();
- public DefaultMixedHandler(@NonNull Transitions player,
- @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
+ public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player,
+ Optional<SplitScreenController> splitScreenControllerOptional,
+ Optional<PipTouchHandler> pipTouchHandlerOptional) {
mPlayer = player;
- mPipHandler = pipHandler;
- mSplitHandler = splitHandler;
+ if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent()
+ && splitScreenControllerOptional.isPresent()) {
+ // Add after dependencies because it is higher priority
+ shellInit.addInitCallback(() -> {
+ mPipHandler = pipTouchHandlerOptional.get().getTransitionHandler();
+ mSplitHandler = splitScreenControllerOptional.get().getTransitionHandler();
+ mPlayer.addHandler(this);
+ }, this);
+ }
}
@Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index cff60f5e5b6c..4c927b6e84b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -554,6 +554,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private void edgeExtendWindow(TransitionInfo.Change change,
Animation a, SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction) {
+ // Do not create edge extension surface for transfer starting window change.
+ // The app surface could be empty thus nothing can draw on the hardware renderer, which will
+ // block this thread when calling Surface#unlockCanvasAndPost.
+ if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+ return;
+ }
final Transformation transformationAtStart = new Transformation();
a.getTransformationAt(0, transformationAtStart);
final Transformation transformationAtEnd = new Transformation();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java
deleted file mode 100644
index 678e91fd8829..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.transition;
-
-import com.android.wm.shell.pip.phone.PipTouchHandler;
-import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.sysui.ShellInit;
-
-import java.util.Optional;
-
-/**
- * Handles transitions between the Splitscreen and PIP components.
- */
-public class SplitscreenPipMixedHandler {
-
- private final Optional<SplitScreenController> mSplitScreenOptional;
- private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
- private final Transitions mTransitions;
-
- public SplitscreenPipMixedHandler(ShellInit shellInit,
- Optional<SplitScreenController> splitScreenControllerOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional,
- Transitions transitions) {
- mSplitScreenOptional = splitScreenControllerOptional;
- mPipTouchHandlerOptional = pipTouchHandlerOptional;
- mTransitions = transitions;
- if (Transitions.ENABLE_SHELL_TRANSITIONS
- && mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) {
- shellInit.addInitCallback(this::onInit, this);
- }
- }
-
- private void onInit() {
- // Special handling for initializing based on multiple components
- final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions,
- mPipTouchHandlerOptional.get().getTransitionHandler(),
- mSplitScreenOptional.get().getTransitionHandler());
- // Added at end so that it has highest priority.
- mTransitions.addHandler(mixedHandler);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
index 2cff1714aff6..c045cebdf4e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.util;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.os.Parcel;
@@ -24,40 +25,143 @@ import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Simple container for recent tasks. May contain either a single or pair of tasks.
*/
public class GroupedRecentTaskInfo implements Parcelable {
- public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1;
- public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2;
- public @Nullable SplitBounds mSplitBounds;
- public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) {
- this(task1, null, null);
+ public static final int TYPE_SINGLE = 1;
+ public static final int TYPE_SPLIT = 2;
+ public static final int TYPE_FREEFORM = 3;
+
+ @IntDef(prefix = {"TYPE_"}, value = {
+ TYPE_SINGLE,
+ TYPE_SPLIT,
+ TYPE_FREEFORM
+ })
+ public @interface GroupType {}
+
+ @NonNull
+ private final ActivityManager.RecentTaskInfo[] mTasks;
+ @Nullable
+ private final SplitBounds mSplitBounds;
+ @GroupType
+ private final int mType;
+
+ /**
+ * Create new for a single task
+ */
+ public static GroupedRecentTaskInfo forSingleTask(
+ @NonNull ActivityManager.RecentTaskInfo task) {
+ return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task}, null,
+ TYPE_SINGLE);
+ }
+
+ /**
+ * Create new for a pair of tasks in split screen
+ */
+ public static GroupedRecentTaskInfo forSplitTasks(@NonNull ActivityManager.RecentTaskInfo task1,
+ @NonNull ActivityManager.RecentTaskInfo task2, @Nullable SplitBounds splitBounds) {
+ return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task1, task2},
+ splitBounds, TYPE_SPLIT);
}
- public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1,
- @Nullable ActivityManager.RecentTaskInfo task2,
- @Nullable SplitBounds splitBounds) {
- mTaskInfo1 = task1;
- mTaskInfo2 = task2;
+ /**
+ * Create new for a group of freeform tasks
+ */
+ public static GroupedRecentTaskInfo forFreeformTasks(
+ @NonNull ActivityManager.RecentTaskInfo... tasks) {
+ return new GroupedRecentTaskInfo(tasks, null, TYPE_FREEFORM);
+ }
+
+ private GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo[] tasks,
+ @Nullable SplitBounds splitBounds, @GroupType int type) {
+ mTasks = tasks;
mSplitBounds = splitBounds;
+ mType = type;
}
GroupedRecentTaskInfo(Parcel parcel) {
- mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
- mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
+ mTasks = parcel.createTypedArray(ActivityManager.RecentTaskInfo.CREATOR);
mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
+ mType = parcel.readInt();
+ }
+
+ /**
+ * Get primary {@link ActivityManager.RecentTaskInfo}
+ */
+ @NonNull
+ public ActivityManager.RecentTaskInfo getTaskInfo1() {
+ return mTasks[0];
+ }
+
+ /**
+ * Get secondary {@link ActivityManager.RecentTaskInfo}.
+ *
+ * Used in split screen.
+ */
+ @Nullable
+ public ActivityManager.RecentTaskInfo getTaskInfo2() {
+ if (mTasks.length > 1) {
+ return mTasks[1];
+ }
+ return null;
+ }
+
+ /**
+ * Get all {@link ActivityManager.RecentTaskInfo}s grouped together.
+ */
+ @NonNull
+ public List<ActivityManager.RecentTaskInfo> getTaskInfoList() {
+ return Arrays.asList(mTasks);
+ }
+
+ /**
+ * Return {@link SplitBounds} if this is a split screen entry or {@code null}
+ */
+ @Nullable
+ public SplitBounds getSplitBounds() {
+ return mSplitBounds;
+ }
+
+ /**
+ * Get type of this recents entry. One of {@link GroupType}
+ */
+ @GroupType
+ public int getType() {
+ return mType;
}
@Override
public String toString() {
- String taskString = "Task1: " + getTaskInfo(mTaskInfo1)
- + ", Task2: " + getTaskInfo(mTaskInfo2);
+ StringBuilder taskString = new StringBuilder();
+ for (int i = 0; i < mTasks.length; i++) {
+ if (i == 0) {
+ taskString.append("Task");
+ } else {
+ taskString.append(", Task");
+ }
+ taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks[i]));
+ }
if (mSplitBounds != null) {
- taskString += ", SplitBounds: " + mSplitBounds.toString();
+ taskString.append(", SplitBounds: ").append(mSplitBounds);
+ }
+ taskString.append(", Type=");
+ switch (mType) {
+ case TYPE_SINGLE:
+ taskString.append("TYPE_SINGLE");
+ break;
+ case TYPE_SPLIT:
+ taskString.append("TYPE_SPLIT");
+ break;
+ case TYPE_FREEFORM:
+ taskString.append("TYPE_FREEFORM");
+ break;
}
- return taskString;
+ return taskString.toString();
}
private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
@@ -74,9 +178,9 @@ public class GroupedRecentTaskInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeTypedObject(mTaskInfo1, flags);
- parcel.writeTypedObject(mTaskInfo2, flags);
+ parcel.writeTypedArray(mTasks, flags);
parcel.writeTypedObject(mSplitBounds, flags);
+ parcel.writeInt(mType);
}
@Override
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index a8154e818a04..2b162aec34ca 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -68,9 +68,7 @@ abstract class BaseTest @JvmOverloads constructor(
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
- test {
- testSpec.setIsTablet(wmHelper.currentState.wmState.isTablet)
- }
+ testSpec.setIsTablet(wmHelper.currentState.wmState.isTablet)
}
transition()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index cc987dc7986c..7058cb8ea149 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -99,15 +99,40 @@ fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible(
portraitPosTop: Boolean
) {
assertLayers {
- this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true)
+ // TODO(b/242025948): Use SPLIT_SCREEN_DIVIDER_COMPONENT.or(component) for notContains
+ // and isInvisible when they are ready.
+ this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT)
.then()
- .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
+ .notContains(component, isOptional = true)
+ .then()
+ .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT, isOptional = true)
+ .then()
+ .isInvisible(component, isOptional = true)
.then()
.splitAppLayerBoundsSnapToDivider(
component, landscapePosLeft, portraitPosTop, endRotation)
}
}
+fun FlickerTestParameter.splitAppLayerBoundsBecomesVisibleByDrag(
+ component: IComponentMatcher
+) {
+ assertLayers {
+ // TODO(b/242025948): Use SPLIT_SCREEN_DIVIDER_COMPONENT.or(component) for notContains
+ // and isInvisible when they are ready.
+ this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ .then()
+ .notContains(component, isOptional = true)
+ .then()
+ .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT, isOptional = true)
+ .then()
+ .isInvisible(component, isOptional = true)
+ .then()
+ // TODO(b/245472831): Verify the component should snap to divider.
+ .isVisible(component)
+ }
+}
+
fun FlickerTestParameter.splitAppLayerBoundsBecomesInvisible(
component: IComponentMatcher,
landscapePosLeft: Boolean,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index 298bf68c41a7..1390334f7938 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -55,21 +55,17 @@ abstract class BaseBubbleScreen(
): FlickerBuilder.() -> Unit {
return {
setup {
- test {
- notifyManager.setBubblesAllowed(testApp.`package`,
- uid, NotificationManager.BUBBLE_PREFERENCE_ALL)
- testApp.launchViaIntent(wmHelper)
- waitAndGetAddBubbleBtn()
- waitAndGetCancelAllBtn()
- }
+ notifyManager.setBubblesAllowed(testApp.`package`,
+ uid, NotificationManager.BUBBLE_PREFERENCE_ALL)
+ testApp.launchViaIntent(wmHelper)
+ waitAndGetAddBubbleBtn()
+ waitAndGetCancelAllBtn()
}
teardown {
- test {
- notifyManager.setBubblesAllowed(testApp.`package`,
- uid, NotificationManager.BUBBLE_PREFERENCE_NONE)
- testApp.exit()
- }
+ notifyManager.setBubblesAllowed(testApp.`package`,
+ uid, NotificationManager.BUBBLE_PREFERENCE_NONE)
+ testApp.exit()
}
extraSpec(this)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
index c44e250901f5..ac4de4780746 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
@@ -54,10 +54,8 @@ open class DismissBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScree
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
- eachRun {
- val addBubbleBtn = waitAndGetAddBubbleBtn()
- addBubbleBtn?.click() ?: error("Add Bubble not found")
- }
+ val addBubbleBtn = waitAndGetAddBubbleBtn()
+ addBubbleBtn?.click() ?: error("Add Bubble not found")
}
transitions {
wm.run { wm.defaultDisplay.getMetrics(displaySize) }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
index bab0112fa68f..7807854ac70a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
@@ -48,10 +48,8 @@ open class ExpandBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
- test {
- val addBubbleBtn = waitAndGetAddBubbleBtn()
- addBubbleBtn?.click() ?: error("Add Bubble not found")
- }
+ val addBubbleBtn = waitAndGetAddBubbleBtn()
+ addBubbleBtn?.click() ?: error("Add Bubble not found")
}
transitions {
val showBubble = device.wait(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
index 47557bcead2f..49681e140cef 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
@@ -49,15 +49,13 @@ class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScr
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
- eachRun {
- val addBubbleBtn = waitAndGetAddBubbleBtn()
- addBubbleBtn?.click() ?: error("Bubble widget not found")
- device.sleep()
- wmHelper.StateSyncBuilder()
- .withoutTopVisibleAppWindows()
- .waitForAndVerify()
- device.wakeUp()
- }
+ val addBubbleBtn = waitAndGetAddBubbleBtn()
+ addBubbleBtn?.click() ?: error("Bubble widget not found")
+ device.sleep()
+ wmHelper.StateSyncBuilder()
+ .withoutTopVisibleAppWindows()
+ .waitForAndVerify()
+ device.wakeUp()
}
transitions {
// Swipe & wait for the notification shade to expand so all can be seen
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
index 3ad92f87421b..2dccda6f9fd5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
@@ -57,20 +57,18 @@ open class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
- test {
- for (i in 1..3) {
- val addBubbleBtn = waitAndGetAddBubbleBtn() ?: error("Add Bubble not found")
- addBubbleBtn.click()
- SystemClock.sleep(1000)
- }
- val showBubble = device.wait(
- Until.findObject(
- By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)
- ), FIND_OBJECT_TIMEOUT
- ) ?: error("Show bubble not found")
- showBubble.click()
+ for (i in 1..3) {
+ val addBubbleBtn = waitAndGetAddBubbleBtn() ?: error("Add Bubble not found")
+ addBubbleBtn.click()
SystemClock.sleep(1000)
}
+ val showBubble = device.wait(
+ Until.findObject(
+ By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)
+ ), FIND_OBJECT_TIMEOUT
+ ) ?: error("Show bubble not found")
+ showBubble.click()
+ SystemClock.sleep(1000)
}
transitions {
val bubbles: List<UiObject2> = device.wait(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 87fa548e8027..eebc97bd75ed 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -18,11 +18,16 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
+import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -59,19 +64,19 @@ class AutoEnterPipOnGoToHomeTest(testSpec: FlickerTestParameter) : EnterPipTest(
*/
override val transition: FlickerBuilder.() -> Unit
get() = {
- setupAndTeardown(this)
setup {
- eachRun {
- pipApp.launchViaIntent(wmHelper)
- pipApp.enableAutoEnterForPipActivity()
- }
+ removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.enableAutoEnterForPipActivity()
}
teardown {
- eachRun {
- // close gracefully so that onActivityUnpinned() can be called before force exit
- pipApp.closePipWindow(wmHelper)
- pipApp.exit(wmHelper)
- }
+ // close gracefully so that onActivityUnpinned() can be called before force exit
+ pipApp.closePipWindow(wmHelper)
+
+ setRotation(Surface.ROTATION_0)
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ pipApp.exit(wmHelper)
}
transitions {
tapl.goHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index 42aac573ca9e..bac6a0c925fd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -19,12 +19,16 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -60,17 +64,17 @@ class EnterPipOnUserLeaveHintTest(testSpec: FlickerTestParameter) : EnterPipTest
*/
override val transition: FlickerBuilder.() -> Unit
get() = {
- setupAndTeardown(this)
setup {
- eachRun {
- pipApp.launchViaIntent(wmHelper)
- pipApp.enableEnterPipOnUserLeaveHint()
- }
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+ device.wakeUpAndGoToHomeScreen()
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.enableEnterPipOnUserLeaveHint()
}
teardown {
- eachRun {
- pipApp.exit(wmHelper)
- }
+ setRotation(Surface.ROTATION_0)
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ pipApp.exit(wmHelper)
}
transitions {
tapl.goHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index b1b97367b378..c9e38e4231b6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -25,6 +25,9 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
@@ -59,16 +62,15 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
- setupAndTeardown(this)
setup {
- eachRun {
- pipApp.launchViaIntent(wmHelper)
- }
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+ pipApp.launchViaIntent(wmHelper)
}
teardown {
- eachRun {
- pipApp.exit(wmHelper)
- }
+ setRotation(Surface.ROTATION_0)
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ pipApp.exit(wmHelper)
}
transitions {
pipApp.clickEnterPipButton(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 3bd2bf391d17..f6bb534663b8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -29,7 +29,10 @@ import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -78,29 +81,28 @@ class EnterPipToOtherOrientationTest(
*/
override val transition: FlickerBuilder.() -> Unit
get() = {
- setupAndTeardown(this)
-
setup {
- eachRun {
- // Launch a portrait only app on the fullscreen stack
- testApp.launchViaIntent(
- wmHelper, stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()
- )
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+
+ // Launch a portrait only app on the fullscreen stack
+ testApp.launchViaIntent(
+ wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()
)
- // Launch the PiP activity fixed as landscape
- pipApp.launchViaIntent(
- wmHelper, stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()
- )
+ )
+ // Launch the PiP activity fixed as landscape
+ pipApp.launchViaIntent(
+ wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()
)
- }
+ )
}
teardown {
- eachRun {
- pipApp.exit(wmHelper)
- testApp.exit(wmHelper)
- }
+ setRotation(Surface.ROTATION_0)
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ pipApp.exit(wmHelper)
+ testApp.exit(wmHelper)
}
transitions {
// Enter PiP, and assert that the PiP is within bounds now that the device is back
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index fd1fe65fa3a8..183c06f8ce7c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -33,14 +33,10 @@ abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true) {
setup {
- eachRun {
- this.setRotation(testSpec.startRotation)
- }
+ this.setRotation(testSpec.startRotation)
}
teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
+ this.setRotation(Surface.ROTATION_0)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 39a7017a16d0..53450de5221d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -64,10 +64,8 @@ class ExitPipViaExpandButtonClickTest(
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true) {
setup {
- eachRun {
- // launch an app behind the pip one
- testApp.launchViaIntent(wmHelper)
- }
+ // launch an app behind the pip one
+ testApp.launchViaIntent(wmHelper)
}
transitions {
// This will bring PipApp to fullscreen
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 421a6fc38e4c..034f623ec3f8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -64,10 +64,8 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true) {
setup {
- eachRun {
- // launch an app behind the pip one
- testApp.launchViaIntent(wmHelper)
- }
+ // launch an app behind the pip one
+ testApp.launchViaIntent(wmHelper)
}
transitions {
// This will bring PipApp to fullscreen
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index d3e2ce1571b4..de8c0093bfc5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -62,12 +62,8 @@ open class MovePipDownShelfHeightChangeTest(
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = false) {
teardown {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- }
- test {
- testApp.exit(wmHelper)
- }
+ testApp.launchViaIntent(wmHelper)
+ testApp.exit(wmHelper)
}
transitions {
tapl.pressHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index 3d64bbeb720b..0b6e7ee84708 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -72,12 +72,8 @@ class MovePipUpShelfHeightChangeTest(
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = false) {
teardown {
- eachRun {
- tapl.pressHome()
- }
- test {
- testApp.exit(wmHelper)
- }
+ tapl.pressHome()
+ testApp.exit(wmHelper)
}
transitions {
testApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index be39faeff321..df6ba987e095 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -59,16 +59,12 @@ open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testS
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = false) {
setup {
- test {
- imeApp.launchViaIntent(wmHelper)
- setRotation(testSpec.startRotation)
- }
+ imeApp.launchViaIntent(wmHelper)
+ setRotation(testSpec.startRotation)
}
teardown {
- test {
- imeApp.exit(wmHelper)
- setRotation(Surface.ROTATION_0)
- }
+ imeApp.exit(wmHelper)
+ setRotation(Surface.ROTATION_0)
}
transitions {
// open the soft keyboard
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 4dc98588217a..fc0f0ee87ec7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -74,12 +74,8 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = false) {
setup {
- test {
- fixedApp.launchViaIntent(wmHelper)
- }
- eachRun {
- setRotation(testSpec.startRotation)
- }
+ fixedApp.launchViaIntent(wmHelper)
+ setRotation(testSpec.startRotation)
}
transitions {
setRotation(testSpec.endRotation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index 9ade59783700..faf6afc64c69 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -23,7 +23,6 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import com.android.wm.shell.flicker.BaseTest
import com.android.wm.shell.flicker.helpers.PipAppHelper
@@ -57,28 +56,6 @@ abstract class PipTransition(testSpec: FlickerTestParameter) : BaseTest(testSpec
}
/**
- * Gets a configuration that handles basic setup and teardown of pip tests
- */
- protected val setupAndTeardown: FlickerBuilder.() -> Unit
- get() = {
- setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- }
- }
- teardown {
- eachRun {
- setRotation(Surface.ROTATION_0)
- }
- test {
- removeAllTasksButHome()
- pipApp.exit(wmHelper)
- }
- }
- }
-
- /**
* Gets a configuration that handles basic setup and teardown of pip tests and that
* launches the Pip app for test
*
@@ -93,30 +70,27 @@ abstract class PipTransition(testSpec: FlickerTestParameter) : BaseTest(testSpec
extraSpec: FlickerBuilder.() -> Unit = {}
): FlickerBuilder.() -> Unit {
return {
- setupAndTeardown(this)
-
setup {
- test {
- if (!eachRun) {
- pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
- }
+ setRotation(Surface.ROTATION_0)
+ removeAllTasksButHome()
+
+ if (!eachRun) {
+ pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
}
- eachRun {
- if (eachRun) {
- pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
- }
+ if (eachRun) {
+ pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
}
}
teardown {
- eachRun {
- if (eachRun) {
- pipApp.exit(wmHelper)
- }
+ setRotation(Surface.ROTATION_0)
+ removeAllTasksButHome()
+ pipApp.exit(wmHelper)
+
+ if (eachRun) {
+ pipApp.exit(wmHelper)
}
- test {
- if (!eachRun) {
- pipApp.exit(wmHelper)
- }
+ if (!eachRun) {
+ pipApp.exit(wmHelper)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index d5de22fb49c0..acada4c8cca3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -61,33 +61,26 @@ open class SetRequestedOrientationWhilePinnedTest(
override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- // Launch the PiP activity fixed as landscape.
- pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
- // Enter PiP.
- broadcastActionTrigger.doAction(Components.PipActivity.ACTION_ENTER_PIP)
- // System bar may fade out during fixed rotation.
- wmHelper.StateSyncBuilder()
- .withPipShown()
- .withRotation(Surface.ROTATION_0)
- .withNavOrTaskBarVisible()
- .withStatusBarVisible()
- .waitForAndVerify()
- }
+ removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+
+ // Launch the PiP activity fixed as landscape.
+ pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
+ // Enter PiP.
+ broadcastActionTrigger.doAction(Components.PipActivity.ACTION_ENTER_PIP)
+ // System bar may fade out during fixed rotation.
+ wmHelper.StateSyncBuilder()
+ .withPipShown()
+ .withRotation(Surface.ROTATION_0)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
}
teardown {
- eachRun {
- pipApp.exit(wmHelper)
- setRotation(Surface.ROTATION_0)
- }
- test {
- removeAllTasksButHome()
- }
+ pipApp.exit(wmHelper)
+ setRotation(Surface.ROTATION_0)
+ removeAllTasksButHome()
}
transitions {
// Launch the activity back into fullscreen and ensure that it is now in landscape
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index 102a78ba16ab..2acdfbf32a9e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -41,6 +42,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:CopyContentInSplit`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -53,9 +55,7 @@ class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testS
get() = {
super.transition(this)
setup {
- eachRun {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, textEditApp)
- }
+ SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, textEditApp)
}
transitions {
SplitScreenHelper.copyContentInSplit(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index bf91292811e8..44157b8aa269 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -39,11 +40,13 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
+
/**
* Test dismiss split screen by dragging the divider bar.
*
* To run this test: `atest WMShellFlickerTests:DismissSplitScreenByDivider`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -55,9 +58,7 @@ class DismissSplitScreenByDivider (testSpec: FlickerTestParameter) : SplitScreen
get() = {
super.transition(this)
setup {
- eachRun {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
- }
+ SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
}
transitions {
if (tapl.isTablet) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 20a7423be681..df83d7082936 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -41,6 +42,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:DismissSplitScreenByGoHome`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -54,9 +56,7 @@ class DismissSplitScreenByGoHome(
get() = {
super.transition(this)
setup {
- eachRun {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
- }
+ SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
}
transitions {
tapl.goHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 8f7673b77429..b77e8da6fca2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -42,6 +43,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:DragDividerToResize`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -53,9 +55,7 @@ class DragDividerToResize (testSpec: FlickerTestParameter) : SplitScreenBase(tes
get() = {
super.transition(this)
setup {
- eachRun {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
- }
+ SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
}
transitions {
SplitScreenHelper.dragDividerToResizeAndWait(device, wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index 8e2769fb75bd..3cd74716ec39 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -25,12 +26,14 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
-import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
import org.junit.Assume
@@ -47,6 +50,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromAllApps`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -65,10 +69,8 @@ class EnterSplitScreenByDragFromAllApps(
get() = {
super.transition(this)
setup {
- eachRun {
- tapl.goHome()
- primaryApp.launchViaIntent(wmHelper)
- }
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
}
transitions {
tapl.launchedAppState.taskbar
@@ -81,7 +83,20 @@ class EnterSplitScreenByDragFromAllApps(
@Presubmit
@Test
- fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+ fun splitScreenDividerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ testSpec.splitScreenDividerBecomesVisible()
+ }
+
+ // TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
+ @Presubmit
+ @Test
+ fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ testSpec.assertLayersEnd {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+ }
@Presubmit
@Test
@@ -98,8 +113,8 @@ class EnterSplitScreenByDragFromAllApps(
@Presubmit
@Test
- fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- secondaryApp, landscapePosLeft = true, portraitPosTop = true)
+ fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisibleByDrag(
+ secondaryApp)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 531d376e3588..7db55ad21a14 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -27,11 +28,13 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
-import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
import org.junit.Assume
@@ -48,6 +51,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromNotification`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -69,33 +73,42 @@ class EnterSplitScreenByDragFromNotification(
get() = {
super.transition(this)
setup {
- eachRun {
- // Send a notification
- sendNotificationApp.launchViaIntent(wmHelper)
- val sendNotification = device.wait(
- Until.findObject(By.text("Send Notification")),
- SplitScreenHelper.TIMEOUT_MS
- )
- sendNotification?.click() ?: error("Send notification button not found")
-
- tapl.goHome()
- primaryApp.launchViaIntent(wmHelper)
- }
+ // Send a notification
+ sendNotificationApp.launchViaIntent(wmHelper)
+ val sendNotification = device.wait(
+ Until.findObject(By.text("Send Notification")),
+ SplitScreenHelper.TIMEOUT_MS
+ )
+ sendNotification?.click() ?: error("Send notification button not found")
+
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
}
transitions {
SplitScreenHelper.dragFromNotificationToSplit(instrumentation, device, wmHelper)
SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
}
teardown {
- eachRun {
- sendNotificationApp.exit(wmHelper)
- }
+ sendNotificationApp.exit(wmHelper)
}
}
@Presubmit
@Test
- fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+ fun splitScreenDividerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ testSpec.splitScreenDividerBecomesVisible()
+ }
+
+ // TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
+ @Presubmit
+ @Test
+ fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ testSpec.assertLayersEnd {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+ }
@Presubmit
@Test
@@ -113,8 +126,8 @@ class EnterSplitScreenByDragFromNotification(
@Presubmit
@Test
- fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- sendNotificationApp, landscapePosLeft = true, portraitPosTop = true)
+ fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisibleByDrag(
+ sendNotificationApp)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index ea43c7ea0ddf..82124128afa1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -25,12 +26,14 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
-import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
import org.junit.Assume
@@ -47,6 +50,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromTaskbar`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -66,13 +70,11 @@ class EnterSplitScreenByDragFromTaskbar(
get() = {
super.transition(this)
setup {
- eachRun {
- tapl.goHome()
- SplitScreenHelper.createShortcutOnHotseatIfNotExist(
- tapl, secondaryApp.appName
- )
- primaryApp.launchViaIntent(wmHelper)
- }
+ tapl.goHome()
+ SplitScreenHelper.createShortcutOnHotseatIfNotExist(
+ tapl, secondaryApp.appName
+ )
+ primaryApp.launchViaIntent(wmHelper)
}
transitions {
tapl.launchedAppState.taskbar
@@ -84,7 +86,20 @@ class EnterSplitScreenByDragFromTaskbar(
@Presubmit
@Test
- fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+ fun splitScreenDividerBecomesVisible() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ testSpec.splitScreenDividerBecomesVisible()
+ }
+
+ // TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
+ @Presubmit
+ @Test
+ fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ testSpec.assertLayersEnd {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+ }
@Presubmit
@Test
@@ -101,8 +116,8 @@ class EnterSplitScreenByDragFromTaskbar(
@Presubmit
@Test
- fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- secondaryApp, landscapePosLeft = true, portraitPosTop = true)
+ fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisibleByDrag(
+ secondaryApp)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index 7ce23c5f9a4e..1868602df7a8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
@@ -42,6 +43,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromOverview`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -53,16 +55,14 @@ class EnterSplitScreenFromOverview(testSpec: FlickerTestParameter) : SplitScreen
get() = {
super.transition(this)
setup {
- eachRun {
- tapl.workspace.switchToOverview().dismissAllTasks()
- primaryApp.launchViaIntent(wmHelper)
- secondaryApp.launchViaIntent(wmHelper)
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withAppTransitionIdle()
- .withHomeActivityVisible()
- .waitForAndVerify()
- }
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
}
transitions {
SplitScreenHelper.splitFromOverview(tapl)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
index 81390b243ef9..eab473ca55a1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
@@ -32,17 +32,13 @@ abstract class SplitScreenBase(testSpec: FlickerTestParameter) : BaseTest(testSp
override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
- test {
- tapl.setEnableRotation(true)
- setRotation(testSpec.startRotation)
- tapl.setExpectedRotation(testSpec.startRotation)
- }
+ tapl.setEnableRotation(true)
+ setRotation(testSpec.startRotation)
+ tapl.setExpectedRotation(testSpec.startRotation)
}
teardown {
- eachRun {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
- }
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index 58f7b048bc70..d265f83ddb96 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -42,6 +43,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:SwitchAppByDoubleTapDivider`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -53,9 +55,7 @@ class SwitchAppByDoubleTapDivider (testSpec: FlickerTestParameter) : SplitScreen
get() = {
super.transition(this)
setup {
- eachRun {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
- }
+ SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
}
transitions {
SplitScreenHelper.doubleTapDividerToSwitch(device)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 0dd6706fb00e..8b905effa8f8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -41,6 +42,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromAnotherApp`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -53,14 +55,12 @@ class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScr
get() = {
super.transition(this)
setup {
- eachRun {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
-
- thirdApp.launchViaIntent(wmHelper)
- wmHelper.StateSyncBuilder()
- .withWindowSurfaceAppeared(thirdApp)
- .waitForAndVerify()
- }
+ SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
+
+ thirdApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder()
+ .withWindowSurfaceAppeared(thirdApp)
+ .waitForAndVerify()
}
transitions {
tapl.launchedAppState.quickSwitchToPreviousApp()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index dc8ba0caa4e4..fcab70204909 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -41,6 +42,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -52,14 +54,12 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas
get() = {
super.transition(this)
setup {
- eachRun {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
-
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .waitForAndVerify()
- }
+ SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
}
transitions {
tapl.workspace.quickSwitchToPreviousApp()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index e5924c563e5b..5364492cf59c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -41,6 +42,7 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromRecent`
*/
+@IwTest(focusArea = "sysui")
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -52,14 +54,12 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB
get() = {
super.transition(this)
setup {
- eachRun {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
-
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .waitForAndVerify()
- }
+ SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
}
transitions {
tapl.workspace.switchToOverview()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 1b5091f58f26..a8d3bdcb7c96 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -43,6 +43,7 @@ import android.util.Size;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -99,7 +100,8 @@ public class PipControllerTest extends ShellTestCase {
@Mock private TaskStackListenerImpl mMockTaskStackListener;
@Mock private ShellExecutor mMockExecutor;
@Mock private Optional<OneHandedController> mMockOneHandedController;
- @Mock private PipParamsChangedForwarder mPipParamsChangedForwarder;
+ @Mock private PipParamsChangedForwarder mMockPipParamsChangedForwarder;
+ @Mock private DisplayInsetsController mMockDisplayInsetsController;
@Mock private DisplayLayout mMockDisplayLayout1;
@Mock private DisplayLayout mMockDisplayLayout2;
@@ -120,8 +122,8 @@ public class PipControllerTest extends ShellTestCase {
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
- mMockTaskStackListener, mPipParamsChangedForwarder,
- mMockOneHandedController, mMockExecutor);
+ mMockTaskStackListener, mMockPipParamsChangedForwarder,
+ mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor);
mShellInit.init();
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -186,8 +188,8 @@ public class PipControllerTest extends ShellTestCase {
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
- mMockTaskStackListener, mPipParamsChangedForwarder,
- mMockOneHandedController, mMockExecutor));
+ mMockTaskStackListener, mMockPipParamsChangedForwarder,
+ mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
new file mode 100644
index 000000000000..baa06f2f0c45
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.recents
+
+import android.app.ActivityManager
+import android.graphics.Rect
+import android.os.Parcel
+import android.testing.AndroidTestingRunner
+import android.window.IWindowContainerToken
+import android.window.WindowContainerToken
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import com.android.wm.shell.util.GroupedRecentTaskInfo.CREATOR
+import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM
+import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SINGLE
+import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SPLIT
+import com.android.wm.shell.util.SplitBounds
+import com.google.common.truth.Correspondence
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+/**
+ * Tests for [GroupedRecentTaskInfo]
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class GroupedRecentTaskInfoTest : ShellTestCase() {
+
+ @Test
+ fun testSingleTask_hasCorrectType() {
+ assertThat(singleTaskGroupInfo().type).isEqualTo(TYPE_SINGLE)
+ }
+
+ @Test
+ fun testSingleTask_task1Set_task2Null() {
+ val group = singleTaskGroupInfo()
+ assertThat(group.taskInfo1.taskId).isEqualTo(1)
+ assertThat(group.taskInfo2).isNull()
+ }
+
+ @Test
+ fun testSingleTask_taskInfoList_hasOneTask() {
+ val list = singleTaskGroupInfo().taskInfoList
+ assertThat(list).hasSize(1)
+ assertThat(list[0].taskId).isEqualTo(1)
+ }
+
+ @Test
+ fun testSplitTasks_hasCorrectType() {
+ assertThat(splitTasksGroupInfo().type).isEqualTo(TYPE_SPLIT)
+ }
+
+ @Test
+ fun testSplitTasks_task1Set_task2Set_boundsSet() {
+ val group = splitTasksGroupInfo()
+ assertThat(group.taskInfo1.taskId).isEqualTo(1)
+ assertThat(group.taskInfo2?.taskId).isEqualTo(2)
+ assertThat(group.splitBounds).isNotNull()
+ }
+
+ @Test
+ fun testSplitTasks_taskInfoList_hasTwoTasks() {
+ val list = splitTasksGroupInfo().taskInfoList
+ assertThat(list).hasSize(2)
+ assertThat(list[0].taskId).isEqualTo(1)
+ assertThat(list[1].taskId).isEqualTo(2)
+ }
+
+ @Test
+ fun testFreeformTasks_hasCorrectType() {
+ assertThat(freeformTasksGroupInfo().type).isEqualTo(TYPE_FREEFORM)
+ }
+
+ @Test
+ fun testSplitTasks_taskInfoList_hasThreeTasks() {
+ val list = freeformTasksGroupInfo().taskInfoList
+ assertThat(list).hasSize(3)
+ assertThat(list[0].taskId).isEqualTo(1)
+ assertThat(list[1].taskId).isEqualTo(2)
+ assertThat(list[2].taskId).isEqualTo(3)
+ }
+
+ @Test
+ fun testParcelling_singleTask() {
+ val recentTaskInfo = singleTaskGroupInfo()
+ val parcel = Parcel.obtain()
+ recentTaskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ // Read the object back from the parcel
+ val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SINGLE)
+ assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
+ assertThat(recentTaskInfoParcel.taskInfo2).isNull()
+ }
+
+ @Test
+ fun testParcelling_splitTasks() {
+ val recentTaskInfo = splitTasksGroupInfo()
+ val parcel = Parcel.obtain()
+ recentTaskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ // Read the object back from the parcel
+ val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SPLIT)
+ assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
+ assertThat(recentTaskInfoParcel.taskInfo2).isNotNull()
+ assertThat(recentTaskInfoParcel.taskInfo2!!.taskId).isEqualTo(2)
+ assertThat(recentTaskInfoParcel.splitBounds).isNotNull()
+ }
+
+ @Test
+ fun testParcelling_freeformTasks() {
+ val recentTaskInfo = freeformTasksGroupInfo()
+ val parcel = Parcel.obtain()
+ recentTaskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ // Read the object back from the parcel
+ val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
+ assertThat(recentTaskInfoParcel.taskInfoList).hasSize(3)
+ // Only compare task ids
+ val taskIdComparator = Correspondence.transforming<ActivityManager.RecentTaskInfo, Int>(
+ { it?.taskId }, "has taskId of"
+ )
+ assertThat(recentTaskInfoParcel.taskInfoList).comparingElementsUsing(taskIdComparator)
+ .containsExactly(1, 2, 3)
+ }
+
+ private fun createTaskInfo(id: Int) = ActivityManager.RecentTaskInfo().apply {
+ taskId = id
+ token = WindowContainerToken(mock(IWindowContainerToken::class.java))
+ }
+
+ private fun singleTaskGroupInfo(): GroupedRecentTaskInfo {
+ val task = createTaskInfo(id = 1)
+ return GroupedRecentTaskInfo.forSingleTask(task)
+ }
+
+ private fun splitTasksGroupInfo(): GroupedRecentTaskInfo {
+ val task1 = createTaskInfo(id = 1)
+ val task2 = createTaskInfo(id = 2)
+ val splitBounds = SplitBounds(Rect(), Rect(), 1, 2)
+ return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds)
+ }
+
+ private fun freeformTasksGroupInfo(): GroupedRecentTaskInfo {
+ val task1 = createTaskInfo(id = 1)
+ val task2 = createTaskInfo(id = 2)
+ val task3 = createTaskInfo(id = 3)
+ return GroupedRecentTaskInfo.forFreeformTasks(task1, task2, task3)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 81bb609cc711..e9a1e2523a86 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -20,6 +20,9 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -45,11 +48,13 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
@@ -179,6 +184,46 @@ public class RecentTasksControllerTest extends ShellTestCase {
}
@Test
+ public void testGetRecentTasks_groupActiveFreeformTasks() {
+ StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
+ DesktopMode.class).startMocking();
+ when(DesktopMode.isActive(any())).thenReturn(true);
+
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ setRawList(t1, t2, t3, t4);
+
+ mRecentTasksController.addActiveFreeformTask(1);
+ mRecentTasksController.addActiveFreeformTask(3);
+
+ ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
+ MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+
+ // 2 freeform tasks should be grouped into one, 3 total recents entries
+ assertEquals(3, recentTasks.size());
+ GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
+ GroupedRecentTaskInfo singleGroup1 = recentTasks.get(1);
+ GroupedRecentTaskInfo singleGroup2 = recentTasks.get(2);
+
+ // Check that groups have expected types
+ assertEquals(GroupedRecentTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
+ assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup1.getType());
+ assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType());
+
+ // Check freeform group entries
+ assertEquals(t1, freeformGroup.getTaskInfoList().get(0));
+ assertEquals(t3, freeformGroup.getTaskInfoList().get(1));
+
+ // Check single entries
+ assertEquals(t2, singleGroup1.getTaskInfo1());
+ assertEquals(t4, singleGroup2.getTaskInfo1());
+
+ mockitoSession.finishMocking();
+ }
+
+ @Test
public void testRemovedTaskRemovesSplit() {
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
@@ -254,6 +299,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
/**
* Asserts that the recent tasks matches the given task ids.
+ *
* @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
* the grouped task list
*/
@@ -262,22 +308,23 @@ public class RecentTasksControllerTest extends ShellTestCase {
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) {
GroupedRecentTaskInfo pair = recentTasks.get(i);
- int taskId1 = pair.mTaskInfo1.taskId;
+ int taskId1 = pair.getTaskInfo1().taskId;
flattenedTaskIds[2 * i] = taskId1;
- flattenedTaskIds[2 * i + 1] = pair.mTaskInfo2 != null
- ? pair.mTaskInfo2.taskId
+ flattenedTaskIds[2 * i + 1] = pair.getTaskInfo2() != null
+ ? pair.getTaskInfo2().taskId
: -1;
- if (pair.mTaskInfo2 != null) {
- assertNotNull(pair.mSplitBounds);
- int leftTopTaskId = pair.mSplitBounds.leftTopTaskId;
- int bottomRightTaskId = pair.mSplitBounds.rightBottomTaskId;
+ if (pair.getTaskInfo2() != null) {
+ assertNotNull(pair.getSplitBounds());
+ int leftTopTaskId = pair.getSplitBounds().leftTopTaskId;
+ int bottomRightTaskId = pair.getSplitBounds().rightBottomTaskId;
// Unclear if pairs are ordered by split position, most likely not.
- assertTrue(leftTopTaskId == taskId1 || leftTopTaskId == pair.mTaskInfo2.taskId);
+ assertTrue(leftTopTaskId == taskId1
+ || leftTopTaskId == pair.getTaskInfo2().taskId);
assertTrue(bottomRightTaskId == taskId1
- || bottomRightTaskId == pair.mTaskInfo2.taskId);
+ || bottomRightTaskId == pair.getTaskInfo2().taskId);
} else {
- assertNull(pair.mSplitBounds);
+ assertNull(pair.getSplitBounds());
}
}
assertTrue("Expected: " + Arrays.toString(expectedTaskIds)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index a67853cfe745..ae69b3ddd042 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -71,11 +71,11 @@ public class SplitTestUtils {
DisplayController displayController, DisplayImeController imeController,
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger, ShellExecutor mainExecutor,
+ ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
- transitions, transactionPool, logger, mainExecutor, recentTasks);
+ transitions, transactionPool, mainExecutor, recentTasks);
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 1d038f4ee377..ea0033ba4bbb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -95,7 +95,6 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private TransactionPool mTransactionPool;
@Mock private Transitions mTransitions;
@Mock private SurfaceSession mSurfaceSession;
- @Mock private SplitscreenEventLogger mLogger;
@Mock private IconProvider mIconProvider;
@Mock private ShellExecutor mMainExecutor;
private SplitLayout mSplitLayout;
@@ -127,7 +126,7 @@ public class SplitTransitionTests extends ShellTestCase {
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool, mLogger, mMainExecutor, Optional.empty());
+ mTransactionPool, mMainExecutor, Optional.empty());
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 4b68870d4129..ea9390ee8efd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -97,8 +97,6 @@ public class StageCoordinatorTests extends ShellTestCase {
@Mock
private TransactionPool mTransactionPool;
@Mock
- private SplitscreenEventLogger mLogger;
- @Mock
private ShellExecutor mMainExecutor;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
@@ -115,7 +113,7 @@ public class StageCoordinatorTests extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mLogger,
+ mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
mMainExecutor, Optional.empty()));
doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 72190e370129..676489051b4a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2383,6 +2383,14 @@ public class AudioSystem
return types.size() == 1 && types.contains(type);
}
+ /**
+ * @hide
+ * Return true if the audio device type is a Bluetooth LE Audio device.
+ */
+ public static boolean isLeAudioDeviceType(int type) {
+ return DEVICE_OUT_ALL_BLE_SET.contains(type);
+ }
+
/** @hide */
public static final int DEFAULT_MUTE_STREAMS_AFFECTED =
(1 << STREAM_MUSIC) |
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index e18642ce9856..0c8cacd894cf 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -79,20 +79,24 @@ import java.util.zip.CRC32;
/**
* This is a class for reading and writing Exif tags in various image file formats.
* <p>
+ * <b>Note:</b> This class has known issues on some versions of Android. It is recommended to use
+ * the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/exifinterface/media/ExifInterface.html">ExifInterface
+ * Library</a> since it offers a superset of the functionality of this class and is more easily
+ * updateable. In addition to the functionality of this class, it supports parsing extra metadata
+ * such as exposure and data compression information as well as setting extra metadata such as GPS
+ * and datetime information.
+ * <p>
* Supported for reading: JPEG, PNG, WebP, HEIF, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF,
* AVIF.
* <p>
- * Supported for writing: JPEG, PNG, WebP, DNG.
+ * Supported for writing: JPEG, PNG, WebP.
* <p>
* Note: JPEG and HEIF files may contain XMP data either inside the Exif data chunk or outside of
* it. This class will search both locations for XMP data, but if XMP data exist both inside and
* outside Exif, will favor the XMP data inside Exif over the one outside.
* <p>
- * Note: It is recommended to use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/exifinterface/media/ExifInterface.html">ExifInterface
- * Library</a> since it is a superset of this class. In addition to the functionalities of this
- * class, it supports parsing extra metadata such as exposure and data compression information
- * as well as setting extra metadata such as GPS and datetime information.
+
*/
public class ExifInterface {
private static final String TAG = "ExifInterface";
@@ -1294,7 +1298,6 @@ public class ExifInterface {
new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
- new ExifTag(TAG_XMP, 700, IFD_FORMAT_BYTE),
new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
@@ -2076,7 +2079,7 @@ public class ExifInterface {
* {@link #setAttribute(String,String)} to set all attributes to write and
* make a single call rather than multiple calls for each attribute.
* <p>
- * This method is supported for JPEG, PNG, WebP, and DNG files.
+ * This method is supported for JPEG, PNG, and WebP files.
* <p class="note">
* Note: after calling this method, any attempts to obtain range information
* from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()}
@@ -2088,11 +2091,15 @@ public class ExifInterface {
* <p>
* For PNG format, the Exif data will be stored as an "eXIf" chunk as per
* "Extensions to the PNG 1.2 Specification, Version 1.5.0".
+ * <p>
+ * <b>Warning:</b> Calling this method on a DNG-based instance of {@code ExifInterface} may
+ * result in the original image file being overwritten with invalid data on some versions of
+ * Android 13 (API 33).
*/
public void saveAttributes() throws IOException {
if (!isSupportedFormatForSavingAttributes()) {
throw new IOException("ExifInterface only supports saving attributes for JPEG, PNG, "
- + "WebP, and DNG formats.");
+ + "and WebP formats.");
}
if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
throw new IOException(
@@ -2150,10 +2157,6 @@ public class ExifInterface {
savePngAttributes(bufferedIn, bufferedOut);
} else if (mMimeType == IMAGE_TYPE_WEBP) {
saveWebpAttributes(bufferedIn, bufferedOut);
- } else if (mMimeType == IMAGE_TYPE_DNG || mMimeType == IMAGE_TYPE_UNKNOWN) {
- ByteOrderedDataOutputStream dataOutputStream =
- new ByteOrderedDataOutputStream(bufferedOut, ByteOrder.BIG_ENDIAN);
- writeExifSegment(dataOutputStream);
}
}
} catch (Exception e) {
@@ -5262,8 +5265,7 @@ public class ExifInterface {
private boolean isSupportedFormatForSavingAttributes() {
if (mIsSupportedFile && (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_PNG
- || mMimeType == IMAGE_TYPE_WEBP || mMimeType == IMAGE_TYPE_DNG
- || mMimeType == IMAGE_TYPE_UNKNOWN)) {
+ || mMimeType == IMAGE_TYPE_WEBP)) {
return true;
}
return false;
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index 4f9c6f1ba68c..4cccf8972798 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -19,6 +19,7 @@ android_test {
static_libs: [
"androidx.test.core",
+ "androidx.test.ext.truth",
"androidx.test.rules",
"compatibility-device-util-axt",
"mockito-target-minus-junit4",
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index aa486cbd9e00..810b408370c7 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -22,6 +22,8 @@ import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
+import static androidx.test.ext.truth.os.BundleSubject.assertThat;
+
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.FEATURE_SAMPLE;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.FEATURE_SPECIAL;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID1;
@@ -351,7 +353,7 @@ public class MediaRouter2ManagerTest {
}
}
- assertThat(remoteRouteCount > 0).isTrue();
+ assertThat(remoteRouteCount).isGreaterThan(0);
}
/**
@@ -384,7 +386,7 @@ public class MediaRouter2ManagerTest {
mManager.selectRoute(mPackageName, routeToSelect);
assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
- assertThat(mManager.getRemoteSessions().size()).isEqualTo(1);
+ assertThat(mManager.getRemoteSessions()).hasSize(1);
}
@Test
@@ -406,20 +408,20 @@ public class MediaRouter2ManagerTest {
}
});
- assertThat(mManager.getRoutingSessions(mPackageName).size()).isEqualTo(1);
+ assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
mManager.selectRoute(mPackageName, routeToSelect);
assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
- assertThat(sessions.size()).isEqualTo(2);
+ assertThat(sessions).hasSize(2);
RoutingSessionInfo sessionInfo = sessions.get(1);
awaitOnRouteChangedManager(
() -> mManager.releaseSession(sessionInfo),
ROUTE_ID1,
route -> TextUtils.equals(route.getClientPackageName(), null));
- assertThat(mManager.getRoutingSessions(mPackageName).size()).isEqualTo(1);
+ assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
}
@Test
@@ -477,20 +479,20 @@ public class MediaRouter2ManagerTest {
}
});
- assertThat(mManager.getRoutingSessions(mPackageName).size()).isEqualTo(1);
- assertThat(mRouter2.getControllers().size()).isEqualTo(1);
+ assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
+ assertThat(mRouter2.getControllers()).hasSize(1);
mManager.transfer(mManager.getRoutingSessions(mPackageName).get(0), routeToSelect);
assertThat(transferLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
- assertThat(mManager.getRoutingSessions(mPackageName).size()).isEqualTo(2);
- assertThat(mRouter2.getControllers().size()).isEqualTo(2);
+ assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(2);
+ assertThat(mRouter2.getControllers()).hasSize(2);
mRouter2.getControllers().get(1).release();
assertThat(releaseLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
- assertThat(mRouter2.getControllers().size()).isEqualTo(1);
- assertThat(mManager.getRoutingSessions(mPackageName).size()).isEqualTo(1);
+ assertThat(mRouter2.getControllers()).hasSize(1);
+ assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
}
/**
@@ -596,8 +598,8 @@ public class MediaRouter2ManagerTest {
.map(RoutingSessionInfo::getId)
.collect(Collectors.toList());
// The old session shouldn't appear on the session list.
- assertThat(remoteSessionIds.contains(sessions.get(0).getId())).isFalse();
- assertThat(remoteSessionIds.contains(sessions.get(1).getId())).isTrue();
+ assertThat(remoteSessionIds).doesNotContain(sessions.get(0).getId());
+ assertThat(remoteSessionIds).contains(sessions.get(1).getId());
assertThat(serviceOnReleaseSessionLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS))
.isFalse();
@@ -705,7 +707,7 @@ public class MediaRouter2ManagerTest {
assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
- assertThat(sessions.size()).isEqualTo(2);
+ assertThat(sessions).hasSize(2);
// test setSessionVolume
RoutingSessionInfo sessionInfo = sessions.get(1);
@@ -768,7 +770,7 @@ public class MediaRouter2ManagerTest {
addManagerCallback(new MediaRouter2Manager.Callback() {});
mManager.setRouteVolume(volRoute, 0);
assertThat(onSetRouteVolumeLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
- assertThat(requestIds.isEmpty()).isFalse();
+ assertThat(requestIds).isNotEmpty();
final int failureReason = REASON_REJECTED;
final CountDownLatch onRequestFailedLatch = new CountDownLatch(1);
@@ -837,13 +839,13 @@ public class MediaRouter2ManagerTest {
@Override
public void onTransferred(RoutingSessionInfo oldSession,
RoutingSessionInfo newSession) {
- assertThat(newSession.getSelectedRoutes().contains(route.getId())).isTrue();
+ assertThat(newSession.getSelectedRoutes()).contains(route.getId());
// The StubMediaRoute2ProviderService is supposed to set control hints
// with the given controllerHints.
Bundle controlHints = newSession.getControlHints();
assertThat(controlHints).isNotNull();
- assertThat(controlHints.containsKey(TEST_KEY)).isTrue();
- assertThat(controlHints.getString(TEST_KEY)).isEqualTo(TEST_VALUE);
+ assertThat(controlHints).containsKey(TEST_KEY);
+ assertThat(controlHints).string(TEST_KEY).isEqualTo(TEST_VALUE);
successLatch.countDown();
}
@@ -891,11 +893,11 @@ public class MediaRouter2ManagerTest {
.collect(Collectors.toList());
for (MediaRoute2Info selectableRoute :
mManager.getSelectableRoutes(newSessionInfo)) {
- assertThat(selectedRoutes.contains(selectableRoute.getId())).isFalse();
+ assertThat(selectedRoutes).doesNotContain(selectableRoute.getId());
}
for (MediaRoute2Info deselectableRoute :
mManager.getDeselectableRoutes(newSessionInfo)) {
- assertThat(selectedRoutes.contains(deselectableRoute.getId())).isTrue();
+ assertThat(selectedRoutes).contains(deselectableRoute.getId());
}
onSessionCreatedLatch.countDown();
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 10d46d6db454..4fb575b0ab09 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -84,7 +84,7 @@ import java.util.List;
public class CompanionDeviceActivity extends FragmentActivity implements
CompanionVendorHelperDialogFragment.CompanionVendorHelperDialogListener {
private static final boolean DEBUG = false;
- private static final String TAG = CompanionDeviceActivity.class.getSimpleName();
+ private static final String TAG = "CDM_CompanionDeviceActivity";
// Keep the following constants in sync with
// frameworks/base/services/companion/java/
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java
index 93040b58d74b..e13e639cec85 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java
@@ -39,7 +39,7 @@ import android.widget.TextView;
*/
public class CompanionDeviceDataTransferActivity extends Activity {
- private static final String LOG_TAG = CompanionDeviceDataTransferActivity.class.getSimpleName();
+ private static final String LOG_TAG = "CDM_CompanionDeviceDataTransferActivity";
// Intent data keys from SystemDataTransferProcessor
private static final String EXTRA_PERMISSION_SYNC_REQUEST = "permission_sync_request";
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index fc5ff085139c..732e7340cf5a 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -70,7 +70,7 @@ import java.util.Objects;
*/
public class CompanionDeviceDiscoveryService extends Service {
private static final boolean DEBUG = false;
- private static final String TAG = CompanionDeviceDiscoveryService.class.getSimpleName();
+ private static final String TAG = "CDM_CompanionDeviceDiscoveryService";
private static final String SYS_PROP_DEBUG_TIMEOUT = "debug.cdm.discovery_timeout";
private static final long TIMEOUT_DEFAULT = 20_000L; // 20 seconds
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
index f2f6cb0cda7d..804e7577366b 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
@@ -44,7 +44,7 @@ import androidx.fragment.app.DialogFragment;
* A fragmentDialog shows additional information about selfManaged devices
*/
public class CompanionVendorHelperDialogFragment extends DialogFragment {
- private static final String TAG = CompanionVendorHelperDialogFragment.class.getSimpleName();
+ private static final String TAG = "CDM_CompanionVendorHelperDialogFragment";
private static final String ASSOCIATION_REQUEST_EXTRA = "association_request";
private CompanionVendorHelperDialogListener mListener;
diff --git a/packages/SettingsLib/AppPreference/res/layout/app_header_preference.xml b/packages/SettingsLib/AppPreference/res/layout/app_header_preference.xml
new file mode 100644
index 000000000000..a2389a9b83a2
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout/app_header_preference.xml
@@ -0,0 +1,81 @@
+<?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.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="24dp"
+ android:paddingBottom="16dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:scaleType="fitCenter"
+ android:antialias="true"/>
+
+ <TextView
+ android:id="@android:id/title"
+ style="@style/TextAppearance.EntityHeaderTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:gravity="center"
+ android:ellipsize="marquee"
+ android:textDirection="locale"
+ android:layout_marginTop="8dp"/>
+
+ <TextView
+ android:id="@+id/install_type"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:text="@string/install_type_instant"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:singleLine="false"
+ android:textAlignment="center"/>
+
+ <TextView
+ android:id="@+id/second_summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:singleLine="false"
+ android:maxLines="4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/AppPreference/res/values/strings.xml b/packages/SettingsLib/AppPreference/res/values/strings.xml
new file mode 100644
index 000000000000..ca148c00bbb5
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Added as the value of a header field indicating this is an instant app (as opposed to installed normally) -->
+ <string name="install_type_instant">Instant app</string>
+</resources>
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java
new file mode 100644
index 000000000000..60d00da29755
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppHeaderPreference.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * The Preference for the pages need to show big apps icon and name in the header of the page.
+ */
+public class AppHeaderPreference extends Preference {
+
+ private boolean mIsInstantApp;
+ private CharSequence mSecondSummary;
+
+ public AppHeaderPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ public AppHeaderPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public AppHeaderPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public AppHeaderPreference(Context context) {
+ super(context);
+ init();
+ }
+
+ private void init() {
+ setLayoutResource(R.layout.app_header_preference);
+ setSelectable(false);
+ setIsInstantApp(false);
+ }
+
+ /**
+ * Returns the installation type of this preference.
+ *
+ * @return whether the app is an instant app
+ * @see #setIsInstantApp(boolean)
+ */
+ public boolean getIsInstantApp() {
+ return mIsInstantApp;
+ }
+
+ /**
+ * Sets the installation type for this preference with a boolean.
+ *
+ * @param isInstantApp whether the app is an instant app
+ */
+ public void setIsInstantApp(@NonNull boolean isInstantApp) {
+ if (mIsInstantApp != isInstantApp) {
+ mIsInstantApp = isInstantApp;
+ notifyChanged();
+ }
+ }
+
+ /**
+ * Returns the second summary of this preference.
+ *
+ * @return The second summary
+ * @see #setSecondSummary(CharSequence)
+ */
+ @Nullable
+ public CharSequence getSecondSummary() {
+ return mSecondSummary;
+ }
+
+ /**
+ * Sets the second summary for this preference with a CharSequence.
+ *
+ * @param secondSummary The second summary for the preference
+ */
+ public void setSecondSummary(@Nullable CharSequence secondSummary) {
+ if (!TextUtils.equals(mSecondSummary, secondSummary)) {
+ mSecondSummary = secondSummary;
+ notifyChanged();
+ }
+ }
+
+ /**
+ * Sets the second summary for this preference with a resource ID.
+ *
+ * @param secondSummaryResId The second summary as a resource
+ * @see #setSecondSummary(CharSequence)
+ */
+ public void setSecondSummary(@StringRes int secondSummaryResId) {
+ setSecondSummary(getContext().getString(secondSummaryResId));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ final TextView installTypeView = (TextView) holder.findViewById(R.id.install_type);
+ if (installTypeView != null) {
+ if (mIsInstantApp) {
+ installTypeView.setVisibility(View.VISIBLE);
+ } else {
+ installTypeView.setVisibility(View.GONE);
+ }
+ }
+
+ final TextView secondSummaryView = (TextView) holder.findViewById(R.id.second_summary);
+ if (secondSummaryView != null) {
+ if (!TextUtils.isEmpty(mSecondSummary)) {
+ secondSummaryView.setText(mSecondSummary);
+ secondSummaryView.setVisibility(View.VISIBLE);
+ } else {
+ secondSummaryView.setVisibility(View.GONE);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
index 7968e96009b5..50f8e5466783 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -28,5 +28,6 @@ android_library {
"com.android.adservices",
"com.android.cellbroadcast",
"com.android.permission",
+ "com.android.healthconnect",
],
}
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
index ead5174f9ee2..0de7be0a5a08 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -43,6 +43,8 @@
<color name="settingslib_color_grey400">#bdc1c6</color>
<color name="settingslib_color_grey300">#dadce0</color>
<color name="settingslib_color_grey200">#e8eaed</color>
+ <color name="settingslib_color_grey100">#f1f3f4</color>
+ <color name="settingslib_color_grey50">#f8f9fa</color>
<color name="settingslib_color_orange600">#e8710a</color>
<color name="settingslib_color_orange400">#fa903e</color>
<color name="settingslib_color_orange300">#fcad70</color>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
new file mode 100644
index 000000000000..93b6acc9e160
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+
+import com.airbnb.lottie.LottieAnimationView;
+import com.airbnb.lottie.LottieProperty;
+import com.airbnb.lottie.model.KeyPath;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Util class which dynamically changes the color of tags in a lottie json file between Dark Theme
+ * (DT) and Light Theme (LT). This class assumes the json file is for Dark Theme.
+ */
+public class LottieColorUtils {
+ private static final Map<String, Integer> DARK_TO_LIGHT_THEME_COLOR_MAP;
+
+ static {
+ HashMap<String, Integer> map = new HashMap<>();
+ map.put(
+ ".grey600",
+ R.color.settingslib_color_grey300);
+ map.put(
+ ".grey800",
+ R.color.settingslib_color_grey200);
+ map.put(
+ ".grey900",
+ R.color.settingslib_color_grey50);
+ map.put(
+ ".red400",
+ R.color.settingslib_color_red600);
+ map.put(
+ ".black",
+ android.R.color.white);
+ map.put(
+ ".blue400",
+ R.color.settingslib_color_blue600);
+ map.put(
+ ".green400",
+ R.color.settingslib_color_green600);
+ DARK_TO_LIGHT_THEME_COLOR_MAP = Collections.unmodifiableMap(map);
+ }
+
+ private LottieColorUtils() {
+ }
+
+ private static boolean isDarkMode(Context context) {
+ return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ /** Applies dynamic colors based on DT vs. LT. The LottieAnimationView should be Dark Theme. */
+ public static void applyDynamicColors(Context context,
+ LottieAnimationView lottieAnimationView) {
+ // Assume the default for the lottie is dark mode
+ if (isDarkMode(context)) {
+ return;
+ }
+ for (String key : DARK_TO_LIGHT_THEME_COLOR_MAP.keySet()) {
+ final int color = context.getColor(DARK_TO_LIGHT_THEME_COLOR_MAP.get(key));
+ lottieAnimationView.addValueCallback(
+ new KeyPath("**", key, "**"),
+ LottieProperty.COLOR_FILTER,
+ frameInfo -> new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
+ }
+ }
+}
diff --git a/packages/SettingsLib/LayoutPreference/Android.bp b/packages/SettingsLib/LayoutPreference/Android.bp
index aaffdc922875..c29e1f789fd3 100644
--- a/packages/SettingsLib/LayoutPreference/Android.bp
+++ b/packages/SettingsLib/LayoutPreference/Android.bp
@@ -15,6 +15,7 @@ android_library {
static_libs: [
"androidx.preference_preference",
+ "SettingsLibSettingsTheme",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
index 2bdd6fe92e1a..f958037cfca6 100644
--- a/packages/SettingsLib/LayoutPreference/res/values/styles.xml
+++ b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
@@ -22,20 +22,6 @@
<item name="android:paddingEnd">16dp</item>
</style>
- <style name="TextAppearance.EntityHeaderTitle"
- parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:textSize">20sp</item>
- </style>
-
- <style name="TextAppearance.EntityHeaderSummary"
- parent="@android:style/TextAppearance.DeviceDefault">
- <item name="android:textAlignment">viewStart</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- <item name="android:singleLine">true</item>
- <item name="android:ellipsize">marquee</item>
- </style>
-
<style name="CrossProfileEntityHeaderIcon">
<item name="android:layout_width">48dp</item>
<item name="android:layout_height">48dp</item>
diff --git a/packages/SettingsLib/SettingsTheme/Android.bp b/packages/SettingsLib/SettingsTheme/Android.bp
index 1f69c8559d17..82e0220997d3 100644
--- a/packages/SettingsLib/SettingsTheme/Android.bp
+++ b/packages/SettingsLib/SettingsTheme/Android.bp
@@ -23,5 +23,6 @@ android_library {
"com.android.cellbroadcast",
"com.android.permission",
"com.android.adservices",
+ "com.android.healthconnect",
],
}
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
index 00bd14122251..328ab46ed2f9 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -33,4 +33,18 @@
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
+ <style name="TextAppearance.EntityHeaderTitle"
+ parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">20sp</item>
+ </style>
+
+ <style name="TextAppearance.EntityHeaderSummary"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:textAlignment">viewStart</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
+ </style>
+
</resources>
diff --git a/packages/SettingsLib/SettingsTransition/Android.bp b/packages/SettingsLib/SettingsTransition/Android.bp
index 708141b7090c..be77845992cf 100644
--- a/packages/SettingsLib/SettingsTransition/Android.bp
+++ b/packages/SettingsLib/SettingsTransition/Android.bp
@@ -23,5 +23,6 @@ android_library {
"com.android.adservices",
"com.android.cellbroadcast",
"com.android.permission",
+ "com.android.healthconnect",
],
}
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index 22299861e0e3..e5bf8ca60576 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -29,6 +29,10 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- </application>
+ <activity
+ android:name=".GalleryDebugActivity"
+ android:exported="true">
+ </activity>
+ </application>
</manifest>
diff --git a/packages/SettingsLib/Spa/gallery/res/raw/accessibility_shortcut_type_triple_tap.json b/packages/SettingsLib/Spa/gallery/res/raw/accessibility_shortcut_type_triple_tap.json
new file mode 100644
index 000000000000..870e671a51fe
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/res/raw/accessibility_shortcut_type_triple_tap.json
@@ -0,0 +1,1959 @@
+{
+ "v": "5.6.5",
+ "fr": 60,
+ "ip": 0,
+ "op": 180,
+ "w": 412,
+ "h": 300,
+ "nm": "Triple_Tap_Screen",
+ "ddd": 0,
+ "assets": [
+ {
+ "id": "comp_0",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": ".white",
+ "cl": "white",
+ "hd": true,
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 15.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -15.4
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -15.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 15.4
+ ]
+ ],
+ "v": [
+ [
+ 178,
+ 150
+ ],
+ [
+ -178,
+ 150
+ ],
+ [
+ -206,
+ 122
+ ],
+ [
+ -206,
+ -122
+ ],
+ [
+ -178,
+ -150
+ ],
+ [
+ 178,
+ -150
+ ],
+ [
+ 206,
+ -122
+ ],
+ [
+ 206,
+ 122
+ ]
+ ],
+ "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": "Group 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 1800,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ }
+ ],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": ".grey200",
+ "cl": "grey200",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 1.35,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -73.4
+ ],
+ [
+ -73.4,
+ 0
+ ],
+ [
+ 0,
+ 73.4
+ ],
+ [
+ 73.4,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ -73.4,
+ 0
+ ],
+ [
+ 0,
+ 73.4
+ ],
+ [
+ 73.4,
+ 0
+ ],
+ [
+ 0,
+ -73.4
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 1.4,
+ -132.9
+ ],
+ [
+ -131.6,
+ 0
+ ],
+ [
+ 1.3,
+ 132.9
+ ],
+ [
+ 134.3,
+ 0
+ ],
+ [
+ 1.4,
+ -132.9
+ ]
+ ],
+ "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,
+ 0
+ ],
+ [
+ -24.7,
+ -24.8
+ ],
+ [
+ 0,
+ -35
+ ],
+ [
+ 24.8,
+ -24.7
+ ],
+ [
+ 35,
+ 0
+ ],
+ [
+ 24.7,
+ 24.8
+ ],
+ [
+ 0,
+ 35
+ ],
+ [
+ -24.8,
+ 24.7
+ ],
+ [
+ -35,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 35,
+ 0
+ ],
+ [
+ 24.7,
+ 24.7
+ ],
+ [
+ 0,
+ 35
+ ],
+ [
+ -24.7,
+ 24.7
+ ],
+ [
+ -35,
+ 0
+ ],
+ [
+ -24.7,
+ -24.8
+ ],
+ [
+ 0,
+ -35
+ ],
+ [
+ 24.7,
+ -24.7
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 1.4,
+ -130.9
+ ],
+ [
+ 94,
+ -92.5
+ ],
+ [
+ 132.4,
+ 0.1
+ ],
+ [
+ 94,
+ 92.7
+ ],
+ [
+ 1.4,
+ 131.1
+ ],
+ [
+ -91.2,
+ 92.7
+ ],
+ [
+ -129.6,
+ 0
+ ],
+ [
+ -91.2,
+ -92.6
+ ],
+ [
+ 1.4,
+ -130.9
+ ]
+ ],
+ "c": false
+ },
+ "ix": 2
+ },
+ "nm": "Path 2",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.909803926945,
+ 0.917647063732,
+ 0.929411768913,
+ 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": "Group 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 300,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 2,
+ "ty": 4,
+ "nm": ".grey300",
+ "cl": "grey300",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 205,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ -7.9,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 8
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1.6
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ 1.5
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 6.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 6.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ -0.7
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 8,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1.6,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1.9,
+ -1.6
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 6.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -6.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ 0.7
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0.1,
+ 7.9
+ ]
+ ],
+ "v": [
+ [
+ -64,
+ 75.3
+ ],
+ [
+ 69.1,
+ 75.3
+ ],
+ [
+ 83.6,
+ 60.8
+ ],
+ [
+ 83.6,
+ -81
+ ],
+ [
+ 86.5,
+ -83.9
+ ],
+ [
+ 86.5,
+ -100.9
+ ],
+ [
+ 80.7,
+ -105.6
+ ],
+ [
+ 80.7,
+ 60.8
+ ],
+ [
+ 69.1,
+ 72.4
+ ],
+ [
+ -64,
+ 72.4
+ ],
+ [
+ -75.6,
+ 60.8
+ ],
+ [
+ -75.6,
+ -107.3
+ ],
+ [
+ -78.5,
+ -105.2
+ ],
+ [
+ -78.5,
+ 60.9
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Path 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.854901969433,
+ 0.86274510622,
+ 0.878431379795,
+ 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": "Group 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "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": "Group 1",
+ "np": 1,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 300,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 3,
+ "ty": 4,
+ "nm": "cursor 5",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 36,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 39.582,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 44.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 55.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 37.791,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 59,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.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": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 36,
+ "op": 59,
+ "st": -1,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 4,
+ "ty": 4,
+ "nm": "cursor 4",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 22,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 25.58,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 30.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 41.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 23.789,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 45,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.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": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 22,
+ "op": 45,
+ "st": -3,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 5,
+ "ty": 4,
+ "nm": "cursor",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 8,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 11.582,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 16.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 27.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 9.791,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 31,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.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": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 8,
+ "op": 31,
+ "st": -5,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 6,
+ "ty": 0,
+ "nm": "BG_White",
+ "refId": "comp_0",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "w": 412,
+ "h": 300,
+ "ip": 0,
+ "op": 1800,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": []
+} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
new file mode 100644
index 000000000000..317346d26f89
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
@@ -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.
+ */
+
+package com.android.settingslib.spa.gallery
+
+import com.android.settingslib.spa.framework.DebugActivity
+
+class GalleryDebugActivity : DebugActivity(SpaEnvironment.EntryRepository, MainActivity::class.java)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
index e75e76c32ea9..6f675a300ce3 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
@@ -51,7 +51,7 @@ object SpaEnvironment {
IllustrationPageProvider,
),
rootPages = listOf(
- SettingsPage(HomePageProvider.name)
+ SettingsPage.create(HomePageProvider.name)
) + ArgumentPageProvider.buildRootPages()
)
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
index 2428bba1dd4c..7c57e75f0c40 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
@@ -17,15 +17,12 @@
package com.android.settingslib.spa.gallery.home
import android.os.Bundle
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
-import com.android.settingslib.spa.gallery.SpaEnvironment
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
@@ -55,17 +52,6 @@ private fun HomePage() {
SettingsPagerPageProvider.EntryItem()
FooterPageProvider.EntryItem()
IllustrationPageProvider.EntryItem()
-
- /**
- * A test button to generate hierarchy.
- * TODO: remove it once the content provider is ready.
- */
- Button(onClick = {
- SpaEnvironment.EntryRepository.printAllPages()
- SpaEnvironment.EntryRepository.printAllEntries()
- }) {
- Text(text = "Generate Entry")
- }
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
index 5fe17e5de34c..5cce21577e43 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
@@ -68,7 +68,10 @@ object ArgumentPageProvider : SettingsPageProvider {
private fun buildInjectEntry(arguments: Bundle?): SettingsEntryBuilder? {
if (!ArgumentPageModel.isValidArgument(arguments)) return null
- return SettingsEntryBuilder.createInject(SettingsPage.create(name, parameter, arguments))
+ return SettingsEntryBuilder.createInject(
+ entryName = ArgumentPageModel.getInjectEntryName(arguments),
+ owner = SettingsPage.create(name, parameter, arguments)
+ )
// Set attributes
.setIsAllowSearch(false)
.setUiLayoutFn {
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
index 8b94342cf082..e27bf6dc5b33 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
@@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavType
import androidx.navigation.navArgument
+import com.android.settingslib.spa.framework.BrowseActivity
import com.android.settingslib.spa.framework.common.PageModel
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.stateOf
@@ -63,6 +64,10 @@ class ArgumentPageModel : PageModel() {
return (stringParam != null && listOf("foo", "bar").contains(stringParam))
}
+ fun getInjectEntryName(arguments: Bundle?): String {
+ return "${name}_${parameter.getStringArg(STRING_PARAM_NAME, arguments)}"
+ }
+
@Composable
fun create(arguments: Bundle?): ArgumentPageModel {
val pageModel: ArgumentPageModel = viewModel(key = arguments.toString())
@@ -75,12 +80,14 @@ class ArgumentPageModel : PageModel() {
private var arguments: Bundle? = null
private var stringParam: String? = null
private var intParam: Int? = null
+ private var highlightName: String? = null
override fun initialize(arguments: Bundle?) {
logMsg("init with args " + arguments.toString())
this.arguments = arguments
stringParam = parameter.getStringArg(STRING_PARAM_NAME, arguments)
intParam = parameter.getIntArg(INT_PARAM_NAME, arguments)
+ highlightName = arguments?.getString(BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME)
}
@Composable
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt
index 5db6e7ac1d36..4f8ea0b75e0a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/IllustrationPage.kt
@@ -54,6 +54,15 @@ object IllustrationPageProvider : SettingsPageProvider {
private fun IllustrationPage() {
Column(Modifier.verticalScroll(rememberScrollState())) {
Preference(object : PreferenceModel {
+ override val title = "Lottie Illustration"
+ })
+
+ Illustration(object : IllustrationModel {
+ override val resId = R.raw.accessibility_shortcut_type_triple_tap
+ override val resourceType = ResourceType.LOTTIE
+ })
+
+ Preference(object : PreferenceModel {
override val title = "Image Illustration"
})
diff --git a/packages/SettingsLib/Spa/spa/Android.bp b/packages/SettingsLib/Spa/spa/Android.bp
index 3c8d91edc05b..a4928e6608ab 100644
--- a/packages/SettingsLib/Spa/spa/Android.bp
+++ b/packages/SettingsLib/Spa/spa/Android.bp
@@ -30,6 +30,7 @@ android_library {
"androidx.compose.ui_ui-tooling-preview",
"androidx.navigation_navigation-compose",
"com.google.android.material_material",
+ "lottie_compose",
],
kotlincflags: [
"-Xjvm-default=all",
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 87d4f5a936f8..ae15da66d68b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -26,7 +26,9 @@ import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navArgument
import com.android.settingslib.spa.R
+import com.android.settingslib.spa.framework.common.ROOT_PAGE_NAME
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.theme.SettingsTheme
@@ -48,22 +50,28 @@ open class BrowseActivity(
@Composable
private fun MainContent() {
- val destination = intent?.getStringExtra(KEY_DESTINATION)
+ val destination =
+ intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPageName()
val navController = rememberNavController()
CompositionLocalProvider(navController.localNavController()) {
- NavHost(navController, sppRepository.getDefaultStartPageName()) {
+ NavHost(navController, ROOT_PAGE_NAME) {
+ composable(ROOT_PAGE_NAME) {}
for (page in sppRepository.getAllProviders()) {
composable(
- route = page.name + page.parameter.navRoute(),
- arguments = page.parameter,
+ route = page.name + page.parameter.navRoute() +
+ "?$HIGHLIGHT_ENTRY_PARAM_NAME={$HIGHLIGHT_ENTRY_PARAM_NAME}",
+ arguments = page.parameter + listOf(
+ // add optional parameters
+ navArgument(HIGHLIGHT_ENTRY_PARAM_NAME) { defaultValue = "null" }
+ ),
) { navBackStackEntry ->
page.Page(navBackStackEntry.arguments)
}
}
}
- if (!destination.isNullOrEmpty()) {
+ if (destination.isNotEmpty()) {
LaunchedEffect(Unit) {
navController.navigate(destination) {
popUpTo(navController.graph.findStartDestination().id) {
@@ -77,5 +85,6 @@ open class BrowseActivity(
companion object {
const val KEY_DESTINATION = "spa:SpaActivity:destination"
+ const val HIGHLIGHT_ENTRY_PARAM_NAME = "highlightEntry"
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
new file mode 100644
index 000000000000..095e6833b613
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework
+
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.navigation.NavType
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navArgument
+import com.android.settingslib.spa.R
+import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_DESTINATION
+import com.android.settingslib.spa.framework.common.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsEntryRepository
+import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.compose.localNavController
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.HomeScaffold
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val ROUTE_ROOT = "root"
+private const val ROUTE_All_PAGES = "pages"
+private const val ROUTE_All_ENTRIES = "entries"
+private const val ROUTE_PAGE = "page"
+private const val ROUTE_ENTRY = "entry"
+private const val PARAM_NAME_PAGE_ID = "pid"
+private const val PARAM_NAME_ENTRY_ID = "eid"
+
+open class DebugActivity(
+ private val entryRepository: SettingsEntryRepository,
+ private val browseActivityClass: Class<*>,
+) : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setTheme(R.style.Theme_SpaLib_DayNight)
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ SettingsTheme {
+ MainContent()
+ }
+ }
+ }
+
+ @Composable
+ private fun MainContent() {
+ val navController = rememberNavController()
+ CompositionLocalProvider(navController.localNavController()) {
+ NavHost(navController, ROUTE_ROOT) {
+ composable(route = ROUTE_ROOT) { RootPage() }
+ composable(route = ROUTE_All_PAGES) { AllPages() }
+ composable(route = ROUTE_All_ENTRIES) { AllEntries() }
+ composable(
+ route = "$ROUTE_PAGE/{$PARAM_NAME_PAGE_ID}",
+ arguments = listOf(
+ navArgument(PARAM_NAME_PAGE_ID) { type = NavType.IntType },
+ )
+ ) { navBackStackEntry -> OnePage(navBackStackEntry.arguments) }
+ composable(
+ route = "$ROUTE_ENTRY/{$PARAM_NAME_ENTRY_ID}",
+ arguments = listOf(
+ navArgument(PARAM_NAME_ENTRY_ID) { type = NavType.IntType },
+ )
+ ) { navBackStackEntry -> OneEntry(navBackStackEntry.arguments) }
+ }
+ }
+ }
+
+ @Composable
+ fun RootPage() {
+ HomeScaffold(title = "Settings Debug") {
+ Preference(object : PreferenceModel {
+ override val title = "List All Pages"
+ override val onClick = navigator(route = ROUTE_All_PAGES)
+ })
+ Preference(object : PreferenceModel {
+ override val title = "List All Entries"
+ override val onClick = navigator(route = ROUTE_All_ENTRIES)
+ })
+ }
+ }
+
+ @Composable
+ fun AllPages() {
+ RegularScaffold(title = "All Pages") {
+ for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
+ Preference(object : PreferenceModel {
+ override val title =
+ "${pageWithEntry.page.displayName} (${pageWithEntry.entries.size})"
+ override val summary = pageWithEntry.page.formatArguments().toState()
+ override val onClick =
+ navigator(route = ROUTE_PAGE + "/${pageWithEntry.page.id}")
+ })
+ }
+ }
+ }
+
+ @Composable
+ fun AllEntries() {
+ RegularScaffold(title = "All Entries") {
+ EntryList(entryRepository.getAllEntries())
+ }
+ }
+
+ @Composable
+ fun OnePage(arguments: Bundle?) {
+ val id = arguments!!.getInt(PARAM_NAME_PAGE_ID)
+ val pageWithEntry = entryRepository.getPageWithEntry(id)!!
+ RegularScaffold(title = "Page ${pageWithEntry.page.displayName}") {
+ Text(text = pageWithEntry.page.formatArguments())
+ Text(text = "Entry size: ${pageWithEntry.entries.size}")
+ Preference(model = object : PreferenceModel {
+ override val title = "open page"
+ override val onClick = openPage(pageWithEntry.page)
+ })
+ EntryList(pageWithEntry.entries)
+ }
+ }
+
+ @Composable
+ fun OneEntry(arguments: Bundle?) {
+ val id = arguments!!.getInt(PARAM_NAME_ENTRY_ID)
+ val entry = entryRepository.getEntry(id)!!
+ RegularScaffold(title = "Entry ${entry.displayName}") {
+ Preference(model = object : PreferenceModel {
+ override val title = "open entry"
+ override val onClick = openEntry(entry)
+ })
+ Text(text = entry.formatAll())
+ }
+ }
+
+ @Composable
+ private fun EntryList(entries: Collection<SettingsEntry>) {
+ for (entry in entries) {
+ Preference(object : PreferenceModel {
+ override val title = entry.displayName
+ override val summary =
+ "${entry.fromPage?.displayName} -> ${entry.toPage?.displayName}".toState()
+ override val onClick = navigator(route = ROUTE_ENTRY + "/${entry.id}")
+ })
+ }
+ }
+
+ @Composable
+ private fun openPage(page: SettingsPage): () -> Unit {
+ val route = page.buildRoute()
+ val context = LocalContext.current
+ val intent = Intent(context, browseActivityClass).apply {
+ putExtra(KEY_DESTINATION, route)
+ }
+ return {
+ Log.d("DEBUG ACTIVITY", "Open page: $route")
+ context.startActivity(intent)
+ }
+ }
+
+ @Composable
+ private fun openEntry(entry: SettingsEntry): () -> Unit {
+ val route = entry.buildRoute()
+ val context = LocalContext.current
+ val intent = Intent(context, browseActivityClass).apply {
+ putExtra(KEY_DESTINATION, route)
+ }
+ return {
+ Log.d("DEBUG ACTIVITY", "Open entry: $route")
+ context.startActivity(intent)
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
index 98734dac648e..86e75f3d018c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
@@ -19,10 +19,13 @@ package com.android.settingslib.spa.framework.common
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.navigation.NamedNavArgument
+import com.android.settingslib.spa.framework.BrowseActivity
+import com.android.settingslib.spa.framework.util.navLink
import com.android.settingslib.spa.framework.util.normalize
const val INJECT_ENTRY_NAME = "INJECT"
const val ROOT_ENTRY_NAME = "ROOT"
+const val ROOT_PAGE_NAME = "Root"
/**
* Defines data of one Settings entry for Settings search.
@@ -37,38 +40,70 @@ data class UiData(val title: String = "")
/**
* Defines data to identify a Settings page.
*/
-data class SettingsPage(val name: String = "", val arguments: Bundle? = null) {
- override fun toString(): String {
- val argsStr = arguments?.toString()?.removeRange(0, 6) ?: ""
- return name + argsStr
- }
+data class SettingsPage(
+ // The unique id of this page, which is computed by name + normalized(arguments)
+ val id: Int,
+
+ // The name of the page, which is used to compute the unique id, and need to be stable.
+ val name: String,
+
+ // The display name of the page, for better readability.
+ // By default, it is the same as name.
+ val displayName: String,
+ // Defined parameters of this page.
+ val parameter: List<NamedNavArgument> = emptyList(),
+
+ // The arguments of this page.
+ val arguments: Bundle? = null,
+) {
companion object {
fun create(
name: String,
parameter: List<NamedNavArgument> = emptyList(),
arguments: Bundle? = null
): SettingsPage {
- return SettingsPage(name, parameter.normalize(arguments))
+ return SettingsPageBuilder(name, parameter).setArguments(arguments).build()
}
}
+
+ fun formatArguments(): String {
+ val normalizedArguments = parameter.normalize(arguments)
+ if (normalizedArguments == null || normalizedArguments.isEmpty) return "[No arguments]"
+ return normalizedArguments.toString().removeRange(0, 6)
+ }
+
+ fun formatAll(): String {
+ return "$displayName ${formatArguments()}"
+ }
+
+ fun buildRoute(highlightEntryName: String? = null): String {
+ val highlightParam =
+ if (highlightEntryName == null)
+ ""
+ else
+ "?${BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME}=$highlightEntryName"
+ return name + parameter.navLink(arguments) + highlightParam
+ }
}
/**
* Defines data of a Settings entry.
*/
data class SettingsEntry(
- // The unique id of this entry.
- // By default, it is computed by name + owner + fromPage + toPage
- val id: String,
-
- // The display name of this entry, which is used to be shown in hierarchy.
- // By default, it is computed by name + owner
- val displayName: String,
+ // The unique id of this entry, which is computed by name + owner + fromPage + toPage.
+ val id: Int,
+ // The name of the page, which is used to compute the unique id, and need to be stable.
val name: String,
+
+ // The owner page of this entry.
val owner: SettingsPage,
+ // The display name of the entry, for better readability.
+ // By default, it is $owner:$name
+ val displayName: String,
+
// Defines linking of Settings entries
val fromPage: SettingsPage? = null,
val toPage: SettingsPage? = null,
@@ -106,8 +141,48 @@ data class SettingsEntry(
*/
val uiLayout: (@Composable () -> Unit) = {},
) {
- override fun toString(): String {
- return displayName + "(${fromPage?.toString()}->${toPage?.toString()})"
+ fun formatAll(): String {
+ val content = listOf<String>(
+ "owner = ${owner.formatAll()}",
+ "linkFrom = ${fromPage?.formatAll()}",
+ "linkTo = ${toPage?.formatAll()}",
+ )
+ return content.joinToString("\n")
+ }
+
+ fun buildRoute(): String {
+ // Open entry in its fromPage.
+ val page = fromPage ?: owner
+ return page.buildRoute(name)
+ }
+}
+
+data class SettingsPageWithEntry(
+ val page: SettingsPage,
+ val entries: List<SettingsEntry>,
+)
+
+class SettingsPageBuilder(
+ private val name: String,
+ private val parameter: List<NamedNavArgument> = emptyList()
+) {
+ private var displayName: String? = null
+ private var arguments: Bundle? = null
+
+ fun build(): SettingsPage {
+ val normArguments = parameter.normalize(arguments)
+ return SettingsPage(
+ id = "$name:${normArguments?.toString()}".toUniqueId(),
+ name = name,
+ displayName = displayName ?: name,
+ parameter = parameter,
+ arguments = arguments,
+ )
+ }
+
+ fun setArguments(arguments: Bundle?): SettingsPageBuilder {
+ this.arguments = arguments
+ return this
}
}
@@ -115,7 +190,6 @@ data class SettingsEntry(
* The helper to build a Settings Entry instance.
*/
class SettingsEntryBuilder(private val name: String, private val owner: SettingsPage) {
- private var uniqueId: String? = null
private var displayName: String? = null
private var fromPage: SettingsPage? = null
private var toPage: SettingsPage? = null
@@ -126,8 +200,8 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
fun build(): SettingsEntry {
return SettingsEntry(
- id = computeUniqueId(),
- displayName = computeDisplayName(),
+ id = "$name:${owner.id}(${fromPage?.id}-${toPage?.id})".toUniqueId(),
+ displayName = displayName ?: "${owner.displayName}:$name",
name = name,
owner = owner,
@@ -136,7 +210,7 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
toPage = toPage,
// attributes
- isAllowSearch = computeSearchable(),
+ isAllowSearch = getIsSearchable(),
// functions
searchData = searchDataFn,
@@ -168,12 +242,7 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
return this
}
- private fun computeUniqueId(): String =
- uniqueId ?: "$owner:$name" + fromPage?.toString() + toPage?.toString()
-
- private fun computeDisplayName(): String = displayName ?: "$owner:$name"
-
- private fun computeSearchable(): Boolean = isAllowSearch ?: false
+ private fun getIsSearchable(): Boolean = isAllowSearch ?: false
companion object {
fun create(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
@@ -188,12 +257,18 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
return create(entryName, owner).setLink(toPage = owner)
}
- fun createInject(owner: SettingsPage): SettingsEntryBuilder {
- return createLinkTo(INJECT_ENTRY_NAME, owner)
+ fun createInject(owner: SettingsPage, entryName: String? = null): SettingsEntryBuilder {
+ val name = entryName ?: "${INJECT_ENTRY_NAME}_${owner.name}"
+ return createLinkTo(name, owner)
}
- fun createRoot(page: SettingsPage): SettingsEntryBuilder {
- return createLinkTo(ROOT_ENTRY_NAME, page)
+ fun createRoot(owner: SettingsPage, entryName: String? = null): SettingsEntryBuilder {
+ val name = entryName ?: "${ROOT_ENTRY_NAME}_${owner.name}"
+ return createLinkTo(name, owner)
}
}
}
+
+private fun String.toUniqueId(): Int {
+ return this.hashCode()
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
index e3a55e54c431..a4e5a58246ff 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
@@ -26,19 +26,19 @@ private const val MAX_ENTRY_SIZE = 5000
*/
class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
// Map of entry unique Id to entry
- private val entryMap: Map<String, SettingsEntry>
+ private val entryMap: Map<Int, SettingsEntry>
// Map of Settings page to its contained entries.
- private val pageToEntryListMap: Map<String, List<SettingsEntry>>
+ private val pageWithEntryMap: Map<Int, SettingsPageWithEntry>
init {
logMsg("Initialize")
entryMap = mutableMapOf()
- pageToEntryListMap = mutableMapOf()
+ pageWithEntryMap = mutableMapOf()
val entryQueue = LinkedList<SettingsEntry>()
for (page in sppRepository.getAllRootPages()) {
- val rootEntry = SettingsEntryBuilder.createRoot(page).build()
+ val rootEntry = SettingsEntryBuilder.createRoot(owner = page).build()
if (!entryMap.containsKey(rootEntry.id)) {
entryQueue.push(rootEntry)
entryMap.put(rootEntry.id, rootEntry)
@@ -48,10 +48,10 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
while (entryQueue.isNotEmpty() && entryMap.size < MAX_ENTRY_SIZE) {
val entry = entryQueue.pop()
val page = entry.toPage
- if (page == null || pageToEntryListMap.containsKey(page.toString())) continue
+ if (page == null || pageWithEntryMap.containsKey(page.id)) continue
val spp = sppRepository.getProviderOrNull(page.name) ?: continue
val newEntries = spp.buildEntry(page.arguments)
- pageToEntryListMap[page.toString()] = newEntries
+ pageWithEntryMap[page.id] = SettingsPageWithEntry(page, newEntries)
for (newEntry in newEntries) {
if (!entryMap.containsKey(newEntry.id)) {
entryQueue.push(newEntry)
@@ -60,19 +60,23 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
}
}
- logMsg("Initialize Completed: ${entryMap.size} entries in ${pageToEntryListMap.size} pages")
+ logMsg("Initialize Completed: ${entryMap.size} entries in ${pageWithEntryMap.size} pages")
}
- fun printAllPages() {
- for (entry in pageToEntryListMap.entries) {
- logMsg("page: ${entry.key} with ${entry.value.size} entries")
- }
+ fun getAllPageWithEntry(): Collection<SettingsPageWithEntry> {
+ return pageWithEntryMap.values
}
- fun printAllEntries() {
- for (entry in entryMap.values) {
- logMsg("entry: $entry")
- }
+ fun getPageWithEntry(pageId: Int): SettingsPageWithEntry? {
+ return pageWithEntryMap[pageId]
+ }
+
+ fun getAllEntries(): Collection<SettingsEntry> {
+ return entryMap.values
+ }
+
+ fun getEntry(entryId: Int): SettingsEntry? {
+ return entryMap[entryId]
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/Illustration.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/Illustration.kt
index 0d4e0c505943..cd8a02a10b3d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/Illustration.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/Illustration.kt
@@ -29,7 +29,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.ui.ImageBox
-
+import com.android.settingslib.spa.widget.ui.Lottie
enum class ResourceType { IMAGE, LOTTIE }
/**
@@ -72,7 +72,7 @@ fun Illustration(
Column(
modifier = modifier
.fillMaxWidth()
- .padding(SettingsDimension.illustrationPadding),
+ .padding(horizontal = SettingsDimension.illustrationPadding),
horizontalAlignment = Alignment.CenterHorizontally,
) {
val illustrationModifier = modifier
@@ -85,7 +85,10 @@ fun Illustration(
when (resourceType) {
ResourceType.LOTTIE -> {
- // TODO: Add Lottie function after lottie is enabled.
+ Lottie(
+ resId = resId,
+ modifier = illustrationModifier,
+ )
}
ResourceType.IMAGE -> {
ImageBox(
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
new file mode 100644
index 000000000000..915c6e2456bc
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.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.settingslib.spa.widget.ui
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import com.airbnb.lottie.compose.LottieAnimation
+import com.airbnb.lottie.compose.LottieCompositionSpec
+import com.airbnb.lottie.compose.LottieConstants
+import com.airbnb.lottie.compose.animateLottieCompositionAsState
+import com.airbnb.lottie.compose.rememberLottieComposition
+
+@Composable
+fun Lottie(
+ resId: Int,
+ modifier: Modifier = Modifier,
+) {
+ Box(
+ modifier = modifier,
+ ) {
+ BaseLottie(resId)
+ }
+}
+
+@Composable
+private fun BaseLottie(resId: Int) {
+ val composition by rememberLottieComposition(
+ LottieCompositionSpec.RawRes(resId)
+ )
+ val progress by animateLottieCompositionAsState(
+ composition,
+ iterations = LottieConstants.IterateForever,
+ )
+ LottieAnimation(
+ composition = composition,
+ progress = { progress },
+ )
+}
diff --git a/packages/SettingsLib/Spa/tests/res/raw/accessibility_shortcut_type_triple_tap.json b/packages/SettingsLib/Spa/tests/res/raw/accessibility_shortcut_type_triple_tap.json
new file mode 100644
index 000000000000..870e671a51fe
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/res/raw/accessibility_shortcut_type_triple_tap.json
@@ -0,0 +1,1959 @@
+{
+ "v": "5.6.5",
+ "fr": 60,
+ "ip": 0,
+ "op": 180,
+ "w": 412,
+ "h": 300,
+ "nm": "Triple_Tap_Screen",
+ "ddd": 0,
+ "assets": [
+ {
+ "id": "comp_0",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": ".white",
+ "cl": "white",
+ "hd": true,
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 15.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -15.4
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -15.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 15.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 15.4
+ ]
+ ],
+ "v": [
+ [
+ 178,
+ 150
+ ],
+ [
+ -178,
+ 150
+ ],
+ [
+ -206,
+ 122
+ ],
+ [
+ -206,
+ -122
+ ],
+ [
+ -178,
+ -150
+ ],
+ [
+ 178,
+ -150
+ ],
+ [
+ 206,
+ -122
+ ],
+ [
+ 206,
+ 122
+ ]
+ ],
+ "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": "Group 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 1800,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ }
+ ],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": ".grey200",
+ "cl": "grey200",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 1.35,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -73.4
+ ],
+ [
+ -73.4,
+ 0
+ ],
+ [
+ 0,
+ 73.4
+ ],
+ [
+ 73.4,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ -73.4,
+ 0
+ ],
+ [
+ 0,
+ 73.4
+ ],
+ [
+ 73.4,
+ 0
+ ],
+ [
+ 0,
+ -73.4
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 1.4,
+ -132.9
+ ],
+ [
+ -131.6,
+ 0
+ ],
+ [
+ 1.3,
+ 132.9
+ ],
+ [
+ 134.3,
+ 0
+ ],
+ [
+ 1.4,
+ -132.9
+ ]
+ ],
+ "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,
+ 0
+ ],
+ [
+ -24.7,
+ -24.8
+ ],
+ [
+ 0,
+ -35
+ ],
+ [
+ 24.8,
+ -24.7
+ ],
+ [
+ 35,
+ 0
+ ],
+ [
+ 24.7,
+ 24.8
+ ],
+ [
+ 0,
+ 35
+ ],
+ [
+ -24.8,
+ 24.7
+ ],
+ [
+ -35,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 35,
+ 0
+ ],
+ [
+ 24.7,
+ 24.7
+ ],
+ [
+ 0,
+ 35
+ ],
+ [
+ -24.7,
+ 24.7
+ ],
+ [
+ -35,
+ 0
+ ],
+ [
+ -24.7,
+ -24.8
+ ],
+ [
+ 0,
+ -35
+ ],
+ [
+ 24.7,
+ -24.7
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 1.4,
+ -130.9
+ ],
+ [
+ 94,
+ -92.5
+ ],
+ [
+ 132.4,
+ 0.1
+ ],
+ [
+ 94,
+ 92.7
+ ],
+ [
+ 1.4,
+ 131.1
+ ],
+ [
+ -91.2,
+ 92.7
+ ],
+ [
+ -129.6,
+ 0
+ ],
+ [
+ -91.2,
+ -92.6
+ ],
+ [
+ 1.4,
+ -130.9
+ ]
+ ],
+ "c": false
+ },
+ "ix": 2
+ },
+ "nm": "Path 2",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.909803926945,
+ 0.917647063732,
+ 0.929411768913,
+ 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": "Group 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 300,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 2,
+ "ty": 4,
+ "nm": ".grey300",
+ "cl": "grey300",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 205,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ -7.9,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 8
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1.6
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ 1.5
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 6.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 6.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ -0.7
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 8,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1.6,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1.9,
+ -1.6
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 6.4
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -6.4,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ 0.7
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0.1,
+ 7.9
+ ]
+ ],
+ "v": [
+ [
+ -64,
+ 75.3
+ ],
+ [
+ 69.1,
+ 75.3
+ ],
+ [
+ 83.6,
+ 60.8
+ ],
+ [
+ 83.6,
+ -81
+ ],
+ [
+ 86.5,
+ -83.9
+ ],
+ [
+ 86.5,
+ -100.9
+ ],
+ [
+ 80.7,
+ -105.6
+ ],
+ [
+ 80.7,
+ 60.8
+ ],
+ [
+ 69.1,
+ 72.4
+ ],
+ [
+ -64,
+ 72.4
+ ],
+ [
+ -75.6,
+ 60.8
+ ],
+ [
+ -75.6,
+ -107.3
+ ],
+ [
+ -78.5,
+ -105.2
+ ],
+ [
+ -78.5,
+ 60.9
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Path 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.854901969433,
+ 0.86274510622,
+ 0.878431379795,
+ 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": "Group 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "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": "Group 1",
+ "np": 1,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 300,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 3,
+ "ty": 4,
+ "nm": "cursor 5",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 36,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 39.582,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 44.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 55.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 37.791,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 59,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.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": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 36,
+ "op": 59,
+ "st": -1,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 4,
+ "ty": 4,
+ "nm": "cursor 4",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 22,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 25.58,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 30.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 41.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 23.789,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 45,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.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": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 22,
+ "op": 45,
+ "st": -3,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 5,
+ "ty": 4,
+ "nm": "cursor",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 8,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 11.582,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 16.953,
+ "s": [
+ 100
+ ]
+ },
+ {
+ "t": 27.697265625,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 207.641,
+ 154.48,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0,
+ 0,
+ 0
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.45,
+ 0.45,
+ 0.45
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 9.791,
+ "s": [
+ 27.252,
+ 27.252,
+ 100
+ ]
+ },
+ {
+ "t": 31,
+ "s": [
+ 56.661,
+ 56.661,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 63.109,
+ 63.109
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.182245725744,
+ 0.894323072246,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.522196631338,
+ 0.9762855081,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 50,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ -180.5,
+ -165.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": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 8,
+ "op": 31,
+ "st": -5,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 6,
+ "ty": 0,
+ "nm": "BG_White",
+ "refId": "comp_0",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 206,
+ 150,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "w": 412,
+ "h": 300,
+ "ip": 0,
+ "op": 1800,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": []
+} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/res/raw/empty.json b/packages/SettingsLib/Spa/tests/res/raw/empty.json
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/res/raw/empty.json
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/IllustrationTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/IllustrationTest.kt
index d2a07e9e041c..54abec9ac0d7 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/IllustrationTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/IllustrationTest.kt
@@ -17,12 +17,14 @@
package com.android.settingslib.spa.widget
import androidx.annotation.DrawableRes
+import androidx.annotation.RawRes
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.SemanticsPropertyKey
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.junit4.createComposeRule
@@ -54,9 +56,32 @@ class IllustrationTest {
fun hasDrawable(@DrawableRes id: Int): SemanticsMatcher =
SemanticsMatcher.expectValue(DrawableId, id)
- val isIllustrationNode = !hasAnyAncestor(hasDrawable(resId))
+ val isIllustrationNode = hasAnyAncestor(hasDrawable(resId))
composeTestRule.onAllNodes(hasDrawable(resId))
.filterToOne(isIllustrationNode)
.assertIsDisplayed()
}
+
+ private val RawId = SemanticsPropertyKey<Int>("RawResId")
+ private var SemanticsPropertyReceiver.rawId by RawId
+
+ @Test
+ fun empty_lottie_not_displayed() {
+ val resId = R.raw.empty
+ composeTestRule.setContent {
+ Illustration(
+ resId = resId,
+ resourceType = ResourceType.LOTTIE,
+ modifier = Modifier.semantics { rawId = resId }
+ )
+ }
+
+ fun hasRaw(@RawRes id: Int): SemanticsMatcher =
+ SemanticsMatcher.expectValue(RawId, id)
+
+ val isIllustrationNode = hasAnyAncestor(hasRaw(resId))
+ composeTestRule.onAllNodes(hasRaw(resId))
+ .filterToOne(isIllustrationNode)
+ .assertIsNotDisplayed()
+ }
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
index 67fa27827843..d537ec258cad 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
@@ -44,6 +44,7 @@ import com.android.settingslib.spaprivileged.template.common.WorkProfilePager
fun <T : AppRecord> AppListPage(
title: String,
listModel: AppListModel<T>,
+ primaryUserOnly: Boolean = false,
appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
) {
val showSystem = rememberSaveable { mutableStateOf(false) }
@@ -55,7 +56,7 @@ fun <T : AppRecord> AppListPage(
},
) { paddingValues ->
Spacer(Modifier.padding(paddingValues))
- WorkProfilePager { userInfo ->
+ WorkProfilePager(primaryUserOnly) { userInfo ->
Column(Modifier.fillMaxSize()) {
val options = remember { listModel.getSpinnerOptions() }
val selectedOption = rememberSaveable { mutableStateOf(0) }
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index d4d8ea456741..c031fe808346 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -82,7 +82,7 @@ internal class TogglePermissionAppInfoPageProvider(
internal fun navigator(permissionType: String, app: ApplicationInfo) =
navigator(route = "$PAGE_NAME/$permissionType/${app.toRoute()}")
- internal fun buildPageId(permissionType: String): SettingsPage {
+ internal fun buildPageData(permissionType: String): SettingsPage {
return SettingsPage.create(
PAGE_NAME, PAGE_PARAMETER, bundleOf(PERMISSION to permissionType))
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index f91a34a131c5..65cc4f9a165c 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -56,7 +56,7 @@ internal class TogglePermissionAppListPageProvider(
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val permissionType = parameter.getStringArg(PERMISSION, arguments)!!
val appListPage = SettingsPage.create(name, parameter, arguments)
- val appInfoPage = TogglePermissionAppInfoPageProvider.buildPageId(permissionType)
+ val appInfoPage = TogglePermissionAppInfoPageProvider.buildPageData(permissionType)
val entryList = mutableListOf<SettingsEntry>()
// TODO: add more categories, such as personal, work, cloned, etc.
for (category in listOf("personal")) {
@@ -110,7 +110,7 @@ internal class TogglePermissionAppListPageProvider(
internal fun buildInjectEntry(permissionType: String): SettingsEntryBuilder {
val appListPage = SettingsPage.create(
PAGE_NAME, PAGE_PARAMETER, bundleOf(PERMISSION to permissionType))
- return SettingsEntryBuilder.createInject(appListPage).setIsAllowSearch(false)
+ return SettingsEntryBuilder.createInject(owner = appListPage).setIsAllowSearch(false)
}
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
index aa5ccf146d47..a76c4385b69e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
@@ -26,11 +26,16 @@ import com.android.settingslib.spa.widget.scaffold.SettingsPager
import com.android.settingslib.spaprivileged.model.enterprise.EnterpriseRepository
@Composable
-fun WorkProfilePager(content: @Composable (userInfo: UserInfo) -> Unit) {
+fun WorkProfilePager(
+ primaryUserOnly: Boolean = false,
+ content: @Composable (userInfo: UserInfo) -> Unit,
+) {
val context = LocalContext.current
val profiles = remember {
val userManager = checkNotNull(context.getSystemService(UserManager::class.java))
- userManager.getProfiles(UserHandle.myUserId())
+ userManager.getProfiles(UserHandle.myUserId()).filter { userInfo ->
+ !primaryUserOnly || userInfo.isPrimary
+ }
}
val titles = remember {
val enterpriseRepository = EnterpriseRepository(context)
diff --git a/packages/SettingsLib/TopIntroPreference/Android.bp b/packages/SettingsLib/TopIntroPreference/Android.bp
index ecf2a72021ed..9e865679069c 100644
--- a/packages/SettingsLib/TopIntroPreference/Android.bp
+++ b/packages/SettingsLib/TopIntroPreference/Android.bp
@@ -23,5 +23,6 @@ android_library {
apex_available: [
"//apex_available:platform",
"com.android.cellbroadcast",
+ "com.android.healthconnect",
],
}
diff --git a/packages/SettingsLib/Utils/Android.bp b/packages/SettingsLib/Utils/Android.bp
index 1b002613f90a..dc88304e1d3c 100644
--- a/packages/SettingsLib/Utils/Android.bp
+++ b/packages/SettingsLib/Utils/Android.bp
@@ -26,5 +26,6 @@ android_library {
"com.android.adservices",
"com.android.permission",
"com.android.cellbroadcast",
+ "com.android.healthconnect",
],
}
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index d8ed7ed03480..5b3aae3304c0 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -597,7 +597,7 @@
<string name="guest_reset_guest" msgid="6110013010356013758">"கெஸ்ட் அமர்வை மீட்டமை"</string>
<string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"கெஸ்ட்டை மீட்டமைக்கவா?"</string>
<string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"கெஸ்ட் பயனரை அகற்றவா?"</string>
- <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"மீட்டமை"</string>
+ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ரீசெட்"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"அகற்று"</string>
<string name="guest_resetting" msgid="7822120170191509566">"கெஸ்ட்டை மீட்டமைக்கிறது…"</string>
<string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"கெஸ்ட் அமர்வை ரீசெட் செய்யவா?"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
index 63a9f0c5c7f4..adfa39e3df80 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
@@ -21,8 +21,6 @@ import android.content.ClipData;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -59,9 +57,9 @@ class AvatarPhotoController {
void startActivityForResult(Intent intent, int resultCode);
- boolean startSystemActivityForResult(Intent intent, int resultCode);
-
int getPhotoSize();
+
+ boolean canCropPhoto();
}
interface ContextInjector {
@@ -84,7 +82,6 @@ class AvatarPhotoController {
private static final long DELAY_BEFORE_CROP_MILLIS = 150;
private static final String IMAGES_DIR = "multi_user";
- private static final String PRE_CROP_PICTURE_FILE_NAME = "PreCropEditUserPhoto.jpg";
private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg";
@@ -94,7 +91,6 @@ class AvatarPhotoController {
private final ContextInjector mContextInjector;
private final File mImagesDir;
- private final Uri mPreCropPictureUri;
private final Uri mCropPictureUri;
private final Uri mTakePictureUri;
@@ -104,8 +100,6 @@ class AvatarPhotoController {
mImagesDir = new File(mContextInjector.getCacheDir(), IMAGES_DIR);
mImagesDir.mkdir();
- mPreCropPictureUri = mContextInjector
- .createTempImageUri(mImagesDir, PRE_CROP_PICTURE_FILE_NAME, !waiting);
mCropPictureUri =
mContextInjector.createTempImageUri(mImagesDir, CROP_PICTURE_FILE_NAME, !waiting);
mTakePictureUri =
@@ -137,7 +131,7 @@ class AvatarPhotoController {
return true;
case REQUEST_CODE_TAKE_PHOTO:
if (mTakePictureUri.equals(pictureUri)) {
- cropPhoto(pictureUri);
+ cropPhoto();
} else {
copyAndCropPhoto(pictureUri, false);
}
@@ -166,7 +160,7 @@ class AvatarPhotoController {
ThreadUtils.postOnBackgroundThread(() -> {
final ContentResolver cr = mContextInjector.getContentResolver();
try (InputStream in = cr.openInputStream(pictureUri);
- OutputStream out = cr.openOutputStream(mPreCropPictureUri)) {
+ OutputStream out = cr.openOutputStream(mTakePictureUri)) {
Streams.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to copy photo", e);
@@ -174,7 +168,7 @@ class AvatarPhotoController {
}
Runnable cropRunnable = () -> {
if (!mAvatarUi.isFinishing()) {
- cropPhoto(mPreCropPictureUri);
+ cropPhoto();
}
};
if (delayBeforeCrop) {
@@ -189,21 +183,22 @@ class AvatarPhotoController {
}
}
- private void cropPhoto(final Uri pictureUri) {
- // TODO: Use a public intent, when there is one.
- Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setDataAndType(pictureUri, "image/*");
- appendOutputExtra(intent, mCropPictureUri);
- appendCropExtras(intent);
- try {
- StrictMode.disableDeathOnFileUriExposure();
- if (mAvatarUi.startSystemActivityForResult(intent, REQUEST_CODE_CROP_PHOTO)) {
- return;
+ private void cropPhoto() {
+ if (mAvatarUi.canCropPhoto()) {
+ // TODO: Use a public intent, when there is one.
+ Intent intent = new Intent("com.android.camera.action.CROP");
+ intent.setDataAndType(mTakePictureUri, "image/*");
+ appendOutputExtra(intent, mCropPictureUri);
+ appendCropExtras(intent);
+ try {
+ StrictMode.disableDeathOnFileUriExposure();
+ mAvatarUi.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO);
+ } finally {
+ StrictMode.enableDeathOnFileUriExposure();
}
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
+ } else {
+ onPhotoNotCropped(mTakePictureUri);
}
- onPhotoNotCropped(pictureUri);
}
private void appendOutputExtra(Intent intent, Uri pictureUri) {
@@ -325,23 +320,15 @@ class AvatarPhotoController {
}
@Override
- public boolean startSystemActivityForResult(Intent intent, int code) {
- ActivityInfo info = intent.resolveActivityInfo(mActivity.getPackageManager(),
- PackageManager.MATCH_SYSTEM_ONLY);
- if (info == null) {
- Log.w(TAG, "No system package activity could be found for code " + code);
- return false;
- }
- intent.setPackage(info.packageName);
- mActivity.startActivityForResult(intent, code);
- return true;
- }
-
- @Override
public int getPhotoSize() {
return mActivity.getResources()
.getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
}
+
+ @Override
+ public boolean canCropPhoto() {
+ return PhotoCapabilityUtils.canCropPhoto(mActivity);
+ }
}
static class ContextInjectorImpl implements ContextInjector {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
index d988111c29d5..3dc2fabe051a 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
@@ -34,7 +34,6 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
@@ -52,7 +51,6 @@ import org.mockito.MockitoAnnotations;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
@RunWith(AndroidJUnit4.class)
@@ -75,7 +73,6 @@ public class AvatarPhotoControllerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mMockAvatarUi.getPhotoSize()).thenReturn(PHOTO_SIZE);
- when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(true);
mImagesDir = new File(
InstrumentationRegistry.getTargetContext().getCacheDir(), "multi_user");
@@ -113,7 +110,9 @@ public class AvatarPhotoControllerTest {
}
@Test
- public void takePhotoIsFollowedByCrop() throws IOException {
+ public void takePhotoIsFollowedByCropWhenSupported() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -122,12 +121,14 @@ public class AvatarPhotoControllerTest {
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void takePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -137,11 +138,12 @@ public class AvatarPhotoControllerTest {
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_CANCELED, intent);
verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
- verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
}
@Test
public void takePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
Intent intent = new Intent();
@@ -149,12 +151,14 @@ public class AvatarPhotoControllerTest {
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void choosePhotoIsFollowedByCrop() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -163,12 +167,14 @@ public class AvatarPhotoControllerTest {
mController.onActivityResult(
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void choosePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -178,11 +184,12 @@ public class AvatarPhotoControllerTest {
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_CANCELED, intent);
verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
- verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
}
@Test
public void choosePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
Intent intent = new Intent();
@@ -190,11 +197,28 @@ public class AvatarPhotoControllerTest {
mController.onActivityResult(
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
+ public void choosePhotoIsNotFollowedByCropIntentWhenCropNotSupported() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(false);
+
+ File file = new File(mImagesDir, "file.txt");
+ saveBitmapToFile(file);
+
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
+ mController.onActivityResult(
+ REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
+
+ verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
+ verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
+ }
+
+ @Test
public void cropPhotoResultIsReturnedIfResultOkAndContent() {
Intent intent = new Intent();
intent.setData(mCropPhotoUri);
@@ -218,58 +242,11 @@ public class AvatarPhotoControllerTest {
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS).times(0)).returnUriResult(mCropPhotoUri);
}
- @Test
- public void cropDoesNotUseTakePhotoUri() throws IOException {
- new File(mImagesDir, "file.txt").createNewFile();
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- Intent startIntent = verifyStartSystemActivityForResult(
- "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
- assertThat(startIntent.getData()).isNotEqualTo(mTakePhotoUri);
- }
-
- @Test
- public void internalCropUsedIfNoSystemCropperFound() throws IOException {
- when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(false);
-
- File file = new File(mImagesDir, "file.txt");
- saveBitmapToFile(file);
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
-
- InputStream imageStream = mContext.getContentResolver().openInputStream(mCropPhotoUri);
- Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
- assertThat(bitmap.getWidth()).isEqualTo(PHOTO_SIZE);
- assertThat(bitmap.getHeight()).isEqualTo(PHOTO_SIZE);
- }
-
- private Intent verifyStartActivityForResult(String action, int resultCode) {
+ private void verifyStartActivityForResult(String action, int resultCode) {
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
.startActivityForResult(captor.capture(), eq(resultCode));
- Intent intent = captor.getValue();
- assertThat(intent.getAction()).isEqualTo(action);
- return intent;
- }
-
- private Intent verifyStartSystemActivityForResult(String action, int resultCode) {
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
- .startSystemActivityForResult(captor.capture(), eq(resultCode));
- Intent intent = captor.getValue();
- assertThat(intent.getAction()).isEqualTo(action);
- return intent;
+ assertThat(captor.getValue().getAction()).isEqualTo(action);
}
private void saveBitmapToFile(File file) throws IOException {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java
new file mode 100644
index 000000000000..fd181ff07222
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceViewHolder;
+
+
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppHeaderPreferenceTest {
+
+ private Context mContext;
+ private View mRootView;
+ private AppHeaderPreference mPreference;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mRootView = View.inflate(mContext, R.layout.app_header_preference, /* parent */ null);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+ mPreference = new AppHeaderPreference(mContext);
+ }
+
+ @Test
+ public void setNonSelectable_viewShouldNotBeSelectable() {
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.itemView.isClickable()).isFalse();
+ }
+
+ @Test
+ public void defaultInstallType_viewShouldNotBeVisible() {
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mRootView.findViewById(R.id.install_type).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setIsInstantApp_shouldUpdateInstallType() {
+
+ mPreference.onBindViewHolder(mHolder);
+ mPreference.setIsInstantApp(true);
+
+ assertThat(((TextView) mRootView.findViewById(R.id.install_type)).getText().toString())
+ .isEqualTo(mContext.getResources().getString(R.string.install_type_instant));
+ }
+
+ @Test
+ public void setSecondSummary_shouldUpdateSecondSummary() {
+ final String defaultTestText = "Test second summary";
+
+ mPreference.onBindViewHolder(mHolder);
+ mPreference.setSecondSummary(defaultTestText);
+
+ assertThat(((TextView) mRootView.findViewById(R.id.second_summary)).getText().toString())
+ .isEqualTo(defaultTestText);
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
new file mode 100644
index 000000000000..b00661575c14
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UCallExpression
+
+/**
+ * Checks for slow calls to ActivityManager.getCurrentUser() or UserManager.getUserInfo() and
+ * suggests using UserTracker instead. For more info, see: http://go/multi-user-in-systemui-slides.
+ */
+@Suppress("UnstableApiUsage")
+class SlowUserQueryDetector : Detector(), SourceCodeScanner {
+
+ override fun getApplicableMethodNames(): List<String> {
+ return listOf("getCurrentUser", "getUserInfo")
+ }
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ val evaluator = context.evaluator
+ if (
+ evaluator.isStatic(method) &&
+ method.name == "getCurrentUser" &&
+ method.containingClass?.qualifiedName == "android.app.ActivityManager"
+ ) {
+ context.report(
+ ISSUE_SLOW_USER_ID_QUERY,
+ method,
+ context.getNameLocation(node),
+ "ActivityManager.getCurrentUser() is slow. " +
+ "Use UserTracker.getUserId() instead."
+ )
+ }
+ if (
+ !evaluator.isStatic(method) &&
+ method.name == "getUserInfo" &&
+ method.containingClass?.qualifiedName == "android.os.UserManager"
+ ) {
+ context.report(
+ ISSUE_SLOW_USER_INFO_QUERY,
+ method,
+ context.getNameLocation(node),
+ "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead."
+ )
+ }
+ }
+
+ companion object {
+ @JvmField
+ val ISSUE_SLOW_USER_ID_QUERY: Issue =
+ Issue.create(
+ id = "SlowUserIdQuery",
+ briefDescription = "User ID queried using ActivityManager instead of UserTracker.",
+ explanation =
+ "ActivityManager.getCurrentUser() makes a binder call and is slow. " +
+ "Instead, inject a UserTracker and call UserTracker.getUserId(). For " +
+ "more info, see: http://go/multi-user-in-systemui-slides",
+ category = Category.PERFORMANCE,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+
+ @JvmField
+ val ISSUE_SLOW_USER_INFO_QUERY: Issue =
+ Issue.create(
+ id = "SlowUserInfoQuery",
+ briefDescription = "User info queried using UserManager instead of UserTracker.",
+ explanation =
+ "UserManager.getUserInfo() makes a binder call and is slow. " +
+ "Instead, inject a UserTracker and call UserTracker.getUserInfo(). For " +
+ "more info, see: http://go/multi-user-in-systemui-slides",
+ category = Category.PERFORMANCE,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index c7c73d3c86a1..4879883e7c2e 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -30,6 +30,8 @@ class SystemUIIssueRegistry : IssueRegistry() {
get() = listOf(
BindServiceViaContextDetector.ISSUE,
BroadcastSentViaContextDetector.ISSUE,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY,
GetMainLooperViaContextDetector.ISSUE,
RegisterReceiverViaContextDetector.ISSUE,
SoftwareBitmapDetector.ISSUE,
diff --git a/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt
new file mode 100644
index 000000000000..2738f0409fd0
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt
@@ -0,0 +1,194 @@
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class SlowUserQueryDetectorTest : LintDetectorTest() {
+
+ override fun getDetector(): Detector = SlowUserQueryDetector()
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ override fun getIssues(): List<Issue> =
+ listOf(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+
+ @Test
+ fun testGetCurrentUser() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.app.ActivityManager;
+
+ public class TestClass1 {
+ public void slewlyGetCurrentUser() {
+ ActivityManager.getCurrentUser();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectWarningCount(1)
+ .expectContains(
+ "ActivityManager.getCurrentUser() is slow. " +
+ "Use UserTracker.getUserId() instead."
+ )
+ }
+
+ @Test
+ fun testGetUserInfo() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.os.UserManager;
+
+ public class TestClass2 {
+ public void slewlyGetUserInfo(UserManager userManager) {
+ userManager.getUserInfo();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectWarningCount(1)
+ .expectContains(
+ "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead."
+ )
+ }
+
+ @Test
+ fun testUserTrackerGetUserId() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import com.android.systemui.settings.UserTracker;
+
+ public class TestClass3 {
+ public void quicklyGetUserId(UserTracker userTracker) {
+ userTracker.getUserId();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testUserTrackerGetUserInfo() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import com.android.systemui.settings.UserTracker;
+
+ public class TestClass4 {
+ public void quicklyGetUserId(UserTracker userTracker) {
+ userTracker.getUserInfo();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectClean()
+ }
+
+ private val activityManagerStub: TestFile =
+ java(
+ """
+ package android.app;
+
+ public class ActivityManager {
+ public static int getCurrentUser() {};
+ }
+ """
+ )
+
+ private val userManagerStub: TestFile =
+ java(
+ """
+ package android.os;
+ import android.content.pm.UserInfo;
+ import android.annotation.UserIdInt;
+
+ public class UserManager {
+ public UserInfo getUserInfo(@UserIdInt int userId) {};
+ }
+ """
+ )
+
+ private val userIdIntStub: TestFile =
+ java(
+ """
+ package android.annotation;
+
+ public @interface UserIdInt {}
+ """
+ )
+
+ private val userInfoStub: TestFile =
+ java(
+ """
+ package android.content.pm;
+
+ public class UserInfo {}
+ """
+ )
+
+ private val userTrackerStub: TestFile =
+ java(
+ """
+ package com.android.systemui.settings;
+ import android.content.pm.UserInfo;
+
+ public interface UserTracker {
+ public int getUserId();
+ public UserInfo getUserInfo();
+ }
+ """
+ )
+
+ private val stubs =
+ arrayOf(activityManagerStub, userManagerStub, userIdIntStub, userInfoStub, userTrackerStub)
+}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
index 11477f9d833b..6588e22721fb 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
@@ -83,7 +83,11 @@ private fun fakeFooterActionsViewModel(
flowOf(
securityText?.let { text ->
SecurityButtonConfig(
- icon = Icon.Resource(R.drawable.ic_info_outline),
+ icon =
+ Icon.Resource(
+ R.drawable.ic_info_outline,
+ contentDescription = null,
+ ),
text = text,
isClickable = securityClickable,
)
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
new file mode 100644
index 000000000000..02d76f404a32
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user
+
+import android.content.Context
+import androidx.appcompat.content.res.AppCompatResources
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.compose.gallery.R
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import com.android.systemui.util.mockito.mock
+
+object Fakes {
+ private val USER_TINT_COLORS =
+ arrayOf(
+ 0x000000,
+ 0x0000ff,
+ 0x00ff00,
+ 0x00ffff,
+ 0xff0000,
+ 0xff00ff,
+ 0xffff00,
+ 0xffffff,
+ )
+
+ fun fakeUserSwitcherViewModel(
+ context: Context,
+ userCount: Int,
+ ): UserSwitcherViewModel {
+ return UserSwitcherViewModel.Factory(
+ userInteractor =
+ UserInteractor(
+ repository =
+ FakeUserRepository().apply {
+ setUsers(
+ (0 until userCount).map { index ->
+ UserModel(
+ id = index,
+ name = Text.Loaded("user_$index"),
+ image =
+ checkNotNull(
+ AppCompatResources.getDrawable(
+ context,
+ R.drawable.ic_avatar_guest_user
+ )
+ ),
+ isSelected = index == 0,
+ isSelectable = true,
+ )
+ }
+ )
+ setActions(
+ UserActionModel.values().mapNotNull {
+ if (it == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) {
+ null
+ } else {
+ it
+ }
+ }
+ )
+ },
+ controller = mock(),
+ activityStarter = mock(),
+ keyguardInteractor =
+ KeyguardInteractor(
+ repository =
+ FakeKeyguardRepository().apply { setKeyguardShowing(false) },
+ ),
+ ),
+ powerInteractor =
+ PowerInteractor(
+ repository = FakePowerRepository(),
+ )
+ )
+ .create(UserSwitcherViewModel::class.java)
+ }
+}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 72757121d53f..bfbccb85b13f 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -240,8 +240,6 @@
-packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -529,6 +527,8 @@
-packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
-packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
-packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
+-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
+-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
-packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
-packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
-packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt
@@ -677,7 +677,6 @@
-packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -834,6 +833,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
new file mode 100644
index 000000000000..57b3acd6557a
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <com.android.keyguard.BouncerKeyguardMessageArea
+ android:id="@+id/bouncer_message_area"
+ style="@style/Keyguard.TextView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/keyguard_lock_padding"
+ android:ellipsize="marquee"
+ android:focusable="true"
+ android:gravity="center"
+ android:singleLine="true" />
+</merge>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index e77e084c48e8..5486adbd2b8e 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -28,6 +28,7 @@
android:layout_gravity="center_horizontal|bottom"
android:gravity="bottom"
>
+ <include layout="@layout/keyguard_bouncer_message_area"/>
<Space
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index 231ead8dc273..2b7bdc2dc4cb 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -31,6 +31,7 @@
android:layout_gravity="center_horizontal|bottom"
android:clipChildren="false"
android:clipToPadding="false">
+ <include layout="@layout/keyguard_bouncer_message_area"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/pattern_container"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 712f657a20ae..64ece472243d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -17,23 +17,25 @@
*/
-->
-<com.android.keyguard.KeyguardPINView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/keyguard_pin_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_gravity="center_horizontal|bottom"
- android:orientation="vertical"
- >
-
- <androidx.constraintlayout.widget.ConstraintLayout
+<com.android.keyguard.KeyguardPINView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pin_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal|bottom"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width">
+<include layout="@layout/keyguard_bouncer_message_area"/>
+
+<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/pin_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_weight="1"
android:layoutDirection="ltr"
android:orientation="vertical">
@@ -79,6 +81,8 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter"
@@ -186,8 +190,6 @@
</androidx.constraintlayout.widget.ConstraintLayout>
-
-
<include layout="@layout/keyguard_eca"
android:id="@+id/keyguard_selector_fade_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index dae2e564dd32..f2fe520f340f 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -26,20 +26,17 @@
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
android:layout_gravity="center_horizontal|bottom">
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- />
-
+ <include layout="@layout/keyguard_bouncer_message_area" />
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
<ImageView
android:id="@+id/keyguard_sim"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tint="@color/background_protected"
android:src="@drawable/ic_lockscreen_sim"/>
-
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -52,14 +49,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eca_overlap" />
-
<RelativeLayout
android:id="@+id/row0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="4dp"
>
-
<com.android.keyguard.PasswordTextView
android:id="@+id/simPinEntry"
style="@style/Widget.TextView.Password"
@@ -195,7 +190,6 @@
/>
</LinearLayout>
</LinearLayout>
-
<include layout="@layout/keyguard_eca"
android:id="@+id/keyguard_selector_fade_container"
android:layout_width="match_parent"
@@ -205,5 +199,4 @@
android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:layout_marginBottom="2dp"
android:gravity="center_horizontal"/>
-
</com.android.keyguard.KeyguardSimPinView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 74f78201e677..a21ec29267fe 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -27,12 +27,12 @@
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
android:layout_gravity="center_horizontal|bottom">
+ <include layout="@layout/keyguard_bouncer_message_area"/>
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- />
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
<ImageView
android:id="@+id/keyguard_sim"
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index deab61088d56..04dffb6e8c52 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -50,7 +50,7 @@
<item name="android:background">@null</item>
<item name="android:textSize">32sp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:paddingBottom">-16dp</item>
</style>
<style name="Widget.TextView.Password" parent="@android:style/Widget.TextView">
diff --git a/packages/SystemUI/res/drawable/media_seekbar_thumb.xml b/packages/SystemUI/res/drawable/media_seekbar_thumb.xml
new file mode 100644
index 000000000000..5eb2bfdbee39
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_seekbar_thumb.xml
@@ -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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="16dp"
+ android:width="4dp"
+ android:viewportHeight="16"
+ android:viewportWidth="4">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G"
+ android:translateX="2"
+ android:translateY="8">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M2 -6 C2,-6 2,6 2,6 C2,7.1 1.1,8 0,8 C0,8 0,8 0,8 C-1.1,8 -2,7.1 -2,6 C-2,6 -2,-6 -2,-6 C-2,-7.1 -1.1,-8 0,-8 C0,-8 0,-8 0,-8 C1.1,-8 2,-7.1 2,-6c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="1017"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index d633803c920b..be76405744db 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -51,7 +51,7 @@
android:id="@+id/biometric_icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal">
+ android:layout_gravity="center">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/biometric_icon"
@@ -61,6 +61,13 @@
android:contentDescription="@null"
android:scaleType="fitXY" />
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/biometric_icon_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:contentDescription="@null"
+ android:scaleType="fitXY" />
</FrameLayout>
<!-- For sensors such as UDFPS, this view is used during custom measurement/layout to add extra
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 0ca19d98a097..8df8c49ee057 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -83,48 +83,6 @@
android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
android:visibility="gone" />
- <ImageView
- android:id="@+id/wallet_button"
- android:layout_height="@dimen/keyguard_affordance_fixed_height"
- android:layout_width="@dimen/keyguard_affordance_fixed_width"
- android:layout_gravity="bottom|end"
- android:scaleType="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_wallet_lockscreen"
- android:background="@drawable/keyguard_bottom_affordance_bg"
- android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset"
- android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
- android:contentDescription="@string/accessibility_wallet_button"
- android:visibility="gone" />
-
- <ImageView
- android:id="@+id/qr_code_scanner_button"
- android:layout_height="@dimen/keyguard_affordance_fixed_height"
- android:layout_width="@dimen/keyguard_affordance_fixed_width"
- android:layout_gravity="bottom|end"
- android:scaleType="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_qr_code_scanner"
- android:background="@drawable/keyguard_bottom_affordance_bg"
- android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset"
- android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
- android:contentDescription="@string/accessibility_qr_code_scanner_button"
- android:visibility="gone" />
-
- <ImageView
- android:id="@+id/controls_button"
- 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:visibility="gone" />
-
<FrameLayout
android:id="@+id/overlay_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 86f8ce26ccab..0c57b934d27e 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -88,7 +88,7 @@
android:layout_marginTop="@dimen/status_bar_height"
android:layout_gravity="top|center_horizontal"
android:gravity="center_horizontal">
- <com.android.keyguard.KeyguardMessageArea
+ <com.android.keyguard.AuthKeyguardMessageArea
android:id="@+id/keyguard_message_area"
style="@style/Keyguard.TextView"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/raw/biometricprompt_fingerprint_to_error_landscape.json b/packages/SystemUI/res/raw/biometricprompt_fingerprint_to_error_landscape.json
new file mode 100644
index 000000000000..3d33b2afeaf1
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_fingerprint_to_error_landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Fingerprint_To_Error_Landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":830,"st":-70,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_landscape_base.json b/packages/SystemUI/res/raw/biometricprompt_landscape_base.json
new file mode 100644
index 000000000000..3781eeee231f
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_landscape_base.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_landscape_base","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Null_Circle","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle mask 3","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Finger","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-60,"s":[55]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":110,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":140,"s":[10]},{"t":170,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-60,"s":[92.146,-65.896,0],"to":[1.361,6.667,0],"ti":[-1.361,-6.667,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.167,"y":0.167},"t":0,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":110,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[100.313,-25.896,0]}],"ix":2,"l":2},"a":{"a":0,"k":[160.315,58.684,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.013,2.518],[5.251,5.023],[8.982,-2.829],[-0.264,-5.587]],"o":[[12.768,-2.854],[-14.961,2.071],[-6.004,1.89],[8.052,1.403]],"v":[[5.115,7.499],[19.814,-10.087],[-16.489,-3.588],[-24.801,8.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,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":[34.67,28.053],"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":"Group 1","np":4,"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":[[22.231,-7],[-27.395,-1.197],[-26.792,4.092],[14.179,15.736]],"o":[[-17.931,5.646],[56.062,2.45],[-1.765,-22.396],[-51.819,17.744]],"v":[[-62.102,-8.314],[-39.958,30.079],[80.033,25.905],[54.879,-32.529]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,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":[80.283,32.779],"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":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"circle mask 7","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"circle mask","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"circle mask 6","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,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":[6.238,5.063],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"circle mask 2","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":[41.706,17.13],"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":"Group 1","np":4,"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":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":[41.706,20.979],"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":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"circle mask 4","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":1,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"circle mask 5","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":1,"nm":".black","cl":"black","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".grey800","cl":"grey800","parent":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.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[0,18.167,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[0,10.667,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Shape Layer 4","parent":1,"td":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.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":".grey900","cl":"grey900","parent":1,"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.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"device frame mask","parent":24,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,1.167,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".blue400","cl":"blue400","parent":18,"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.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-115.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-199,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-159,"s":[100.25,-105.667,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-0.75,-14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"device frame mask 5","parent":18,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-165,"op":6.00000000000001,"st":-271,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"device frame mask 9","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-145,"s":[50]},{"t":-75,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-165,"s":[0,0]},{"t":-75,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":73,"s":[50]},{"t":113,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"device frame mask 8","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-165,"s":[50]},{"t":-95,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-195,"s":[0,0]},{"t":-105,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[50]},{"t":83,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"device frame mask 7","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-195,"s":[50]},{"t":-125,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-225,"s":[0,0]},{"t":-135,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":53,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"device frame mask 6","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-225,"s":[50]},{"t":-155,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-255,"s":[0,0]},{"t":-165,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[50]},{"t":23,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_portrait_base_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_portrait_base_bottomright.json
new file mode 100644
index 000000000000..495066638cf4
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_portrait_base_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_portrait_base_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"biometricprompt_landscape_base","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Null_Circle","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle mask 3","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Finger","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-60,"s":[55]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":110,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":140,"s":[10]},{"t":170,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-60,"s":[92.146,-65.896,0],"to":[1.361,6.667,0],"ti":[-1.361,-6.667,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.167,"y":0.167},"t":0,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":110,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[100.313,-25.896,0]}],"ix":2,"l":2},"a":{"a":0,"k":[160.315,58.684,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.013,2.518],[5.251,5.023],[8.982,-2.829],[-0.264,-5.587]],"o":[[12.768,-2.854],[-14.961,2.071],[-6.004,1.89],[8.052,1.403]],"v":[[5.115,7.499],[19.814,-10.087],[-16.489,-3.588],[-24.801,8.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,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":[34.67,28.053],"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":"Group 1","np":4,"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":[[22.231,-7],[-27.395,-1.197],[-26.792,4.092],[14.179,15.736]],"o":[[-17.931,5.646],[56.062,2.45],[-1.765,-22.396],[-51.819,17.744]],"v":[[-62.102,-8.314],[-39.958,30.079],[80.033,25.905],[54.879,-32.529]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,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":[80.283,32.779],"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":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"circle mask 7","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"circle mask","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"circle mask 6","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,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":[6.238,5.063],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"circle mask 2","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":[41.706,17.13],"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":"Group 1","np":4,"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":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":[41.706,20.979],"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":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"circle mask 4","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":1,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"circle mask 5","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":1,"nm":".black","cl":"black","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".grey800","cl":"grey800","parent":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.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[0,18.167,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[0,10.667,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Shape Layer 4","parent":1,"td":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.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":".grey900","cl":"grey900","parent":1,"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.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"device frame mask","parent":24,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,1.167,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".blue400","cl":"blue400","parent":18,"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.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-115.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-199,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-159,"s":[100.25,-105.667,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-0.75,-14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"device frame mask 5","parent":18,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-165,"op":6.00000000000001,"st":-271,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"device frame mask 9","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-145,"s":[50]},{"t":-75,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-165,"s":[0,0]},{"t":-75,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":73,"s":[50]},{"t":113,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"device frame mask 8","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-165,"s":[50]},{"t":-95,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-195,"s":[0,0]},{"t":-105,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[50]},{"t":83,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"device frame mask 7","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-195,"s":[50]},{"t":-125,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-225,"s":[0,0]},{"t":-135,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":53,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"device frame mask 6","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-225,"s":[50]},{"t":-155,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-255,"s":[0,0]},{"t":-165,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[50]},{"t":23,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0}]}],"layers":[{"ddd":0,"ind":6,"ty":0,"nm":"biometricprompt_landscape_base","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[170,170,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":340,"h":340,"ip":0,"op":900,"st":0,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json b/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json
new file mode 100644
index 000000000000..b1616094ade4
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_portrait_base_topleft","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":6,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null_Circle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey905","cl":"grey905","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"circle mask 3","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Finger_Flipped","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-24.98,-35.709,0],"ix":2,"l":2},"a":{"a":0,"k":[31.791,75.23,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.03,5.25],[-2.83,8.98],[-5.59,-0.26],[2.52,-11.02]],"o":[[-2.85,12.77],[2.07,-14.96],[1.9,-6],[1.4,8.05],[0,0]],"v":[[7.5,4.99],[-10.09,19.69],[-3.59,-16.61],[8.69,-24.92],[7.5,5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,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":[27.8,24.94],"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":"Group 1","np":4,"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":[[-7.01,22.23],[-1.2,-27.39],[4.09,-26.79],[15.73,14.18]],"o":[[5.64,-17.93],[2.45,56.06],[-22.4,-1.77],[17.73,-51.82]],"v":[[-7.57,-66.9],[30.82,-44.76],[26.65,75.23],[-31.78,50.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,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":[31.79,75.23],"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":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"circle mask 7","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"circle mask","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey904","cl":"grey904","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"circle mask 6","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey903","cl":"grey903","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,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":[6.238,5.063],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"circle mask 2","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".blue400","cl":"blue400","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":[41.706,17.13],"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":"Group 1","np":4,"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":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":[41.706,20.979],"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":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"circle mask 4","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":1,"nm":".grey902","cl":"grey902","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"circle mask 5","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,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,-17.333],"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":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":1,"nm":".black","cl":"black","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".grey800","cl":"grey800","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":".grey901","cl":"grey901","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-87.156,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[100.25,-94.656,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Shape Layer 4","parent":6,"td":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.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,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":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":".grey900","cl":"grey900","parent":6,"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.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"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":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_landscape.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_landscape.json
new file mode 100644
index 000000000000..034ac87c0f94
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_fingerprint_landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_bottomright.json
new file mode 100644
index 000000000000..e5cc565e31d8
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_fingerprint_portrait_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 12","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_topleft.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_topleft.json
new file mode 100644
index 000000000000..b08226551a02
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_fingerprint_portrait_topleft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 12","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_landscape.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_landscape.json
new file mode 100644
index 000000000000..befe3bb61cca
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_success_landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":112,"s":[0]},{"t":122,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":112,"op":1012,"st":112,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_bottomright.json
new file mode 100644
index 000000000000..d75b335747ce
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_success_portrait_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":112,"s":[0]},{"t":122,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":112,"op":1012,"st":112,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 13","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_topleft.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_topleft.json
new file mode 100644
index 000000000000..e6b2db1fd537
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_success_portrait_topleft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":112,"s":[0]},{"t":122,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":112,"op":1012,"st":112,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 13","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_bottomright.json
new file mode 100644
index 000000000000..0da143c9243a
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_fingerprint_to_error_portrait_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 14","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":830,"st":-70,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_topleft.json b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_topleft.json
new file mode 100644
index 000000000000..15457c7e19c8
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_fingerprint_to_error_portrait_topleft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 14","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":830,"st":-70,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_landscape.json b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_landscape.json
new file mode 100644
index 000000000000..f39894b0a300
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_fingerprint_to_success_landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":661,"st":-239,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_bottomright.json
new file mode 100644
index 000000000000..6d4f4e2e1d23
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_fingerprint_to_success_portrait_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 15","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":661,"st":-239,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_topleft.json b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_topleft.json
new file mode 100644
index 000000000000..b7bb0d5cbb26
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_fingerprint_to_success_portrait_topleft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"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":14,"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":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,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":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"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":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":3,"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":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"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":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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":"Group 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,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":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"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.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 15","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":661,"st":-239,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index c0071cb45e21..80628f903e76 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -35,9 +35,6 @@
<!-- How many lines to show in the security footer -->
<integer name="qs_security_footer_maxLines">1</integer>
- <!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
- <bool name="allow_force_nav_bar_handle_opaque">false</bool>
-
<bool name="config_use_large_screen_shade_header">true</bool>
<!-- Whether to show the side fps hint while on bouncer -->
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
index a0bf072ed27c..3d8da8a6c3e8 100644
--- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -23,7 +23,7 @@
<dimen name="status_view_margin_horizontal">124dp</dimen>
<dimen name="keyguard_clock_top_margin">80dp</dimen>
<dimen name="keyguard_status_view_bottom_margin">80dp</dimen>
- <dimen name="bouncer_user_switcher_y_trans">90dp</dimen>
+ <dimen name="bouncer_user_switcher_y_trans">200dp</dimen>
<dimen name="large_screen_shade_header_left_padding">24dp</dimen>
<dimen name="qqs_layout_padding_bottom">40dp</dimen>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ec22c609b0b9..a8027238a0bf 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -587,9 +587,6 @@
when the double-press power gesture is used. Ignored if empty. -->
<string translatable="false" name="config_cameraGesturePackage"></string>
- <!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
- <bool name="allow_force_nav_bar_handle_opaque">true</bool>
-
<!-- Whether a transition of ACTIVITY_TYPE_DREAM to the home app should play a home sound
effect -->
<bool name="config_playHomeSoundAfterDream">false</bool>
@@ -617,8 +614,9 @@
<!-- Which face help messages to surface when fingerprint is also enrolled.
Message ids correspond with the acquired ids in BiometricFaceConstants -->
<integer-array name="config_face_help_msgs_when_fingerprint_enrolled">
- <item>25</item>
- <item>26</item>
+ <item>3</item> <!-- TOO_DARK -->
+ <item>25</item> <!-- DARK_GLASSES -->
+ <item>26</item> <!-- MOUTH_COVERING_DETECTED -->
</integer-array>
<!-- Whether the communal service should be enabled -->
@@ -629,11 +627,15 @@
<!-- This value is used when calculating whether the device is in ambient light mode. It is
light mode when the light sensor sample value exceeds above this value. -->
- <integer name="config_ambientLightModeThreshold">10</integer>
+ <item name="config_ambientLightModeThreshold" translatable="false" format="float" type="dimen">
+ 0.8
+ </item>
<!-- This value is used when calculating whether the device is in ambient dark mode. It is
dark mode when the light sensor sample value drops below this value. -->
- <integer name="config_ambientDarkModeThreshold">5</integer>
+ <item name="config_ambientDarkModeThreshold" translatable="false" format="float" type="dimen">
+ 0.4
+ </item>
<!-- This value is used when calculating whether the device is in ambient light mode. Each
sample contains light sensor events from this span of time duration. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2865606860cd..19a6aba1fa72 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1477,14 +1477,14 @@
if their end is aligned with the parent end. Represented as the percentage over from the
start of the parent container. -->
<item name="dream_overlay_complication_guide_end_percent" format="float" type="dimen">
- 0.75
+ 0.5
</item>
<!-- The position of the start guide, which dream overlay complications can align their end to
if their start is aligned with the parent start. Represented as the percentage over from
the start of the parent container. -->
<item name="dream_overlay_complication_guide_start_percent" format="float" type="dimen">
- 0.25
+ 0.5
</item>
<!-- The position of the bottom guide, which dream overlay complications can align their top to
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c5b7dedf1907..9324e8f3babb 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1862,10 +1862,10 @@
<!-- URL for care instructions for overheating devices -->
<string name="high_temp_dialog_help_url" translatable="false"></string>
- <!-- Title for alarm dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=30] -->
- <string name="high_temp_alarm_title">Unplug charger</string>
- <!-- Text body for dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=300] -->
- <string name="high_temp_alarm_notify_message">There\u2019s an issue charging this device. Unplug the power adapter, and take care as the cable may be warm.</string>
+ <!-- Title for alarm dialog alerting user the usb charger or accessorry has reached a certain temperature that should unplug the device immediately. [CHAR LIMIT=30] -->
+ <string name="high_temp_alarm_title">Unplug your device</string>
+ <!-- Text body for dialog alerting user the usb charger or accessorry has reached a certain temperature that should unplug the device immediately. [CHAR LIMIT=300] -->
+ <string name="high_temp_alarm_notify_message">Your device is getting warm near the charging port. If it\u2019s connected to a charger or USB accessory, unplug it, and take care as the cable may also be warm.</string>
<!-- Text for See care steps button [CHAR LIMIT=300] -->
<string name="high_temp_alarm_help_care_steps">See care steps</string>
<!-- Text link directs to usb overheat help page. -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d087b1dd4a7c..a734fa744b48 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -580,6 +580,7 @@
</style>
<style name="MediaPlayer.ProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal">
+ <item name="android:thumb">@drawable/media_seekbar_thumb</item>
<item name="android:thumbTint">?android:attr/textColorPrimary</item>
<item name="android:progressDrawable">@drawable/media_squiggly_progress</item>
<item name="android:progressTint">?android:attr/textColorPrimary</item>
@@ -978,6 +979,8 @@
<!-- Padding for indeterminate progress bar -->
<item name="progressBarStartPadding">12dp</item>
<item name="progressBarEndPadding">16dp</item>
+
+ <item name="iconSize">25dp</item>
</style>
<style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.DeviceDefault.Large">
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index f59a3200976a..9040ea176e4f 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -52,7 +52,7 @@ android_library {
"SystemUIUnfoldLib",
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
- "gson-prebuilt-jar",
+ "gson",
"dagger2",
"jsr330",
],
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index a030bf629507..e77c65079456 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -42,13 +42,6 @@ interface ISystemUiProxy {
void onOverviewShown(boolean fromHome) = 6;
/**
- * Control the {@param alpha} of the option nav bar button (back-button in 2 button mode
- * and home handle & background in gestural mode). The {@param animate} is currently only
- * supported for 2 button mode.
- */
- void setNavBarButtonAlpha(float alpha, boolean animate) = 19;
-
- /**
* Proxies motion events from the homescreen UI to the status bar. Only called when
* swipe down is detected on WORKSPACE. The sender guarantees the following order of events on
* the tracking pointer.
diff --git a/packages/SystemUI/src/com/android/keyguard/AuthKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/AuthKeyguardMessageArea.kt
new file mode 100644
index 000000000000..82ce1ca3bbb4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AuthKeyguardMessageArea.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.keyguard
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.util.AttributeSet
+
+/**
+ * Displays security messages for auth outside of the security method (pin, password, pattern), like
+ * biometric auth.
+ */
+class AuthKeyguardMessageArea(context: Context?, attrs: AttributeSet?) :
+ KeyguardMessageArea(context, attrs) {
+ override fun updateTextColor() {
+ setTextColor(ColorStateList.valueOf(Color.WHITE))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
new file mode 100644
index 000000000000..0075ddd73cd3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.keyguard
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.TypedArray
+import android.graphics.Color
+import android.util.AttributeSet
+import com.android.settingslib.Utils
+
+/** Displays security messages for the keyguard bouncer. */
+class BouncerKeyguardMessageArea(context: Context?, attrs: AttributeSet?) :
+ KeyguardMessageArea(context, attrs) {
+ private val DEFAULT_COLOR = -1
+ private var mDefaultColorState: ColorStateList? = null
+ private var mNextMessageColorState: ColorStateList? = ColorStateList.valueOf(DEFAULT_COLOR)
+
+ override fun updateTextColor() {
+ var colorState = mDefaultColorState
+ mNextMessageColorState?.defaultColor?.let { color ->
+ if (color != DEFAULT_COLOR) {
+ colorState = mNextMessageColorState
+ mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR)
+ }
+ }
+ setTextColor(colorState)
+ }
+
+ override fun setNextMessageColor(colorState: ColorStateList?) {
+ mNextMessageColorState = colorState
+ }
+
+ override fun onThemeChanged() {
+ val array: TypedArray =
+ mContext.obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary))
+ val newTextColors: ColorStateList = ColorStateList.valueOf(array.getColor(0, Color.RED))
+ array.recycle()
+ mDefaultColorState = newTextColors
+ super.onThemeChanged()
+ }
+
+ override fun reloadColor() {
+ mDefaultColorState = Utils.getColorAttr(context, android.R.attr.textColorPrimary)
+ super.reloadColor()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index b8fcb103402d..92ba619e7eb9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -50,7 +50,6 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
private final FalsingCollector mFalsingCollector;
private final EmergencyButtonController mEmergencyButtonController;
private CountDownTimer mCountdownTimer;
- protected KeyguardMessageAreaController mMessageAreaController;
private boolean mDismissing;
protected AsyncTask<?, ?, ?> mPendingLockCheck;
protected boolean mResumed;
@@ -80,14 +79,13 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController) {
- super(view, securityMode, keyguardSecurityCallback, emergencyButtonController);
+ super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
+ messageAreaControllerFactory);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
- KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
- mMessageAreaController = messageAreaControllerFactory.create(kma);
}
abstract void resetState();
@@ -95,7 +93,6 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
@Override
public void onInit() {
super.onInit();
- mMessageAreaController.init();
}
@Override
@@ -134,6 +131,10 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
@Override
public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (mMessageAreaController == null) {
+ return;
+ }
+
if (colorState != null) {
mMessageAreaController.setNextMessageColor(colorState);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 87300c3f0504..f26b9057dc7c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -17,9 +17,11 @@
package com.android.keyguard;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.telephony.TelephonyManager;
+import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.util.LatencyTracker;
@@ -44,7 +46,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
private final EmergencyButton mEmergencyButton;
private final EmergencyButtonController mEmergencyButtonController;
private boolean mPaused;
-
+ protected KeyguardMessageAreaController<BouncerKeyguardMessageArea> mMessageAreaController;
// The following is used to ignore callbacks from SecurityViews that are no longer current
// (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
@@ -72,12 +74,24 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
protected KeyguardInputViewController(T view, SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
- EmergencyButtonController emergencyButtonController) {
+ EmergencyButtonController emergencyButtonController,
+ @Nullable KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
super(view);
mSecurityMode = securityMode;
mKeyguardSecurityCallback = keyguardSecurityCallback;
mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button);
mEmergencyButtonController = emergencyButtonController;
+ if (messageAreaControllerFactory != null) {
+ try {
+ BouncerKeyguardMessageArea kma = view.requireViewById(R.id.bouncer_message_area);
+ mMessageAreaController = messageAreaControllerFactory.create(kma);
+ mMessageAreaController.init();
+ mMessageAreaController.setIsVisible(true);
+ } catch (IllegalArgumentException exception) {
+ Log.e("KeyguardInputViewController",
+ "Ensure that a BouncerKeyguardMessageArea is included in the layout");
+ }
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 5ab2fd0aff09..c79fc2c27f0e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -17,9 +17,7 @@
package com.android.keyguard;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
@@ -33,7 +31,6 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.internal.policy.SystemBarUtils;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import java.lang.ref.WeakReference;
@@ -41,7 +38,7 @@ import java.lang.ref.WeakReference;
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
*/
-public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
+public abstract class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
/** Handler token posted with accessibility announcement runnables. */
private static final Object ANNOUNCE_TOKEN = new Object();
@@ -50,15 +47,11 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
* lift-to-type from interrupting itself.
*/
private static final long ANNOUNCEMENT_DELAY = 250;
- private static final int DEFAULT_COLOR = -1;
private final Handler mHandler;
- private ColorStateList mDefaultColorState;
private CharSequence mMessage;
- private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
- private boolean mBouncerShowing;
- private boolean mAltBouncerShowing;
+ private boolean mIsVisible;
/**
* Container that wraps the KeyguardMessageArea - may be null if current view hierarchy doesn't
* contain {@link R.id.keyguard_message_area_container}.
@@ -96,23 +89,11 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
mContainer.setLayoutParams(lp);
}
- @Override
- public void setNextMessageColor(ColorStateList colorState) {
- mNextMessageColorState = colorState;
- }
-
- void onThemeChanged() {
- TypedArray array = mContext.obtainStyledAttributes(new int[] {
- android.R.attr.textColorPrimary
- });
- ColorStateList newTextColors = ColorStateList.valueOf(array.getColor(0, Color.RED));
- array.recycle();
- mDefaultColorState = newTextColors;
+ protected void onThemeChanged() {
update();
}
- void reloadColor() {
- mDefaultColorState = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary);
+ protected void reloadColor() {
update();
}
@@ -151,17 +132,6 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
setMessage(message);
}
- public static KeyguardMessageArea findSecurityMessageDisplay(View v) {
- KeyguardMessageArea messageArea = v.findViewById(R.id.keyguard_message_area);
- if (messageArea == null) {
- messageArea = v.getRootView().findViewById(R.id.keyguard_message_area);
- }
- if (messageArea == null) {
- throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
- }
- return messageArea;
- }
-
private void securityMessageChanged(CharSequence message) {
mMessage = message;
update();
@@ -177,40 +147,23 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
void update() {
CharSequence status = mMessage;
- setVisibility(TextUtils.isEmpty(status) || (!mBouncerShowing && !mAltBouncerShowing)
- ? INVISIBLE : VISIBLE);
+ setVisibility(TextUtils.isEmpty(status) || (!mIsVisible) ? INVISIBLE : VISIBLE);
setText(status);
- ColorStateList colorState = mDefaultColorState;
- if (mNextMessageColorState.getDefaultColor() != DEFAULT_COLOR) {
- colorState = mNextMessageColorState;
- mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
- }
- if (mAltBouncerShowing) {
- // alt bouncer has a black scrim, so always show the text in white
- colorState = ColorStateList.valueOf(Color.WHITE);
- }
- setTextColor(colorState);
+ updateTextColor();
}
/**
* Set whether the bouncer is fully showing
*/
- public void setBouncerShowing(boolean bouncerShowing) {
- if (mBouncerShowing != bouncerShowing) {
- mBouncerShowing = bouncerShowing;
+ public void setIsVisible(boolean isVisible) {
+ if (mIsVisible != isVisible) {
+ mIsVisible = isVisible;
update();
}
}
- /**
- * Set whether the alt bouncer is showing
- */
- void setAltBouncerShowing(boolean showing) {
- if (mAltBouncerShowing != showing) {
- mAltBouncerShowing = showing;
- update();
- }
- }
+ /** Set the text color */
+ protected abstract void updateTextColor();
/**
* Runnable used to delay accessibility announcements.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 3ec8ce918a4d..c2802f7b6843 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -26,11 +26,14 @@ import com.android.systemui.util.ViewController;
import javax.inject.Inject;
-/** Controller for a {@link KeyguardMessageAreaController}. */
-public class KeyguardMessageAreaController extends ViewController<KeyguardMessageArea> {
+/**
+ * Controller for a {@link KeyguardMessageAreaController}.
+ * @param <T> A subclass of KeyguardMessageArea.
+ */
+public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
+ extends ViewController<T> {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
- private boolean mAltBouncerShowing;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
public void onFinishedGoingToSleep(int why) {
@@ -59,7 +62,7 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag
}
};
- private KeyguardMessageAreaController(KeyguardMessageArea view,
+ protected KeyguardMessageAreaController(T view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController) {
super(view);
@@ -83,17 +86,10 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag
}
/**
- * Set whether alt bouncer is showing
- */
- public void setAltBouncerShowing(boolean showing) {
- mView.setAltBouncerShowing(showing);
- }
-
- /**
- * Set bouncer is fully showing
+ * Indicate that view is visible and can display messages.
*/
- public void setBouncerShowing(boolean showing) {
- mView.setBouncerShowing(showing);
+ public void setIsVisible(boolean isVisible) {
+ mView.setIsVisible(isVisible);
}
public void setMessage(CharSequence s) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 6844b655629a..20fa8f817dc0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -16,23 +16,25 @@
package com.android.keyguard;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_APPEAR;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_DISAPPEAR;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
+import android.util.MathUtils;
import android.view.View;
import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
-import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
/**
@@ -40,7 +42,7 @@ import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostu
*/
public class KeyguardPINView extends KeyguardPinBasedInputView {
- private final AppearAnimationUtils mAppearAnimationUtils;
+ ValueAnimator mAppearAnimator = ValueAnimator.ofFloat(0f, 1f);
private final DisappearAnimationUtils mDisappearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
private ConstraintLayout mContainer;
@@ -54,7 +56,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
public KeyguardPINView(Context context, AttributeSet attrs) {
super(context, attrs);
- mAppearAnimationUtils = new AppearAnimationUtils(context);
mDisappearAnimationUtils = new DisappearAnimationUtils(context,
125, 0.6f /* translationScale */,
0.45f /* delayScale */, AnimationUtils.loadInterpolator(
@@ -169,25 +170,20 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
@Override
public void startAppearAnimation() {
- enableClipping(false);
- setAlpha(1f);
- setTranslationY(mAppearAnimationUtils.getStartTranslation());
- AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
- 0, mAppearAnimationUtils.getInterpolator(),
- getAnimationListener(CUJ_LOCKSCREEN_PIN_APPEAR));
- mAppearAnimationUtils.startAnimation2d(mViews,
- new Runnable() {
- @Override
- public void run() {
- enableClipping(true);
- }
- });
+ if (mAppearAnimator.isRunning()) {
+ mAppearAnimator.cancel();
+ }
+ mAppearAnimator.setDuration(650);
+ mAppearAnimator.addUpdateListener(animation -> animate(animation.getAnimatedFraction()));
+ mAppearAnimator.start();
}
public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
final Runnable finishRunnable) {
+ if (mAppearAnimator.isRunning()) {
+ mAppearAnimator.cancel();
+ }
- enableClipping(false);
setTranslationY(0);
DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
? mDisappearAnimationUtilsLocked
@@ -195,7 +191,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
disappearAnimationUtils.createAnimation(
this, 0, 200, mDisappearYTranslation, false,
mDisappearAnimationUtils.getInterpolator(), () -> {
- enableClipping(true);
if (finishRunnable != null) {
finishRunnable.run();
}
@@ -204,14 +199,32 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
return true;
}
- private void enableClipping(boolean enable) {
- mContainer.setClipToPadding(enable);
- mContainer.setClipChildren(enable);
- setClipChildren(enable);
- }
-
@Override
public boolean hasOverlappingRendering() {
return false;
}
+
+ /** Animate subviews according to expansion or time. */
+ private void animate(float progress) {
+ for (int i = 0; i < mViews.length; i++) {
+ View[] row = mViews[i];
+ for (View view : row) {
+ if (view == null) {
+ continue;
+ }
+
+ float scaledProgress = MathUtils.constrain(
+ (progress - 0.075f * i) / (1f - 0.075f * mViews.length),
+ 0f,
+ 1f
+ );
+ view.setAlpha(scaledProgress);
+ Interpolator interpolator = Interpolators.STANDARD_ACCELERATE;
+ view.setTranslationY(40 - (40 * interpolator.getInterpolation(scaledProgress)));
+ if (view instanceof NumPadAnimationListener) {
+ ((NumPadAnimationListener) view).setProgress(scaledProgress);
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 1862fc7f6603..afc25909ca91 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -71,7 +71,7 @@ public class KeyguardPatternView extends KeyguardInputView
*/
private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
- KeyguardMessageArea mSecurityMessageDisplay;
+ BouncerKeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
private ConstraintLayout mContainer;
@@ -120,7 +120,7 @@ public class KeyguardPatternView extends KeyguardInputView
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this);
+ mSecurityMessageDisplay = findViewById(R.id.bouncer_message_area);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 9aa6f0320f50..987164557a7a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -59,12 +59,9 @@ public class KeyguardPatternViewController
private final LatencyTracker mLatencyTracker;
private final FalsingCollector mFalsingCollector;
private final EmergencyButtonController mEmergencyButtonController;
- private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
private final DevicePostureController mPostureController;
private final DevicePostureController.Callback mPostureCallback =
posture -> mView.onDevicePostureChanged(posture);
-
- private KeyguardMessageAreaController mMessageAreaController;
private LockPatternView mLockPatternView;
private CountDownTimer mCountdownTimer;
private AsyncTask<?, ?, ?> mPendingLockCheck;
@@ -201,15 +198,13 @@ public class KeyguardPatternViewController
EmergencyButtonController emergencyButtonController,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
DevicePostureController postureController) {
- super(view, securityMode, keyguardSecurityCallback, emergencyButtonController);
+ super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
+ messageAreaControllerFactory);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
- mMessageAreaControllerFactory = messageAreaControllerFactory;
- KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
- mMessageAreaController = mMessageAreaControllerFactory.create(kma);
mLockPatternView = mView.findViewById(R.id.lockPatternView);
mPostureController = postureController;
}
@@ -217,7 +212,6 @@ public class KeyguardPatternViewController
@Override
public void onInit() {
super.onInit();
- mMessageAreaController.init();
}
@Override
@@ -346,6 +340,9 @@ public class KeyguardPatternViewController
@Override
public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (mMessageAreaController == null) {
+ return;
+ }
if (colorState != null) {
mMessageAreaController.setNextMessageColor(colorState);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index d0baf3dad79d..f73c98e4971b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -665,7 +665,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
// When using EXACTLY spec, measure will use the layout width if > 0. Set before
// measuring the child
lp.width = MeasureSpec.getSize(updatedWidthMeasureSpec);
- measureChildWithMargins(view, updatedWidthMeasureSpec, 0, heightMeasureSpec, 0);
+ measureChildWithMargins(view, updatedWidthMeasureSpec, 0,
+ heightMeasureSpec, 0);
maxWidth = Math.max(maxWidth,
view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
@@ -1306,7 +1307,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
int yTrans = mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_y_trans);
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mUserSwitcherViewGroup.setTranslationY(yTrans);
- mViewFlipper.setTranslationY(-yTrans);
} else {
// Attempt to reposition a bit higher to make up for this frame being a bit lower
// on the device
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 3aa5ada0794d..bddf4b09ebb3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -146,7 +146,8 @@ public class KeyguardSecurityViewFlipperController
protected NullKeyguardInputViewController(SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
EmergencyButtonController emergencyButtonController) {
- super(null, securityMode, keyguardSecurityCallback, emergencyButtonController);
+ super(null, securityMode, keyguardSecurityCallback, emergencyButtonController,
+ null);
}
@Override
@@ -156,7 +157,6 @@ public class KeyguardSecurityViewFlipperController
@Override
public void onStartingToHide() {
-
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index acbea1beeae3..7d6f377d5287 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -50,12 +50,10 @@ constructor(
viewsIdToTranslate =
setOf(
ViewIdToTranslate(R.id.keyguard_status_area, LEFT, filterNever),
- ViewIdToTranslate(R.id.controls_button, LEFT, filterNever),
ViewIdToTranslate(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly),
ViewIdToTranslate(R.id.lockscreen_clock_view, LEFT, filterNever),
ViewIdToTranslate(
R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
- ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever),
ViewIdToTranslate(R.id.start_button, LEFT, filterNever),
ViewIdToTranslate(R.id.end_button, RIGHT, filterNever)),
progressProvider = unfoldProgressProvider)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 094c4a772d05..1068c261b68e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -451,6 +451,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
FACE_AUTH_TRIGGERED_TRUST_DISABLED);
}
+ mLogger.logTrustChanged(wasTrusted, enabled, userId);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -466,12 +467,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean userHasTrust = getUserHasTrust(userId);
if (userHasTrust && trustGrantedMessages != null) {
for (String msg : trustGrantedMessages) {
- if (!TextUtils.isEmpty(msg)) {
- message = msg;
+ message = msg;
+ if (!TextUtils.isEmpty(message)) {
break;
}
}
}
+
+ if (message != null) {
+ mLogger.logShowTrustGrantedMessage(message.toString());
+ }
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -728,6 +733,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintCancelSignal = null;
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_FP_AUTHENTICATED);
+ mLogger.d("onFingerprintAuthenticated");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -971,6 +977,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFaceCancelSignal = null;
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED);
+ mLogger.d("onFaceAuthenticated");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -3431,6 +3438,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mUserFaceAuthenticated.clear();
mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT, unlockedUser);
mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, unlockedUser);
+ mLogger.d("clearBiometricRecognized");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -3680,6 +3688,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardUpdateMonitor state:");
+ pw.println(" getUserHasTrust()=" + getUserHasTrust(getCurrentUser()));
+ pw.println(" getUserUnlockedWithBiometric()="
+ + getUserUnlockedWithBiometric(getCurrentUser()));
pw.println(" SIM States:");
for (SimData data : mSimDatas.values()) {
pw.println(" " + data.toString());
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimationListener.kt b/packages/SystemUI/src/com/android/keyguard/NumPadAnimationListener.kt
new file mode 100644
index 000000000000..f449edf8e894
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimationListener.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+/** Interface for classes to track animation progress. */
+interface NumPadAnimationListener {
+ /** Track the progress of the animation. */
+ fun setProgress(progress: Float)
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index c91c8992780c..e0cafaed5a35 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -48,6 +48,10 @@ class NumPadAnimator {
private int mTextColorPrimary;
private int mTextColorPressed;
private int mStyle;
+ private float mStartRadius;
+ private float mEndRadius;
+ private int mHeight;
+
private static final int EXPAND_ANIMATION_MS = 100;
private static final int EXPAND_COLOR_ANIMATION_MS = 50;
private static final int CONTRACT_ANIMATION_DELAY_MS = 33;
@@ -80,12 +84,20 @@ class NumPadAnimator {
mContractAnimatorSet.start();
}
+ public void setProgress(float progress) {
+ mBackground.setCornerRadius(mEndRadius + (mStartRadius - mEndRadius) * progress);
+ int height = (int) (mHeight * 0.8f + mHeight * 0.2 * progress);
+ int difference = mHeight - height;
+ mBackground.setBounds(0, difference / 2, mHeight, mHeight - difference / 2);
+ }
+
void onLayout(int height) {
- float startRadius = height / 2f;
- float endRadius = height / 4f;
- mBackground.setCornerRadius(startRadius);
- mExpandAnimator.setFloatValues(startRadius, endRadius);
- mContractAnimator.setFloatValues(endRadius, startRadius);
+ mHeight = height;
+ mStartRadius = height / 2f;
+ mEndRadius = height / 4f;
+ mBackground.setCornerRadius(mStartRadius);
+ mExpandAnimator.setFloatValues(mStartRadius, mEndRadius);
+ mContractAnimator.setFloatValues(mEndRadius, mStartRadius);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 8099f75ed7d4..37060987cb21 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -30,7 +30,7 @@ import androidx.annotation.Nullable;
/**
* Similar to the {@link NumPadKey}, but displays an image.
*/
-public class NumPadButton extends AlphaOptimizedImageButton {
+public class NumPadButton extends AlphaOptimizedImageButton implements NumPadAnimationListener {
@Nullable
private NumPadAnimator mAnimator;
@@ -104,4 +104,11 @@ public class NumPadButton extends AlphaOptimizedImageButton {
a.recycle();
((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor));
}
+
+ @Override
+ public void setProgress(float progress) {
+ if (mAnimator != null) {
+ mAnimator.setProgress(progress);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 4aed2513d4f2..0a4880e1ce66 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -37,7 +37,10 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-public class NumPadKey extends ViewGroup {
+/**
+ * Viewgroup for the bouncer numpad button, specifically for digits.
+ */
+public class NumPadKey extends ViewGroup implements NumPadAnimationListener {
// list of "ABC", etc per digit, starting with '0'
static String sKlondike[];
@@ -221,4 +224,11 @@ public class NumPadKey extends ViewGroup {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
+
+ @Override
+ public void setProgress(float progress) {
+ if (mAnimator != null) {
+ mAnimator.setProgress(progress);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java b/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
index 7c86a1dfb797..777bd19864bf 100644
--- a/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
+++ b/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
@@ -20,7 +20,8 @@ import android.content.res.ColorStateList;
public interface SecurityMessageDisplay {
- void setNextMessageColor(ColorStateList colorState);
+ /** Set text color for the next security message. */
+ default void setNextMessageColor(ColorStateList colorState) {}
void setMessage(CharSequence msg);
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 2bc98f1a535c..7a00cd930f2a 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -340,4 +340,40 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
bool1 = dismissKeyguard
}, { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" })
}
+
+ fun logShowTrustGrantedMessage(
+ message: String
+ ) {
+ logBuffer.log(TAG, DEBUG, {
+ str1 = message
+ }, { "showTrustGrantedMessage message$str1" })
+ }
+
+ fun logTrustChanged(
+ wasTrusted: Boolean,
+ isNowTrusted: Boolean,
+ userId: Int
+ ) {
+ logBuffer.log(TAG, DEBUG, {
+ bool1 = wasTrusted
+ bool2 = isNowTrusted
+ int1 = userId
+ }, { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" })
+ }
+
+ fun logKeyguardStateUpdate(
+ secure: Boolean,
+ canDismissLockScreen: Boolean,
+ trusted: Boolean,
+ trustManaged: Boolean
+
+ ) {
+ logBuffer.log("KeyguardState", DEBUG, {
+ bool1 = secure
+ bool2 = canDismissLockScreen
+ bool3 = trusted
+ bool4 = trustManaged
+ }, { "#update secure=$bool1 canDismissKeyguard=$bool2" +
+ " trusted=$bool3 trustManaged=$bool4" })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
index 40d1eff2a887..e4c197ff940e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
@@ -29,8 +29,9 @@ import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMAT
/** Face/Fingerprint combined icon animator for BiometricPrompt. */
class AuthBiometricFingerprintAndFaceIconController(
context: Context,
- iconView: LottieAnimationView
-) : AuthBiometricFingerprintIconController(context, iconView) {
+ iconView: LottieAnimationView,
+ iconViewOverlay: LottieAnimationView
+) : AuthBiometricFingerprintIconController(context, iconView, iconViewOverlay) {
override val actsAsConfirmButton: Boolean = true
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
index 7371442bdd07..7f5a67f8c327 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
@@ -39,5 +39,5 @@ class AuthBiometricFingerprintAndFaceView(
override fun onPointerDown(failedModalities: Set<Int>) = failedModalities.contains(TYPE_FACE)
override fun createIconController(): AuthIconController =
- AuthBiometricFingerprintAndFaceIconController(mContext, mIconView)
+ AuthBiometricFingerprintAndFaceIconController(mContext, mIconView, mIconViewOverlay)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 9b5f54a0a91d..b40b3560f9db 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -18,7 +18,12 @@ package com.android.systemui.biometrics
import android.annotation.RawRes
import android.content.Context
+import android.hardware.fingerprint.FingerprintManager
+import android.view.DisplayInfo
+import android.view.Surface
+import android.view.View
import com.airbnb.lottie.LottieAnimationView
+import com.android.settingslib.widget.LottieColorUtils
import com.android.systemui.R
import com.android.systemui.biometrics.AuthBiometricView.BiometricState
import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED
@@ -32,14 +37,18 @@ import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMAT
/** Fingerprint only icon animator for BiometricPrompt. */
open class AuthBiometricFingerprintIconController(
context: Context,
- iconView: LottieAnimationView
+ iconView: LottieAnimationView,
+ protected val iconViewOverlay: LottieAnimationView
) : AuthIconController(context, iconView) {
+ private val isSideFps: Boolean
var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
set(value) {
if (field == value) {
return
}
+ iconViewOverlay.layoutParams.width = value.first
+ iconViewOverlay.layoutParams.height = value.second
iconView.layoutParams.width = value.first
iconView.layoutParams.height = value.second
field = value
@@ -50,9 +59,53 @@ open class AuthBiometricFingerprintIconController(
R.dimen.biometric_dialog_fingerprint_icon_width),
context.resources.getDimensionPixelSize(
R.dimen.biometric_dialog_fingerprint_icon_height))
+ var sideFps = false
+ (context.getSystemService(Context.FINGERPRINT_SERVICE)
+ as FingerprintManager?)?.let { fpm ->
+ for (prop in fpm.sensorPropertiesInternal) {
+ if (prop.isAnySidefpsType) {
+ sideFps = true
+ }
+ }
+ }
+ isSideFps = sideFps
+ val displayInfo = DisplayInfo()
+ context.display?.getDisplayInfo(displayInfo)
+ if (isSideFps && displayInfo.rotation == Surface.ROTATION_180) {
+ iconView.rotation = 180f
+ }
}
- override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ val displayInfo = DisplayInfo()
+ context.display?.getDisplayInfo(displayInfo)
+ val rotation = displayInfo.rotation
+ val iconAnimation = getSideFpsAnimationForTransition(rotation)
+ val iconViewOverlayAnimation =
+ getSideFpsOverlayAnimationForTransition(lastState, newState, rotation) ?: return
+
+ if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
+ iconView.setAnimation(iconAnimation)
+ iconViewOverlay.setAnimation(iconViewOverlayAnimation)
+ }
+
+ val iconContentDescription = getIconContentDescription(newState)
+ if (iconContentDescription != null) {
+ iconView.contentDescription = iconContentDescription
+ iconViewOverlay.contentDescription = iconContentDescription
+ }
+
+ iconView.frame = 0
+ iconViewOverlay.frame = 0
+ if (shouldAnimateForTransition(lastState, newState)) {
+ iconView.playAnimation()
+ iconViewOverlay.playAnimation()
+ }
+ LottieColorUtils.applyDynamicColors(context, iconView)
+ LottieColorUtils.applyDynamicColors(context, iconViewOverlay)
+ }
+
+ private fun updateIconNormal(@BiometricState lastState: Int, @BiometricState newState: Int) {
val icon = getAnimationForTransition(lastState, newState) ?: return
if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
@@ -68,6 +121,16 @@ open class AuthBiometricFingerprintIconController(
if (shouldAnimateForTransition(lastState, newState)) {
iconView.playAnimation()
}
+ LottieColorUtils.applyDynamicColors(context, iconView)
+ }
+
+ override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ if (isSideFps) {
+ updateIconSideFps(lastState, newState)
+ } else {
+ iconViewOverlay.visibility = View.GONE
+ updateIconNormal(lastState, newState)
+ }
}
private fun getIconContentDescription(@BiometricState newState: Int): CharSequence? {
@@ -125,4 +188,89 @@ open class AuthBiometricFingerprintIconController(
}
return if (id != null) return id else null
}
+
+ @RawRes
+ private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) {
+ Surface.ROTATION_0 -> R.raw.biometricprompt_landscape_base
+ Surface.ROTATION_90 -> R.raw.biometricprompt_portrait_base_topleft
+ Surface.ROTATION_180 -> R.raw.biometricprompt_landscape_base
+ Surface.ROTATION_270 -> R.raw.biometricprompt_portrait_base_bottomright
+ else -> R.raw.biometricprompt_landscape_base
+ }
+
+ @RawRes
+ private fun getSideFpsOverlayAnimationForTransition(
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int,
+ rotation: Int
+ ): Int? = when (newState) {
+ STATE_HELP,
+ STATE_ERROR -> {
+ when (rotation) {
+ Surface.ROTATION_0 -> R.raw.biometricprompt_fingerprint_to_error_landscape
+ Surface.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
+ Surface.ROTATION_180 ->
+ R.raw.biometricprompt_fingerprint_to_error_landscape
+ Surface.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
+ else -> R.raw.biometricprompt_fingerprint_to_error_landscape
+ }
+ }
+ STATE_AUTHENTICATING_ANIMATING_IN,
+ STATE_AUTHENTICATING -> {
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ when (rotation) {
+ Surface.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
+ Surface.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft
+ Surface.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
+ Surface.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright
+ else -> R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
+ }
+ } else {
+ when (rotation) {
+ Surface.ROTATION_0 -> R.raw.biometricprompt_fingerprint_to_error_landscape
+ Surface.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
+ Surface.ROTATION_180 ->
+ R.raw.biometricprompt_fingerprint_to_error_landscape
+ Surface.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
+ else -> R.raw.biometricprompt_fingerprint_to_error_landscape
+ }
+ }
+ }
+ STATE_AUTHENTICATED -> {
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ when (rotation) {
+ Surface.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_error_to_success_landscape
+ Surface.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_error_to_success_portrait_topleft
+ Surface.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_error_to_success_landscape
+ Surface.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright
+ else -> R.raw.biometricprompt_symbol_error_to_success_landscape
+ }
+ } else {
+ when (rotation) {
+ Surface.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
+ Surface.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
+ Surface.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
+ Surface.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
+ else -> R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
+ }
+ }
+ }
+ else -> null
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
index 9cce066afe9d..20666344ade8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
@@ -86,7 +86,7 @@ open class AuthBiometricFingerprintView(
override fun supportsSmallDialog() = false
override fun createIconController(): AuthIconController =
- AuthBiometricFingerprintIconController(mContext, mIconView)
+ AuthBiometricFingerprintIconController(mContext, mIconView, mIconViewOverlay)
fun updateOverrideIconLayoutParamsSize() {
udfpsAdapter?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index fc5cf9f005ed..e94b1f8122a6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -133,6 +133,7 @@ public abstract class AuthBiometricView extends LinearLayout {
private TextView mSubtitleView;
private TextView mDescriptionView;
private View mIconHolderView;
+ protected LottieAnimationView mIconViewOverlay;
protected LottieAnimationView mIconView;
protected TextView mIndicatorView;
@@ -651,6 +652,7 @@ public abstract class AuthBiometricView extends LinearLayout {
mTitleView = findViewById(R.id.title);
mSubtitleView = findViewById(R.id.subtitle);
mDescriptionView = findViewById(R.id.description);
+ mIconViewOverlay = findViewById(R.id.biometric_icon_overlay);
mIconView = findViewById(R.id.biometric_icon);
mIconHolderView = findViewById(R.id.biometric_icon_frame);
mIndicatorView = findViewById(R.id.indicator);
@@ -689,6 +691,11 @@ public abstract class AuthBiometricView extends LinearLayout {
mIconController = createIconController();
if (mIconController.getActsAsConfirmButton()) {
+ mIconViewOverlay.setOnClickListener((view)->{
+ if (mState == STATE_PENDING_CONFIRMATION) {
+ updateState(STATE_AUTHENTICATED);
+ }
+ });
mIconView.setOnClickListener((view) -> {
if (mState == STATE_PENDING_CONFIRMATION) {
updateState(STATE_AUTHENTICATED);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt
new file mode 100644
index 000000000000..f2d8aaa30f21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+/**
+ * Provides whether an acquired error message should be shown immediately when its received (see
+ * [shouldDefer]) or should be shown when the biometric error is received [getDeferredMessage].
+ * @property excludedMessages messages that are excluded from counts
+ * @property messagesToDefer messages that shouldn't show immediately when received, but may be
+ * shown later if the message is the most frequent message processed and meets [THRESHOLD]
+ * percentage of all messages (excluding [excludedMessages])
+ */
+class BiometricMessageDeferral(
+ private val excludedMessages: Set<Int>,
+ private val messagesToDefer: Set<Int>
+) {
+ private val msgCounts: MutableMap<Int, Int> = HashMap() // msgId => frequency of msg
+ private val msgIdToCharSequence: MutableMap<Int, CharSequence> = HashMap() // msgId => message
+ private var totalRelevantMessages = 0
+ private var mostFrequentMsgIdToDefer: Int? = null
+
+ /** Reset all saved counts. */
+ fun reset() {
+ totalRelevantMessages = 0
+ msgCounts.clear()
+ msgIdToCharSequence.clear()
+ }
+
+ /** Whether the given message should be deferred instead of being shown immediately. */
+ fun shouldDefer(acquiredMsgId: Int): Boolean {
+ return messagesToDefer.contains(acquiredMsgId)
+ }
+
+ /**
+ * Adds the acquiredMsgId to the counts if it's not in [excludedMessages]. We still count
+ * messages that shouldn't be deferred in these counts.
+ */
+ fun processMessage(acquiredMsgId: Int, helpString: CharSequence) {
+ if (excludedMessages.contains(acquiredMsgId)) {
+ return
+ }
+
+ totalRelevantMessages++
+ msgIdToCharSequence[acquiredMsgId] = helpString
+
+ val newAcquiredMsgCount = msgCounts.getOrDefault(acquiredMsgId, 0) + 1
+ msgCounts[acquiredMsgId] = newAcquiredMsgCount
+ if (
+ messagesToDefer.contains(acquiredMsgId) &&
+ (mostFrequentMsgIdToDefer == null ||
+ newAcquiredMsgCount > msgCounts.getOrDefault(mostFrequentMsgIdToDefer!!, 0))
+ ) {
+ mostFrequentMsgIdToDefer = acquiredMsgId
+ }
+ }
+
+ /**
+ * Get the most frequent deferred message that meets the [THRESHOLD] percentage of processed
+ * messages excluding [excludedMessages].
+ * @return null if no messages have been deferred OR deferred messages didn't meet the
+ * [THRESHOLD] percentage of messages to show.
+ */
+ fun getDeferredMessage(): CharSequence? {
+ mostFrequentMsgIdToDefer?.let {
+ if (msgCounts.getOrDefault(it, 0) > (THRESHOLD * totalRelevantMessages)) {
+ return msgIdToCharSequence[mostFrequentMsgIdToDefer]
+ }
+ }
+
+ return null
+ }
+ companion object {
+ const val THRESHOLD = .5f
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index d96476fbf087..49e378e4a76f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -99,11 +99,12 @@ public class UdfpsEnrollProgressBarDrawable extends Drawable {
mProgressColor = context.getColor(R.color.udfps_enroll_progress);
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
mIsAccessibilityEnabled = am.isTouchExplorationEnabled();
- mOnFirstBucketFailedColor = context.getColor(R.color.udfps_moving_target_fill_error);
if (!mIsAccessibilityEnabled) {
mHelpColor = context.getColor(R.color.udfps_enroll_progress_help);
+ mOnFirstBucketFailedColor = context.getColor(R.color.udfps_moving_target_fill_error);
} else {
mHelpColor = context.getColor(R.color.udfps_enroll_progress_help_with_talkback);
+ mOnFirstBucketFailedColor = mHelpColor;
}
mCheckmarkDrawable = context.getDrawable(R.drawable.udfps_enroll_checkmark);
mCheckmarkDrawable.mutate();
@@ -166,8 +167,7 @@ public class UdfpsEnrollProgressBarDrawable extends Drawable {
}
private void updateProgress(int remainingSteps, int totalSteps, boolean showingHelp) {
- if (mRemainingSteps == remainingSteps && mTotalSteps == totalSteps
- && mShowingHelp == showingHelp) {
+ if (mRemainingSteps == remainingSteps && mTotalSteps == totalSteps) {
return;
}
@@ -197,7 +197,6 @@ public class UdfpsEnrollProgressBarDrawable extends Drawable {
}
}
- mShowingHelp = showingHelp;
mRemainingSteps = remainingSteps;
mTotalSteps = totalSteps;
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index 0b65966ca109..6c45af2a8729 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -24,11 +24,15 @@ import android.graphics.drawable.Drawable
* [Icon.Resource] to a resource.
*/
sealed class Icon {
+ abstract val contentDescription: ContentDescription?
+
data class Loaded(
val drawable: Drawable,
+ override val contentDescription: ContentDescription?,
) : Icon()
data class Resource(
@DrawableRes val res: Int,
+ override val contentDescription: ContentDescription?,
) : Icon()
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Text.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Text.kt
new file mode 100644
index 000000000000..5d0e08ffc307
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Text.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.common.shared.model
+
+import android.annotation.StringRes
+
+/**
+ * Models a text, that can either be already [loaded][Text.Loaded] or be a [reference]
+ * [Text.Resource] to a resource.
+ */
+sealed class Text {
+ data class Loaded(
+ val text: String?,
+ ) : Text()
+
+ data class Resource(
+ @StringRes val res: Int,
+ ) : Text()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
index d6433aae9845..93ae637c7182 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
@@ -21,14 +21,16 @@ import com.android.systemui.common.shared.model.ContentDescription
object ContentDescriptionViewBinder {
fun bind(
- contentDescription: ContentDescription,
+ contentDescription: ContentDescription?,
view: View,
) {
- when (contentDescription) {
- is ContentDescription.Loaded -> view.contentDescription = contentDescription.description
- is ContentDescription.Resource -> {
- view.contentDescription = view.context.resources.getString(contentDescription.res)
+ view.contentDescription =
+ when (contentDescription) {
+ null -> null
+ is ContentDescription.Loaded -> contentDescription.description
+ is ContentDescription.Resource -> {
+ view.context.resources.getString(contentDescription.res)
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
index aecee2afc9d2..108e22bc392b 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
@@ -24,6 +24,7 @@ object IconViewBinder {
icon: Icon,
view: ImageView,
) {
+ ContentDescriptionViewBinder.bind(icon.contentDescription, view)
when (icon) {
is Icon.Loaded -> view.setImageDrawable(icon.drawable)
is Icon.Resource -> view.setImageResource(icon.res)
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TextViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TextViewBinder.kt
new file mode 100644
index 000000000000..396e8bb5a60d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TextViewBinder.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.common.ui.binder
+
+import android.widget.TextView
+import com.android.systemui.common.shared.model.Text
+
+object TextViewBinder {
+ fun bind(view: TextView, viewModel: Text) {
+ view.text =
+ when (viewModel) {
+ is Text.Resource -> view.context.getString(viewModel.res)
+ is Text.Loaded -> viewModel.text
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 91e75f6436c2..822f8f2e6191 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -33,6 +33,12 @@ interface ControlsUiController {
fun hide()
/**
+ * Returns the preferred activity to start, depending on if the user has favorited any
+ * controls.
+ */
+ fun resolveActivity(): Class<*>
+
+ /**
* Request all open dialogs be closed. Set [immediately] to true to dismiss without
* animations.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index c1e99f578459..bf7d71635694 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -149,6 +149,19 @@ class ControlsUiControllerImpl @Inject constructor (
}
}
+ override fun resolveActivity(): Class<*> {
+ val allStructures = controlsController.get().getFavorites()
+ val selectedStructure = getPreferredStructure(allStructures)
+
+ return if (controlsController.get().addSeedingFavoritesCallback(onSeedingComplete)) {
+ ControlsActivity::class.java
+ } else if (selectedStructure.controls.isEmpty() && allStructures.size <= 1) {
+ ControlsProviderSelectorActivity::class.java
+ } else {
+ ControlsActivity::class.java
+ }
+ }
+
override fun show(
parent: ViewGroup,
onDismiss: Runnable,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index d89c0be26351..b59855426a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -101,6 +101,11 @@ public interface DozeHost {
* Called when the always on suppression state changes. See {@link #isAlwaysOnSuppressed()}.
*/
default void onAlwaysOnSuppressedChanged(boolean suppressed) {}
+
+ /**
+ * Called when the dozing state may have been updated.
+ */
+ default void onDozingChanged(boolean isDozing) {}
}
interface PulseCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index c4553c4cb299..78099d1fd12f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -66,6 +66,10 @@ public class Flags {
public static final UnreleasedFlag FSI_REQUIRES_KEYGUARD =
new UnreleasedFlag(110, true);
+ public static final UnreleasedFlag INSTANT_VOICE_REPLY = new UnreleasedFlag(111, true);
+
+ // next id: 112
+
/***************************************/
// 200 - keyguard/lockscreen
@@ -88,12 +92,6 @@ public class Flags {
public static final ResourceBooleanFlag FACE_SCANNING_ANIM =
new ResourceBooleanFlag(205, R.bool.config_enableFaceScanningAnimation);
- /**
- * Whether the KeyguardBottomArea(View|Controller) should use the modern architecture or the old
- * one.
- */
- public static final ReleasedFlag MODERN_BOTTOM_AREA = new ReleasedFlag(206, true);
-
public static final UnreleasedFlag LOCKSCREEN_CUSTOM_CLOCKS = new UnreleasedFlag(207);
/**
@@ -186,6 +184,9 @@ public class Flags {
// 801 - region sampling
public static final UnreleasedFlag REGION_SAMPLING = new UnreleasedFlag(801);
+ // 802 - wallpaper rendering
+ public static final UnreleasedFlag USE_CANVAS_RENDERER = new UnreleasedFlag(802);
+
/***************************************/
// 900 - media
public static final ReleasedFlag MEDIA_TAP_TO_TRANSFER = new ReleasedFlag(900);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 898959e6eb58..ee5ef9905819 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -29,6 +29,7 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -160,6 +161,9 @@ public class KeyguardService extends Service {
return apps.length == 0 ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
: TRANSIT_OLD_KEYGUARD_GOING_AWAY;
} else if (type == TRANSIT_KEYGUARD_OCCLUDE) {
+ boolean isOccludeByDream = apps.length > 0 && apps[0].taskInfo.topActivityType
+ == WindowConfiguration.ACTIVITY_TYPE_DREAM;
+ if (isOccludeByDream) return TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
return TRANSIT_OLD_KEYGUARD_OCCLUDE;
} else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
@@ -271,6 +275,12 @@ public class KeyguardService extends Service {
definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE,
occludeAnimationAdapter);
+ final RemoteAnimationAdapter occludeByDreamAnimationAdapter =
+ new RemoteAnimationAdapter(
+ mKeyguardViewMediator.getOccludeByDreamAnimationRunner(), 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM,
+ occludeByDreamAnimationAdapter);
+
final RemoteAnimationAdapter unoccludeAnimationAdapter =
new RemoteAnimationAdapter(
mKeyguardViewMediator.getUnoccludeAnimationRunner(), 0, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 9ecfb7521151..8eca1bad213b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -282,7 +282,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
* window like any other app. This can be true while [willUnlockWithSmartspaceTransition] is
* false, if the smartspace is not available or was not ready in time.
*/
- private var willUnlockWithInWindowLauncherAnimations: Boolean = false
+ @VisibleForTesting
+ var willUnlockWithInWindowLauncherAnimations: Boolean = false
/**
* Whether we decided in [prepareForInWindowLauncherAnimations] that we are able to and want to
@@ -574,7 +575,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
// Now that the Launcher surface (with its smartspace positioned identically to ours) is
// visible, hide our smartspace.
- lockscreenSmartspace!!.visibility = View.INVISIBLE
+ lockscreenSmartspace?.visibility = View.INVISIBLE
// As soon as the shade has animated out of the way, finish the keyguard exit animation. The
// in-window animations in the Launcher window will end on their own.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2c60d5d67dac..dc034360ad09 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -887,6 +887,86 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private IRemoteAnimationRunner mOccludeAnimationRunner =
new OccludeActivityLaunchRemoteAnimationRunner(mOccludeAnimationController);
+ private final IRemoteAnimationRunner mOccludeByDreamAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Nullable private ValueAnimator mOccludeByDreamAnimator;
+
+ @Override
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ if (mOccludeByDreamAnimator != null) {
+ mOccludeByDreamAnimator.cancel();
+ }
+ setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */);
+ if (DEBUG) {
+ Log.d(TAG, "Occlude by Dream animation cancelled. Occluded state is now: "
+ + mOccluded);
+ }
+ }
+
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+ setOccluded(true /* isOccluded */, true /* animate */);
+
+ if (apps == null || apps.length == 0 || apps[0] == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No apps provided to the OccludeByDream runner; "
+ + "skipping occluding animation.");
+ }
+ finishedCallback.onAnimationFinished();
+ return;
+ }
+
+ final RemoteAnimationTarget primary = apps[0];
+ final boolean isDream = (apps[0].taskInfo.topActivityType
+ == WindowConfiguration.ACTIVITY_TYPE_DREAM);
+ if (!isDream) {
+ Log.w(TAG, "The occluding app isn't Dream; "
+ + "finishing up. Please check that the config is correct.");
+ finishedCallback.onAnimationFinished();
+ return;
+ }
+
+ final SyncRtSurfaceTransactionApplier applier =
+ new SyncRtSurfaceTransactionApplier(
+ mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+
+ mContext.getMainExecutor().execute(() -> {
+ if (mOccludeByDreamAnimator != null) {
+ mOccludeByDreamAnimator.cancel();
+ }
+
+ mOccludeByDreamAnimator = ValueAnimator.ofFloat(0f, 1f);
+ // Use the same duration as for the UNOCCLUDE.
+ mOccludeByDreamAnimator.setDuration(UNOCCLUDE_ANIMATION_DURATION);
+ mOccludeByDreamAnimator.setInterpolator(Interpolators.LINEAR);
+ mOccludeByDreamAnimator.addUpdateListener(
+ animation -> {
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder
+ paramsBuilder =
+ new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(primary.leash)
+ .withAlpha(animation.getAnimatedFraction());
+ applier.scheduleApply(paramsBuilder.build());
+ });
+ mOccludeByDreamAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ mOccludeByDreamAnimator = null;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ mOccludeByDreamAnimator.start();
+ });
+ }
+ };
+
/**
* Animation controller for activities that unocclude the keyguard. This does not use the
* ActivityLaunchAnimator since we're just translating down, rather than emerging from a view
@@ -1682,6 +1762,10 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
return mOccludeAnimationRunner;
}
+ public IRemoteAnimationRunner getOccludeByDreamAnimationRunner() {
+ return mOccludeByDreamAnimationRunner;
+ }
+
public IRemoteAnimationRunner getUnoccludeAnimationRunner() {
return mUnoccludeAnimationRunner;
}
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 e52d9ee7b9d4..840a4b20a3f0 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
@@ -20,6 +20,7 @@ import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLoggin
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.doze.DozeHost
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
@@ -28,6 +29,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
/** Defines interface for classes that encapsulate application state for the keyguard. */
interface KeyguardRepository {
@@ -102,6 +104,7 @@ class KeyguardRepositoryImpl
constructor(
statusBarStateController: StatusBarStateController,
keyguardStateController: KeyguardStateController,
+ dozeHost: DozeHost,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -136,19 +139,21 @@ constructor(
awaitClose { keyguardStateController.removeCallback(callback) }
}
- override val isDozing: Flow<Boolean> = conflatedCallbackFlow {
- val callback =
- object : StatusBarStateController.StateListener {
- override fun onDozingChanged(isDozing: Boolean) {
- trySendWithFailureLogging(isDozing, TAG, "updated isDozing")
- }
+ override val isDozing: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : DozeHost.Callback {
+ override fun onDozingChanged(isDozing: Boolean) {
+ trySendWithFailureLogging(isDozing, TAG, "updated isDozing")
+ }
+ }
+ dozeHost.addCallback(callback)
+ trySendWithFailureLogging(false, TAG, "initial isDozing: false")
+
+ awaitClose { dozeHost.removeCallback(callback) }
}
+ .distinctUntilChanged()
- statusBarStateController.addCallback(callback)
- trySendWithFailureLogging(statusBarStateController.isDozing, TAG, "initial isDozing")
-
- awaitClose { statusBarStateController.removeCallback(callback) }
- }
override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
val callback =
object : StatusBarStateController.StateListener {
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 9a69e26488d9..95acc0b8564e 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
@@ -32,6 +32,7 @@ import javax.inject.Inject
import kotlin.reflect.KClass
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.onStart
@SysUISingleton
class KeyguardQuickAffordanceInteractor
@@ -88,7 +89,15 @@ constructor(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceModel> {
val configs = registry.getAll(position)
- return combine(configs.map { config -> config.state }) { states ->
+ return combine(
+ configs.map { config ->
+ // We emit an initial "Hidden" value to make sure that there's always an initial
+ // value and avoid subtle bugs where the downstream isn't receiving any values
+ // because one config implementation is not emitting an initial value. For example,
+ // see b/244296596.
+ config.state.onStart { emit(KeyguardQuickAffordanceConfig.State.Hidden) }
+ }
+ ) { states ->
val index = states.indexOfFirst { it is KeyguardQuickAffordanceConfig.State.Visible }
if (index != -1) {
val visibleState = states[index] as KeyguardQuickAffordanceConfig.State.Visible
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 8f32ff9db50c..ac2c9b1d7ff2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -94,6 +94,7 @@ constructor(
hasFavorites = favorites?.isNotEmpty() == true,
hasServiceInfos = serviceInfos.isNotEmpty(),
iconResourceId = component.getTileImageId(),
+ visibility = component.getVisibility(),
),
TAG,
)
@@ -110,9 +111,16 @@ constructor(
isFeatureEnabled: Boolean,
hasFavorites: Boolean,
hasServiceInfos: Boolean,
+ visibility: ControlsComponent.Visibility,
@DrawableRes iconResourceId: Int?,
): KeyguardQuickAffordanceConfig.State {
- return if (isFeatureEnabled && hasFavorites && hasServiceInfos && iconResourceId != null) {
+ return if (
+ isFeatureEnabled &&
+ hasFavorites &&
+ hasServiceInfos &&
+ iconResourceId != null &&
+ visibility == ControlsComponent.Visibility.AVAILABLE
+ ) {
KeyguardQuickAffordanceConfig.State.Visible(
icon = ContainedDrawable.WithResource(iconResourceId),
contentDescriptionResourceId = component.getTileTitleId(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index ae4c7c73a2ea..6baf6e137ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -41,6 +41,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.NotifPanelEvents
import com.android.systemui.statusbar.CrossFadeHelper
@@ -401,7 +402,7 @@ class MediaHierarchyManager @Inject constructor(
}
/**
- * Is the doze animation currently Running
+ * Is the dream overlay currently active
*/
private var dreamOverlayActive: Boolean = false
private set(value) {
@@ -412,6 +413,17 @@ class MediaHierarchyManager @Inject constructor(
}
/**
+ * Is the dream media complication currently active
+ */
+ private var dreamMediaComplicationActive: Boolean = false
+ private set(value) {
+ if (field != value) {
+ field = value
+ updateDesiredLocation(forceNoAnimation = true)
+ }
+ }
+
+ /**
* The current cross fade progress. 0.5f means it's just switching
* between the start and the end location and the content is fully faded, while 0.75f means
* that we're halfway faded in again in the target state.
@@ -500,6 +512,12 @@ class MediaHierarchyManager @Inject constructor(
})
dreamOverlayStateController.addCallback(object : DreamOverlayStateController.Callback {
+ override fun onComplicationsChanged() {
+ dreamMediaComplicationActive = dreamOverlayStateController.complications.any {
+ it is MediaDreamComplication
+ }
+ }
+
override fun onStateChanged() {
dreamOverlayStateController.isOverlayActive.also { dreamOverlayActive = it }
}
@@ -1068,7 +1086,7 @@ class MediaHierarchyManager @Inject constructor(
val onLockscreen = (!bypassController.bypassEnabled &&
(statusbarState == StatusBarState.KEYGUARD))
val location = when {
- dreamOverlayActive -> LOCATION_DREAM_OVERLAY
+ dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY
(qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
!hasActiveMedia -> LOCATION_QS
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 35a6c74518e0..5d6d683f93f6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -34,15 +34,14 @@ import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
-import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
-import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
+import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
+import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
/**
@@ -56,18 +55,16 @@ class MediaTttChipControllerReceiver @Inject constructor(
context: Context,
@MediaTttReceiverLogger logger: MediaTttLogger,
windowManager: WindowManager,
- viewUtil: ViewUtil,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
@Main private val mainHandler: Handler,
private val uiEventLogger: MediaTttReceiverUiEventLogger,
-) : MediaTttChipControllerCommon<ChipReceiverInfo>(
+) : TemporaryViewDisplayController<ChipReceiverInfo>(
context,
logger,
windowManager,
- viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
@@ -119,18 +116,18 @@ class MediaTttChipControllerReceiver @Inject constructor(
uiEventLogger.logReceiverStateChange(chipState)
if (chipState == ChipStateReceiver.FAR_FROM_SENDER) {
- removeChip(removalReason = ChipStateReceiver.FAR_FROM_SENDER::class.simpleName!!)
+ removeView(removalReason = ChipStateReceiver.FAR_FROM_SENDER::class.simpleName!!)
return
}
if (appIcon == null) {
- displayChip(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
+ displayView(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
return
}
appIcon.loadDrawableAsync(
context,
Icon.OnDrawableLoadedListener { drawable ->
- displayChip(ChipReceiverInfo(routeInfo, drawable, appName))
+ displayView(ChipReceiverInfo(routeInfo, drawable, appName))
},
// Notify the listener on the main handler since the listener will update
// the UI.
@@ -138,19 +135,19 @@ class MediaTttChipControllerReceiver @Inject constructor(
)
}
- override fun updateChipView(newChipInfo: ChipReceiverInfo, currentChipView: ViewGroup) {
- super.updateChipView(newChipInfo, currentChipView)
+ override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
+ super.updateView(newInfo, currentView)
val iconName = setIcon(
- currentChipView,
- newChipInfo.routeInfo.clientPackageName,
- newChipInfo.appIconDrawableOverride,
- newChipInfo.appNameOverride
+ currentView,
+ newInfo.routeInfo.clientPackageName,
+ newInfo.appIconDrawableOverride,
+ newInfo.appNameOverride
)
- currentChipView.contentDescription = iconName
+ currentView.contentDescription = iconName
}
- override fun animateChipIn(chipView: ViewGroup) {
- val appIconView = chipView.requireViewById<View>(R.id.app_icon)
+ override fun animateViewIn(view: ViewGroup) {
+ val appIconView = view.requireViewById<View>(R.id.app_icon)
appIconView.animate()
.translationYBy(-1 * getTranslationAmount().toFloat())
.setDuration(30.frames)
@@ -160,8 +157,8 @@ class MediaTttChipControllerReceiver @Inject constructor(
.setDuration(5.frames)
.start()
// Using withEndAction{} doesn't apply a11y focus when screen is unlocked.
- appIconView.postOnAnimation { chipView.requestAccessibilityFocus() }
- startRipple(chipView.requireViewById(R.id.ripple))
+ appIconView.postOnAnimation { view.requestAccessibilityFocus() }
+ startRipple(view.requireViewById(R.id.ripple))
}
override fun getIconSize(isAppIcon: Boolean): Int? =
@@ -216,7 +213,7 @@ data class ChipReceiverInfo(
val routeInfo: MediaRoute2Info,
val appIconDrawableOverride: Drawable?,
val appNameOverride: CharSequence?
-) : ChipInfoCommon {
+) : TemporaryViewInfo {
override fun getTimeoutMs() = DEFAULT_TIMEOUT_MILLIS
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index a153cb6c0d31..bde588c14fc8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -25,7 +25,7 @@ import androidx.annotation.StringRes
import com.android.internal.logging.UiEventLogger
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
-import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
+import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
/**
* A class enumerating all the possible states of the media tap-to-transfer chip on the sender
@@ -120,7 +120,7 @@ enum class ChipStateSender(
// state, but that may take too long to go through the binder and the user may be
// confused ast o why the UI hasn't changed yet. So, we immediately change the UI
// here.
- controllerSender.displayChip(
+ controllerSender.displayView(
ChipSenderInfo(
TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo, undoCallback
)
@@ -155,7 +155,7 @@ enum class ChipStateSender(
// state, but that may take too long to go through the binder and the user may be
// confused as to why the UI hasn't changed yet. So, we immediately change the UI
// here.
- controllerSender.displayChip(
+ controllerSender.displayView(
ChipSenderInfo(
TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo, undoCallback
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 933548963390..0c1ebd70c572 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -33,14 +33,13 @@ import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ViewHierarchyAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
-import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
-import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
+import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
+import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
/**
@@ -53,17 +52,15 @@ class MediaTttChipControllerSender @Inject constructor(
context: Context,
@MediaTttSenderLogger logger: MediaTttLogger,
windowManager: WindowManager,
- viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
private val uiEventLogger: MediaTttSenderUiEventLogger
-) : MediaTttChipControllerCommon<ChipSenderInfo>(
+) : TemporaryViewDisplayController<ChipSenderInfo>(
context,
logger,
windowManager,
- viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
@@ -106,53 +103,52 @@ class MediaTttChipControllerSender @Inject constructor(
uiEventLogger.logSenderStateChange(chipState)
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
- removeChip(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!)
+ removeView(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!)
} else {
- displayChip(ChipSenderInfo(chipState, routeInfo, undoCallback))
+ displayView(ChipSenderInfo(chipState, routeInfo, undoCallback))
}
}
- /** Displays the chip view for the given state. */
- override fun updateChipView(
- newChipInfo: ChipSenderInfo,
- currentChipView: ViewGroup
+ override fun updateView(
+ newInfo: ChipSenderInfo,
+ currentView: ViewGroup
) {
- super.updateChipView(newChipInfo, currentChipView)
+ super.updateView(newInfo, currentView)
- val chipState = newChipInfo.state
+ val chipState = newInfo.state
// App icon
- val iconName = setIcon(currentChipView, newChipInfo.routeInfo.clientPackageName)
+ val iconName = setIcon(currentView, newInfo.routeInfo.clientPackageName)
// Text
- val otherDeviceName = newChipInfo.routeInfo.name.toString()
+ val otherDeviceName = newInfo.routeInfo.name.toString()
val chipText = chipState.getChipTextString(context, otherDeviceName)
- currentChipView.requireViewById<TextView>(R.id.text).text = chipText
+ currentView.requireViewById<TextView>(R.id.text).text = chipText
// Loading
- currentChipView.requireViewById<View>(R.id.loading).visibility =
+ currentView.requireViewById<View>(R.id.loading).visibility =
chipState.isMidTransfer.visibleIfTrue()
// Undo
- val undoView = currentChipView.requireViewById<View>(R.id.undo)
+ val undoView = currentView.requireViewById<View>(R.id.undo)
val undoClickListener = chipState.undoClickListener(
- this, newChipInfo.routeInfo, newChipInfo.undoCallback, uiEventLogger
+ this, newInfo.routeInfo, newInfo.undoCallback, uiEventLogger
)
undoView.setOnClickListener(undoClickListener)
undoView.visibility = (undoClickListener != null).visibleIfTrue()
// Failure
- currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
+ currentView.requireViewById<View>(R.id.failure_icon).visibility =
chipState.isTransferFailure.visibleIfTrue()
// For accessibility
- currentChipView.requireViewById<ViewGroup>(
+ currentView.requireViewById<ViewGroup>(
R.id.media_ttt_sender_chip_inner
).contentDescription = "$iconName $chipText"
}
- override fun animateChipIn(chipView: ViewGroup) {
- val chipInnerView = chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
+ override fun animateViewIn(view: ViewGroup) {
+ val chipInnerView = view.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
ViewHierarchyAnimator.animateAddition(
chipInnerView,
ViewHierarchyAnimator.Hotspot.TOP,
@@ -165,14 +161,14 @@ class MediaTttChipControllerSender @Inject constructor(
)
}
- override fun removeChip(removalReason: String) {
+ override fun removeView(removalReason: String) {
// Don't remove the chip if we're mid-transfer since the user should still be able to
// see the status of the transfer. (But do remove it if it's finally timed out.)
- if (chipInfo?.state?.isMidTransfer == true &&
- removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
+ if (info?.state?.isMidTransfer == true &&
+ removalReason != TemporaryDisplayRemovalReason.REASON_TIMEOUT) {
return
}
- super.removeChip(removalReason)
+ super.removeView(removalReason)
}
private fun Boolean.visibleIfTrue(): Int {
@@ -188,7 +184,7 @@ data class ChipSenderInfo(
val state: ChipStateSender,
val routeInfo: MediaRoute2Info,
val undoCallback: IUndoMediaTransferCallback? = null
-) : ChipInfoCommon {
+) : TemporaryViewInfo {
override fun getTimeoutMs() = state.timeout
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 75e48d2220c8..30947e839f0a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -35,7 +35,6 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
@@ -229,10 +228,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private Locale mLocale;
private int mLayoutDirection;
- private boolean mAllowForceNavBarHandleOpaque;
- private boolean mForceNavBarHandleOpaque;
private Optional<Long> mHomeButtonLongPressDurationMs;
- private boolean mIsCurrentUserSetup;
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
@@ -379,37 +375,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
@Override
- public void onNavBarButtonAlphaChanged(float alpha, boolean animate) {
- if (!mIsCurrentUserSetup) {
- // If the current user is not yet setup, then don't update any button alphas
- return;
- }
- if (QuickStepContract.isLegacyMode(mNavBarMode)) {
- // Don't allow the bar buttons to be affected by the alpha
- return;
- }
-
- ButtonDispatcher buttonDispatcher = null;
- boolean forceVisible = false;
- if (QuickStepContract.isGesturalMode(mNavBarMode)) {
- // Disallow home handle animations when in gestural
- animate = false;
- forceVisible = mAllowForceNavBarHandleOpaque && mForceNavBarHandleOpaque;
- buttonDispatcher = mView.getHomeHandle();
- if (getBarTransitions() != null) {
- getBarTransitions().setBackgroundOverrideAlpha(alpha);
- }
- } else if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
- buttonDispatcher = mView.getBackButton();
- }
- if (buttonDispatcher != null) {
- buttonDispatcher.setVisibility(
- (forceVisible || alpha > 0) ? View.VISIBLE : View.INVISIBLE);
- buttonDispatcher.setAlpha(forceVisible ? 1f : alpha, animate);
- }
- }
-
- @Override
public void onHomeRotationEnabled(boolean enabled) {
mView.getRotationButtonController().setHomeRotationEnabled(enabled);
}
@@ -456,15 +421,10 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
- mForceNavBarHandleOpaque = properties.getBoolean(
- NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
- }
-
if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) {
mHomeButtonLongPressDurationMs = Optional.of(
- properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0)
- ).filter(duration -> duration != 0);
+ properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0))
+ .filter(duration -> duration != 0);
if (mView != null) {
reconfigureHomeLongClick();
}
@@ -472,14 +432,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
};
- private final DeviceProvisionedController.DeviceProvisionedListener mUserSetupListener =
- new DeviceProvisionedController.DeviceProvisionedListener() {
- @Override
- public void onUserSetupChanged() {
- mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
- }
- };
-
private final NotificationShadeDepthController.DepthListener mDepthListener =
new NotificationShadeDepthController.DepthListener() {
boolean mHasBlurs;
@@ -660,12 +612,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mCommandQueue.addCallback(this);
mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
mNavBarHelper.init();
- mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
- R.bool.allow_force_nav_bar_handle_opaque);
- mForceNavBarHandleOpaque = mDeviceConfigProxy.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- NAV_BAR_HANDLE_FORCE_OPAQUE,
- /* defaultValue = */ true);
mHomeButtonLongPressDurationMs = Optional.of(mDeviceConfigProxy.getLong(
DeviceConfig.NAMESPACE_SYSTEMUI,
HOME_BUTTON_LONG_PRESS_DURATION_MS,
@@ -685,8 +631,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
// Respect the latest disabled-flags.
mCommandQueue.recomputeDisableFlags(mDisplayId, false);
- mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
- mDeviceProvisionedController.addCallback(mUserSetupListener);
mNotificationShadeDepthController.addListener(mDepthListener);
}
@@ -698,7 +642,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.destroy();
- mDeviceProvisionedController.removeCallback(mUserSetupListener);
mNotificationShadeDepthController.removeListener(mDepthListener);
mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
diff --git a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
index 3709a86f2fa5..7184fa0685af 100644
--- a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
@@ -20,13 +20,18 @@ import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
+import com.android.systemui.power.data.repository.PowerRepositoryModule;
import dagger.Binds;
import dagger.Module;
/** Dagger Module for code in the power package. */
-@Module
+@Module(
+ includes = {
+ PowerRepositoryModule.class,
+ }
+)
public interface PowerModule {
/** */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
new file mode 100644
index 000000000000..b2e04bb4f26f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.power.data.repository
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.PowerManager
+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 javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Defines interface for classes that act as source of truth for power-related data. */
+interface PowerRepository {
+ /** Whether the device is interactive. Starts with the current state. */
+ val isInteractive: Flow<Boolean>
+}
+
+@SysUISingleton
+class PowerRepositoryImpl
+@Inject
+constructor(
+ manager: PowerManager,
+ dispatcher: BroadcastDispatcher,
+) : PowerRepository {
+
+ override val isInteractive: Flow<Boolean> = conflatedCallbackFlow {
+ fun send() {
+ trySendWithFailureLogging(manager.isInteractive, TAG)
+ }
+
+ val receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ send()
+ }
+ }
+
+ dispatcher.registerReceiver(
+ receiver,
+ IntentFilter().apply {
+ addAction(Intent.ACTION_SCREEN_ON)
+ addAction(Intent.ACTION_SCREEN_OFF)
+ },
+ )
+ send()
+
+ awaitClose { dispatcher.unregisterReceiver(receiver) }
+ }
+
+ companion object {
+ private const val TAG = "PowerRepository"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepositoryModule.kt
new file mode 100644
index 000000000000..491da65c0291
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepositoryModule.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.power.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface PowerRepositoryModule {
+ @Binds fun bindRepository(impl: PowerRepositoryImpl): PowerRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
new file mode 100644
index 000000000000..3f799f724fe7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.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.power.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.power.data.repository.PowerRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Hosts business logic for interacting with the power system. */
+@SysUISingleton
+class PowerInteractor
+@Inject
+constructor(
+ repository: PowerRepository,
+) {
+ /** Whether the screen is on or off. */
+ val isInteractive: Flow<Boolean> = repository.isInteractive
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 56298fa155fa..920f4634abe2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -16,6 +16,7 @@ package com.android.systemui.qs;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -30,15 +31,11 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.PagedTileLayout.PageListener;
-import com.android.systemui.qs.QSHost.Callback;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.tileimpl.HeightOverrideable;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.Collection;
@@ -47,16 +44,26 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
-/** */
+/**
+ * Performs the animated transition between the QQS and QS views.
+ *
+ * <p>The transition is driven externally via {@link #setPosition(float)}, where 0 is a fully
+ * collapsed QQS and one a fully expanded QS.
+ *
+ * <p>This implementation maintains a set of {@code TouchAnimator} to transition the properties of
+ * views both in QQS and QS. These {@code TouchAnimator} are re-created lazily if contents of either
+ * view change, see {@link #requestAnimatorUpdate()}.
+ *
+ * <p>During the transition, both QS and QQS are visible. For overlapping tiles (Whenever the QS
+ * shows the first page), the corresponding QS tiles are hidden until QS is fully expanded.
+ */
@QSScope
-public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener,
- OnAttachStateChangeListener, Tunable {
+public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener,
+ TouchAnimator.Listener, OnLayoutChangeListener,
+ OnAttachStateChangeListener {
private static final String TAG = "QSAnimator";
- private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim";
- private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
-
private static final float EXPANDED_TILE_DELAY = .86f;
//Non first page delays
private static final float QS_TILE_LABEL_FADE_OUT_START = 0.15f;
@@ -65,7 +72,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
public static final float SHORT_PARALLAX_AMOUNT = 0.1f;
-
/**
* List of all views that will be reset when clearing animation state
* see {@link #clearAnimationState()} }
@@ -125,14 +131,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
private boolean mNeedsAnimatorUpdate = false;
private boolean mOnKeyguard;
- private boolean mAllowFancy;
- private boolean mFullRows;
private int mNumQuickTiles;
private int mLastQQSTileHeight;
private float mLastPosition;
private final QSTileHost mHost;
private final Executor mExecutor;
- private final TunerService mTunerService;
private boolean mShowCollapsedOnKeyguard;
private boolean mTranslateWhileExpanding;
private int mQQSTop;
@@ -153,7 +156,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
mQuickStatusBarHeader = quickStatusBarHeader;
mHost = qsTileHost;
mExecutor = executor;
- mTunerService = tunerService;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mHost.addCallback(this);
mQsPanelController.addOnAttachStateChangeListener(this);
@@ -199,7 +201,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
setCurrentPosition();
}
-
private void setCurrentPosition() {
setPosition(mLastPosition);
}
@@ -210,28 +211,13 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
}
@Override
- public void onViewAttachedToWindow(@Nullable View v) {
- mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION,
- MOVE_FULL_ROWS);
+ public void onViewAttachedToWindow(@NonNull View view) {
+ updateAnimators();
}
@Override
- public void onViewDetachedFromWindow(View v) {
+ public void onViewDetachedFromWindow(@NonNull View v) {
mHost.removeCallback(this);
- mTunerService.removeTunable(this);
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (ALLOW_FANCY_ANIMATION.equals(key)) {
- mAllowFancy = TunerService.parseIntegerSwitch(newValue, true);
- if (!mAllowFancy) {
- clearAnimationState();
- }
- } else if (MOVE_FULL_ROWS.equals(key)) {
- mFullRows = TunerService.parseIntegerSwitch(newValue, true);
- }
- updateAnimators();
}
private void addNonFirstPageAnimators(int page) {
@@ -339,8 +325,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
View view = mQs.getView();
// This case: less tiles to animate in small displays.
- if (count < mQuickQSPanelController.getTileLayout().getNumVisibleTiles()
- && mAllowFancy) {
+ if (count < mQuickQSPanelController.getTileLayout().getNumVisibleTiles()) {
// Quick tiles.
QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
if (quickTileView == null) continue;
@@ -422,7 +407,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
mAnimatedQsViews.add(tileView);
mAllViews.add(quickTileView);
mAllViews.add(quickTileView.getSecondaryLabel());
- } else if (mFullRows && isIconInAnimatedRow(count)) {
+ } else if (isIconInAnimatedRow(count)) {
firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
@@ -457,44 +442,42 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
}
}
- if (mAllowFancy) {
- animateBrightnessSlider(firstPageBuilder);
+ animateBrightnessSlider(firstPageBuilder);
- mFirstPageAnimator = firstPageBuilder
- // Fade in the tiles/labels as we reach the final position.
- .addFloat(tileLayout, "alpha", 0, 1)
- .addFloat(quadraticInterpolatorBuilder.build(), "position", 0, 1)
- .setListener(this)
- .build();
+ mFirstPageAnimator = firstPageBuilder
+ // Fade in the tiles/labels as we reach the final position.
+ .addFloat(tileLayout, "alpha", 0, 1)
+ .addFloat(quadraticInterpolatorBuilder.build(), "position", 0, 1)
+ .setListener(this)
+ .build();
- // Fade in the media player as we reach the final position
- Builder builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
- if (mQsPanelController.shouldUseHorizontalLayout()
- && mQsPanelController.mMediaHost.hostView != null) {
- builder.addFloat(mQsPanelController.mMediaHost.hostView, "alpha", 0, 1);
- } else {
- // In portrait, media view should always be visible
- mQsPanelController.mMediaHost.hostView.setAlpha(1.0f);
- }
- mAllPagesDelayedAnimator = builder.build();
- translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
- qqsTranslationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
- translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
- if (mOnFirstPage) {
- // Only recreate this animator if we're in the first page. That way we know that
- // the first page is attached and has the proper positions/measures.
- mQQSTranslationYAnimator = qqsTranslationYBuilder.build();
- }
- mTranslationYAnimator = translationYBuilder.build();
- mTranslationXAnimator = translationXBuilder.build();
- if (mQQSTileHeightAnimator != null) {
- mQQSTileHeightAnimator.setInterpolator(
- mQSExpansionPathInterpolator.getYInterpolator());
- }
- if (mOtherFirstPageTilesHeightAnimator != null) {
- mOtherFirstPageTilesHeightAnimator.setInterpolator(
- mQSExpansionPathInterpolator.getYInterpolator());
- }
+ // Fade in the media player as we reach the final position
+ Builder builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
+ if (mQsPanelController.shouldUseHorizontalLayout()
+ && mQsPanelController.mMediaHost.hostView != null) {
+ builder.addFloat(mQsPanelController.mMediaHost.hostView, "alpha", 0, 1);
+ } else {
+ // In portrait, media view should always be visible
+ mQsPanelController.mMediaHost.hostView.setAlpha(1.0f);
+ }
+ mAllPagesDelayedAnimator = builder.build();
+ translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
+ qqsTranslationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
+ translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
+ if (mOnFirstPage) {
+ // Only recreate this animator if we're in the first page. That way we know that
+ // the first page is attached and has the proper positions/measures.
+ mQQSTranslationYAnimator = qqsTranslationYBuilder.build();
+ }
+ mTranslationYAnimator = translationYBuilder.build();
+ mTranslationXAnimator = translationXBuilder.build();
+ if (mQQSTileHeightAnimator != null) {
+ mQQSTileHeightAnimator.setInterpolator(
+ mQSExpansionPathInterpolator.getYInterpolator());
+ }
+ if (mOtherFirstPageTilesHeightAnimator != null) {
+ mOtherFirstPageTilesHeightAnimator.setInterpolator(
+ mQSExpansionPathInterpolator.getYInterpolator());
}
mNonfirstPageAlphaAnimator = nonFirstPageAlphaBuilder
.addFloat(mQuickQsPanel, "alpha", 1, 0)
@@ -568,7 +551,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
if (animator == null) {
animator = new HeightExpansionAnimator(
- this, mLastQQSTileHeight, tileView.getMeasuredHeight());
+ this, mLastQQSTileHeight, tileView.getMeasuredHeight());
animator.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
}
animator.addView(tileView);
@@ -639,7 +622,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
}
private void getRelativePositionInt(int[] loc1, View view, View parent) {
- if(view == parent || view == null) return;
+ if (view == parent || view == null) return;
// Ignore tile pages as they can have some offset we don't want to take into account in
// RTL.
if (!isAPage(view)) {
@@ -672,7 +655,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
}
}
mLastPosition = position;
- if (!mAllowFancy) return;
if (mOnFirstPage) {
mQuickQsPanel.setAlpha(1);
mFirstPageAnimator.setPosition(position);
@@ -806,30 +788,31 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
- float mLastT = -1;
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- float t = valueAnimator.getAnimatedFraction();
- final int viewCount = mViews.size();
- int height = (Integer) valueAnimator.getAnimatedValue();
- for (int i = 0; i < viewCount; i++) {
- View v = mViews.get(i);
- if (v instanceof HeightOverrideable) {
- ((HeightOverrideable) v).setHeightOverride(height);
- } else {
- v.setBottom(v.getTop() + height);
+ float mLastT = -1;
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ float t = valueAnimator.getAnimatedFraction();
+ final int viewCount = mViews.size();
+ int height = (Integer) valueAnimator.getAnimatedValue();
+ for (int i = 0; i < viewCount; i++) {
+ View v = mViews.get(i);
+ if (v instanceof HeightOverrideable) {
+ ((HeightOverrideable) v).setHeightOverride(height);
+ } else {
+ v.setBottom(v.getTop() + height);
+ }
+ }
+ if (t == 0f) {
+ mListener.onAnimationAtStart();
+ } else if (t == 1f) {
+ mListener.onAnimationAtEnd();
+ } else if (mLastT <= 0 || mLastT == 1) {
+ mListener.onAnimationStarted();
+ }
+ mLastT = t;
}
- }
- if (t == 0f) {
- mListener.onAnimationAtStart();
- } else if (t == 1f) {
- mListener.onAnimationAtEnd();
- } else if (mLastT <= 0 || mLastT == 1) {
- mListener.onAnimationStarted();
- }
- mLastT = t;
- }
- };
+ };
HeightExpansionAnimator(TouchAnimator.Listener listener, int startHeight, int endHeight) {
mListener = listener;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index b20d7ba33397..67bf3003deff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -97,7 +97,8 @@ public class QSSecurityFooter extends ViewController<View>
super(rootView);
mFooterText = mView.findViewById(R.id.footer_text);
mPrimaryFooterIcon = mView.findViewById(R.id.primary_footer_icon);
- mFooterIcon = new Icon.Resource(R.drawable.ic_info_outline);
+ mFooterIcon = new Icon.Resource(
+ R.drawable.ic_info_outline, /* contentDescription= */ null);
mContext = rootView.getContext();
mSecurityController = securityController;
mMainHandler = mainHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
index f6322743eaa1..bd75c75faa00 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
@@ -75,6 +75,7 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.R;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
+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;
@@ -243,16 +244,17 @@ public class QSSecurityFooterUtils implements DialogInterface.OnClickListener {
isWorkProfileOn).toString();
Icon icon;
+ ContentDescription contentDescription = null;
if (isParentalControlsEnabled) {
- icon = new Icon.Loaded(securityModel.getDeviceAdminIcon());
+ icon = new Icon.Loaded(securityModel.getDeviceAdminIcon(), contentDescription);
} else if (vpnName != null || vpnNameWorkProfile != null) {
if (securityModel.isVpnBranded()) {
- icon = new Icon.Resource(R.drawable.stat_sys_branded_vpn);
+ icon = new Icon.Resource(R.drawable.stat_sys_branded_vpn, contentDescription);
} else {
- icon = new Icon.Resource(R.drawable.stat_sys_vpn_ic);
+ icon = new Icon.Resource(R.drawable.stat_sys_vpn_ic, contentDescription);
}
} else {
- icon = new Icon.Resource(R.drawable.ic_info_outline);
+ icon = new Icon.Resource(R.drawable.ic_info_outline, contentDescription);
}
return new SecurityButtonConfig(icon, text, isClickable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 8dd506ec8775..28ddead0bdd9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -31,7 +31,6 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R
-import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.people.ui.view.PeopleViewBinder.bind
@@ -233,10 +232,8 @@ object FooterActionsViewBinder {
val icon = model.icon
val iconView = button.icon
- val contentDescription = model.contentDescription
IconViewBinder.bind(icon, iconView)
- ContentDescriptionViewBinder.bind(contentDescription, iconView)
if (model.iconTint != null) {
iconView.setColorFilter(model.iconTint, PorterDuff.Mode.SRC_IN)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
index 4c0879e225c1..2ad0513c2ace 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.qs.footer.ui.viewmodel
import android.annotation.DrawableRes
import android.view.View
-import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
/**
@@ -29,7 +28,6 @@ data class FooterActionsButtonViewModel(
val icon: Icon,
val iconTint: Int?,
@DrawableRes val background: Int,
- val contentDescription: ContentDescription,
// TODO(b/230830644): Replace View by an Expandable interface that can expand in either dialog
// or activity.
val onClick: (View) -> Unit,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index b556a3e0d66b..a935338c2565 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -138,10 +138,12 @@ class FooterActionsViewModel(
/** The model for the settings button. */
val settings: FooterActionsButtonViewModel =
FooterActionsButtonViewModel(
- Icon.Resource(R.drawable.ic_settings),
+ Icon.Resource(
+ R.drawable.ic_settings,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+ ),
iconTint = null,
R.drawable.qs_footer_action_circle,
- ContentDescription.Resource(R.string.accessibility_quick_settings_settings),
this::onSettingsButtonClicked,
)
@@ -149,14 +151,16 @@ class FooterActionsViewModel(
val power: FooterActionsButtonViewModel? =
if (showPowerButton) {
FooterActionsButtonViewModel(
- Icon.Resource(android.R.drawable.ic_lock_power_off),
+ Icon.Resource(
+ android.R.drawable.ic_lock_power_off,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ ),
iconTint =
Utils.getColorAttrDefaultColor(
context,
com.android.internal.R.attr.textColorOnAccent,
),
R.drawable.qs_footer_action_circle_color,
- ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu),
this::onPowerButtonClicked,
)
} else {
@@ -252,10 +256,12 @@ class FooterActionsViewModel(
}
return FooterActionsButtonViewModel(
- Icon.Loaded(icon),
+ Icon.Loaded(
+ icon,
+ ContentDescription.Loaded(userSwitcherContentDescription(status.currentUserName)),
+ ),
iconTint,
R.drawable.qs_footer_action_circle,
- ContentDescription.Loaded(userSwitcherContentDescription(status.currentUserName)),
this::onUserSwitcherClicked,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 05b342072974..c65bd9b4348e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -31,7 +31,6 @@ import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE
import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -42,7 +41,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.statusbar.policy.KeyguardStateController
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
@@ -55,8 +53,7 @@ class DeviceControlsTile @Inject constructor(
statusBarStateController: StatusBarStateController,
activityStarter: ActivityStarter,
qsLogger: QSLogger,
- private val controlsComponent: ControlsComponent,
- private val keyguardStateController: KeyguardStateController
+ private val controlsComponent: ControlsComponent
) : QSTileImpl<QSTile.State>(
host,
backgroundLooper,
@@ -105,7 +102,8 @@ class DeviceControlsTile @Inject constructor(
}
val intent = Intent().apply {
- component = ComponentName(mContext, ControlsActivity::class.java)
+ component = ComponentName(mContext, controlsComponent.getControlsUiController().get()
+ .resolveActivity())
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(ControlsUiController.EXTRA_ANIMATE, true)
}
@@ -127,10 +125,15 @@ class DeviceControlsTile @Inject constructor(
state.icon = icon
if (controlsComponent.isEnabled() && hasControlsApps.get()) {
if (controlsComponent.getVisibility() == AVAILABLE) {
- val structure = controlsComponent
- .getControlsController().get().getPreferredStructure().structure
- state.state = Tile.STATE_ACTIVE
- state.secondaryLabel = if (structure == tileLabel) null else structure
+ val structureInfo = controlsComponent
+ .getControlsController().get().getPreferredStructure()
+ state.state = if (structureInfo.controls.isEmpty()) {
+ Tile.STATE_INACTIVE
+ } else {
+ Tile.STATE_ACTIVE
+ }
+ val label = structureInfo.structure
+ state.secondaryLabel = if (label == tileLabel) null else label
} else {
state.state = Tile.STATE_INACTIVE
state.secondaryLabel = mContext.getText(R.string.controls_tile_locked)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 30862b78c93f..3788ad98092f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -61,6 +61,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.PatternMatcher;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -174,7 +175,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
private boolean mBound;
private boolean mIsEnabled;
private int mCurrentBoundedUserId = -1;
- private float mNavBarButtonAlpha;
private boolean mInputFocusTransferStarted;
private float mInputFocusTransferStartY;
private long mInputFocusTransferStartMillis;
@@ -296,12 +296,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
- public void setNavBarButtonAlpha(float alpha, boolean animate) {
- verifyCallerAndClearCallingIdentityPostMain("setNavBarButtonAlpha", () ->
- notifyNavBarButtonAlphaChanged(alpha, animate));
- }
-
- @Override
public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
verifyCallerAndClearCallingIdentityPostMain("onAssistantProgress", () ->
notifyAssistantProgress(progress));
@@ -581,6 +575,12 @@ public class OverviewProxyService extends CurrentUserTracker implements
AssistUtils assistUtils,
DumpManager dumpManager) {
super(broadcastDispatcher);
+
+ // b/241601880: This component shouldn't be running for a non-primary user
+ if (!Process.myUserHandle().equals(UserHandle.SYSTEM)) {
+ Log.e(TAG_OPS, "Unexpected initialization for non-primary user", new Throwable());
+ }
+
mContext = context;
mPipOptional = pipOptional;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
@@ -603,9 +603,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
mBackAnimation = backAnimation;
mUiEventLogger = uiEventLogger;
- // Assumes device always starts with back button until launcher tells it that it does not
- mNavBarButtonAlpha = 1.0f;
-
dumpManager.registerDumpable(getClass().getSimpleName(), this);
// Listen for nav bar mode changes
@@ -807,7 +804,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
mConnectionCallbacks.add(listener);
}
listener.onConnectionChanged(mOverviewProxy != null);
- listener.onNavBarButtonAlphaChanged(mNavBarButtonAlpha, false);
}
@Override
@@ -837,17 +833,10 @@ public class OverviewProxyService extends CurrentUserTracker implements
if (mOverviewProxy != null) {
mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
mOverviewProxy = null;
- notifyNavBarButtonAlphaChanged(1f, false /* animate */);
notifyConnectionChanged();
}
}
- private void notifyNavBarButtonAlphaChanged(float alpha, boolean animate) {
- for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
- mConnectionCallbacks.get(i).onNavBarButtonAlphaChanged(alpha, animate);
- }
- }
-
private void notifyHomeRotationEnabled(boolean enabled) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onHomeRotationEnabled(enabled);
@@ -1076,7 +1065,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
pw.print(" mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis);
pw.print(" mWindowCornerRadius="); pw.println(mWindowCornerRadius);
pw.print(" mSupportsRoundedCornersOnWindows="); pw.println(mSupportsRoundedCornersOnWindows);
- pw.print(" mNavBarButtonAlpha="); pw.println(mNavBarButtonAlpha);
pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
pw.print(" mNavBarMode="); pw.println(mNavBarMode);
mSysUiState.dump(pw, args);
@@ -1091,8 +1079,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
default void onQuickScrubStarted() {}
/** Notify the recents app (overview) is started by 3-button navigation. */
default void onToggleRecentApps() {}
- /** Notify changes in the nav bar button alpha */
- default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {}
default void onHomeRotationEnabled(boolean enabled) {}
default void onTaskbarStatusUpdated(boolean visible, boolean stashed) {}
default void onTaskbarAutohideSuspend(boolean suspend) {}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 91c6a9c892e7..c3b265fec36e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -117,7 +117,6 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.camera.CameraGestureHelper;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
@@ -140,7 +139,6 @@ import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -217,7 +215,6 @@ import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.Utils;
import com.android.systemui.util.time.SystemClock;
-import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.android.wm.shell.animation.FlingAnimationUtils;
import java.io.PrintWriter;
@@ -325,9 +322,6 @@ public final class NotificationPanelViewController extends PanelViewController {
private final FragmentService mFragmentService;
private final ScrimController mScrimController;
private final PrivacyDotViewController mPrivacyDotViewController;
- private final QuickAccessWalletController mQuickAccessWalletController;
- private final QRCodeScannerController mQRCodeScannerController;
- private final ControlsComponent mControlsComponent;
private final NotificationRemoteInputManager mRemoteInputManager;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@@ -696,8 +690,8 @@ public final class NotificationPanelViewController extends PanelViewController {
};
private final CameraGestureHelper mCameraGestureHelper;
- private final Provider<KeyguardBottomAreaViewModel> mKeyguardBottomAreaViewModelProvider;
- private final Provider<KeyguardBottomAreaInteractor> mKeyguardBottomAreaInteractorProvider;
+ private final KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
+ private final KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
@Inject
public NotificationPanelViewController(NotificationPanelView view,
@@ -746,8 +740,6 @@ public final class NotificationPanelViewController extends PanelViewController {
NavigationModeController navigationModeController,
FragmentService fragmentService,
ContentResolver contentResolver,
- QuickAccessWalletController quickAccessWalletController,
- QRCodeScannerController qrCodeScannerController,
RecordingController recordingController,
LargeScreenShadeHeaderController largeScreenShadeHeaderController,
ScreenOffAnimationController screenOffAnimationController,
@@ -755,7 +747,6 @@ public final class NotificationPanelViewController extends PanelViewController {
PanelExpansionStateManager panelExpansionStateManager,
NotificationRemoteInputManager remoteInputManager,
Optional<SysUIUnfoldComponent> unfoldComponent,
- ControlsComponent controlsComponent,
InteractionJankMonitor interactionJankMonitor,
QsFrameTranslateController qsFrameTranslateController,
SysUiState sysUiState,
@@ -768,8 +759,8 @@ public final class NotificationPanelViewController extends PanelViewController {
ShadeTransitionController shadeTransitionController,
SystemClock systemClock,
CameraGestureHelper cameraGestureHelper,
- Provider<KeyguardBottomAreaViewModel> keyguardBottomAreaViewModelProvider,
- Provider<KeyguardBottomAreaInteractor> keyguardBottomAreaInteractorProvider) {
+ KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
+ KeyguardBottomAreaInteractor keyguardBottomAreaInteractor) {
super(view,
falsingManager,
dozeLog,
@@ -791,9 +782,6 @@ public final class NotificationPanelViewController extends PanelViewController {
mVibratorHelper = vibratorHelper;
mKeyguardMediaController = keyguardMediaController;
mPrivacyDotViewController = privacyDotViewController;
- mQuickAccessWalletController = quickAccessWalletController;
- mQRCodeScannerController = qrCodeScannerController;
- mControlsComponent = controlsComponent;
mMetricsLogger = metricsLogger;
mConfigurationController = configurationController;
mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
@@ -897,7 +885,7 @@ public final class NotificationPanelViewController extends PanelViewController {
mQsFrameTranslateController = qsFrameTranslateController;
updateUserSwitcherFlags();
- mKeyguardBottomAreaViewModelProvider = keyguardBottomAreaViewModelProvider;
+ mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel;
onFinishInflate();
keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
@@ -951,7 +939,7 @@ public final class NotificationPanelViewController extends PanelViewController {
}
});
mCameraGestureHelper = cameraGestureHelper;
- mKeyguardBottomAreaInteractorProvider = keyguardBottomAreaInteractorProvider;
+ mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor;
}
@VisibleForTesting
@@ -1276,17 +1264,7 @@ public final class NotificationPanelViewController extends PanelViewController {
}
private void initBottomArea() {
- if (mFeatureFlags.isEnabled(Flags.MODERN_BOTTOM_AREA)) {
- mKeyguardBottomArea.init(mKeyguardBottomAreaViewModelProvider.get(), mFalsingManager);
- } else {
- // TODO(b/235403546): remove this method call when the new implementation is complete
- // and these are not needed.
- mKeyguardBottomArea.init(
- mFalsingManager,
- mQuickAccessWalletController,
- mControlsComponent,
- mQRCodeScannerController);
- }
+ mKeyguardBottomArea.init(mKeyguardBottomAreaViewModel, mFalsingManager);
}
@VisibleForTesting
@@ -1403,7 +1381,6 @@ public final class NotificationPanelViewController extends PanelViewController {
}
mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
- mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
mStackScrollerMeasuringPass++;
requestScrollerTopPaddingUpdate(animate);
@@ -1453,7 +1430,7 @@ public final class NotificationPanelViewController extends PanelViewController {
mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
- mKeyguardBottomAreaInteractorProvider.get().setClockPosition(
+ mKeyguardBottomAreaInteractor.setClockPosition(
mClockPositionResult.clockX, mClockPositionResult.clockY);
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
@@ -3296,8 +3273,7 @@ public final class NotificationPanelViewController extends PanelViewController {
getExpandedFraction());
float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
alpha *= mBottomAreaShadeAlpha;
- mKeyguardBottomArea.setComponentAlphas(alpha);
- mKeyguardBottomAreaInteractorProvider.get().setAlpha(alpha);
+ mKeyguardBottomAreaInteractor.setAlpha(alpha);
mLockIconViewController.setAlpha(alpha);
}
@@ -3496,8 +3472,7 @@ public final class NotificationPanelViewController extends PanelViewController {
}
private void updateDozingVisibilities(boolean animate) {
- mKeyguardBottomArea.setDozing(mDozing, animate);
- mKeyguardBottomAreaInteractorProvider.get().setAnimateDozingTransitions(animate);
+ mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
if (!mDozing && animate) {
mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
}
@@ -3799,8 +3774,7 @@ public final class NotificationPanelViewController extends PanelViewController {
mView.setDozing(dozing);
mDozing = dozing;
mNotificationStackScrollLayoutController.setDozing(mDozing, animate);
- mKeyguardBottomArea.setDozing(mDozing, animate);
- mKeyguardBottomAreaInteractorProvider.get().setAnimateDozingTransitions(animate);
+ mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
mKeyguardStatusBarViewController.setDozing(mDozing);
if (dozing) {
@@ -3849,7 +3823,6 @@ public final class NotificationPanelViewController extends PanelViewController {
public void dozeTimeTick() {
mLockIconViewController.dozeTimeTick();
- mKeyguardBottomArea.dozeTimeTick();
mKeyguardStatusViewController.dozeTimeTick();
if (mInterpolatedDarkAmount > 0) {
positionClockAndNotifications();
@@ -4672,7 +4645,6 @@ public final class NotificationPanelViewController extends PanelViewController {
public void onDozeAmountChanged(float linearAmount, float amount) {
mInterpolatedDarkAmount = amount;
mLinearDarkAmount = linearAmount;
- mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
positionClockAndNotifications();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index e93f60596280..abafecce2965 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -29,6 +29,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.LockIconViewController;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
@@ -138,6 +139,13 @@ public class NotificationShadeWindowViewController {
return mView.findViewById(R.id.keyguard_bouncer_container);
}
+ /**
+ * @return Location where to place the KeyguardMessageArea
+ */
+ public AuthKeyguardMessageArea getKeyguardMessageArea() {
+ return mView.findViewById(R.id.keyguard_message_area);
+ }
+
/** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
public void setupExpandedStatusBar() {
mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
index 876a1ffa3ecb..a00769374938 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
@@ -397,7 +397,7 @@ public abstract class PanelViewController {
mInitialOffsetOnTouch = expandedHeight;
mInitialExpandY = newY;
mInitialExpandX = newX;
- mInitialTouchFromKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+ mInitialTouchFromKeyguard = mKeyguardStateController.isShowing();
if (startTracking) {
mTouchSlopExceeded = true;
setExpandedHeight(mInitialOffsetOnTouch);
@@ -416,9 +416,7 @@ public abstract class PanelViewController {
float vectorVel = (float) Math.hypot(
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
- final boolean onKeyguard =
- mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
-
+ final boolean onKeyguard = mKeyguardStateController.isShowing();
final boolean expand;
if (mKeyguardStateController.isKeyguardFadingAway()
|| (mInitialTouchFromKeyguard && !onKeyguard)) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
index 621a6090f4a0..9b3fe92f9225 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
@@ -26,6 +26,7 @@ import com.android.systemui.Dumpable
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.tuner.TunerService
@@ -49,6 +50,7 @@ class PulsingGestureListener @Inject constructor(
private val dockManager: DockManager,
private val centralSurfaces: CentralSurfaces,
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
+ private val statusBarStateController: StatusBarStateController,
tunerService: TunerService,
dumpManager: DumpManager
) : GestureDetector.SimpleOnGestureListener(), Dumpable {
@@ -74,7 +76,8 @@ class PulsingGestureListener @Inject constructor(
}
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
- if (singleTapEnabled &&
+ if (statusBarStateController.isPulsing &&
+ singleTapEnabled &&
!dockManager.isDocked &&
!falsingManager.isProximityNear &&
!falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)
@@ -89,7 +92,8 @@ class PulsingGestureListener @Inject constructor(
}
override fun onDoubleTap(e: MotionEvent): Boolean {
- if ((doubleTapEnabled || singleTapEnabled) &&
+ if (statusBarStateController.isPulsing &&
+ (doubleTapEnabled || singleTapEnabled) &&
!falsingManager.isProximityNear &&
!falsingManager.isFalseDoubleTap
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 47dc5c2a5513..408c61f8f387 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,6 +19,10 @@ package com.android.systemui.statusbar;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_FIRST_FRAME_RECEIVED;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_GOOD;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_START;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
import static android.hardware.biometrics.BiometricSourceType.FACE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -78,6 +82,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
+import com.android.systemui.biometrics.BiometricMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -178,7 +183,7 @@ public class KeyguardIndicationController {
private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
private String mBiometricErrorMessageToShowOnScreenOn;
- private final Set<Integer> mCoExFaceHelpMsgIdsToShow;
+ private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
private boolean mInited;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -249,11 +254,11 @@ public class KeyguardIndicationController {
mScreenLifecycle = screenLifecycle;
mScreenLifecycle.addObserver(mScreenObserver);
- mCoExFaceHelpMsgIdsToShow = new HashSet<>();
+ mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>();
int[] msgIds = context.getResources().getIntArray(
com.android.systemui.R.array.config_face_help_msgs_when_fingerprint_enrolled);
for (int msgId : msgIds) {
- mCoExFaceHelpMsgIdsToShow.add(msgId);
+ mCoExFaceAcquisitionMsgIdsToShow.add(msgId);
}
mHandler = new Handler(mainLooper) {
@@ -687,11 +692,11 @@ public class KeyguardIndicationController {
/**
* Returns the indication text indicating that trust has been granted.
*
- * @return {@code null} or an empty string if a trust indication text should not be shown.
+ * @return an empty string if a trust indication text should not be shown.
*/
@VisibleForTesting
String getTrustGrantedIndication() {
- return TextUtils.isEmpty(mTrustGrantedIndication)
+ return mTrustGrantedIndication == null
? mContext.getString(R.string.keyguard_indication_trust_unlocked)
: mTrustGrantedIndication.toString();
}
@@ -927,7 +932,7 @@ public class KeyguardIndicationController {
return; // udfps affordance is highlighted, no need to show action to unlock
} else if (mKeyguardUpdateMonitor.isFaceEnrolled()) {
String message = mContext.getString(R.string.keyguard_retry);
- mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState);
}
} else {
final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(
@@ -990,7 +995,7 @@ public class KeyguardIndicationController {
mTopIndicationView == null ? null : mTopIndicationView.getText()));
pw.println(" computePowerIndication(): " + computePowerIndication());
pw.println(" trustGrantedIndication: " + getTrustGrantedIndication());
- pw.println(" mCoExFaceHelpMsgIdsToShow=" + mCoExFaceHelpMsgIdsToShow);
+ pw.println(" mCoExFaceHelpMsgIdsToShow=" + mCoExFaceAcquisitionMsgIdsToShow);
mRotateTextViewController.dump(pw, args);
}
@@ -1048,6 +1053,17 @@ public class KeyguardIndicationController {
return;
}
+ if (biometricSourceType == FACE) {
+ if (msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED) {
+ mFaceAcquiredMessageDeferral.reset();
+ } else {
+ mFaceAcquiredMessageDeferral.processMessage(msgId, helpString);
+ if (mFaceAcquiredMessageDeferral.shouldDefer(msgId)) {
+ return;
+ }
+ }
+ }
+
final boolean faceAuthSoftError = biometricSourceType == FACE
&& msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
final boolean faceAuthFailed = biometricSourceType == FACE
@@ -1055,19 +1071,24 @@ public class KeyguardIndicationController {
final boolean isUnlockWithFingerprintPossible =
mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
getCurrentUser());
- if (faceAuthSoftError
- && isUnlockWithFingerprintPossible
- && !mCoExFaceHelpMsgIdsToShow.contains(msgId)) {
+ final boolean isCoExFaceAcquisitionMessage =
+ faceAuthSoftError && isUnlockWithFingerprintPossible;
+ if (isCoExFaceAcquisitionMessage && !mCoExFaceAcquisitionMsgIdsToShow.contains(msgId)) {
if (DEBUG) {
Log.d(TAG, "skip showing msgId=" + msgId + " helpString=" + helpString
+ ", due to co-ex logic");
}
return;
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
+ mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
mInitialTextColorState);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
- if (faceAuthFailed && isUnlockWithFingerprintPossible) {
+ if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
+ showBiometricMessage(
+ helpString,
+ mContext.getString(R.string.keyguard_suggest_fingerprint)
+ );
+ } else if (faceAuthFailed && isUnlockWithFingerprintPossible) {
showBiometricMessage(
mContext.getString(R.string.keyguard_face_failed),
mContext.getString(R.string.keyguard_suggest_fingerprint)
@@ -1090,41 +1111,51 @@ public class KeyguardIndicationController {
@Override
public void onBiometricError(int msgId, String errString,
BiometricSourceType biometricSourceType) {
- if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) {
- return;
+ CharSequence deferredFaceMessage = null;
+ if (biometricSourceType == FACE) {
+ deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
+ mFaceAcquiredMessageDeferral.reset();
}
- if (biometricSourceType == FACE
- && msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS) {
- // suppress all face UNABLE_TO_PROCESS errors
+ if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) {
if (DEBUG) {
- Log.d(TAG, "skip showing FACE_ERROR_UNABLE_TO_PROCESS errString="
- + errString);
+ Log.d(TAG, "suppressingBiometricError msgId=" + msgId
+ + " source=" + biometricSourceType);
}
- } else if (biometricSourceType == FACE
- && msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+ } else if (biometricSourceType == FACE && msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+ // Co-ex: show deferred message OR nothing
if (mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- getCurrentUser())) {
- // no message if fingerprint is also enrolled
+ KeyguardUpdateMonitor.getCurrentUser())) {
+ // if we're on the lock screen (bouncer isn't showing), show the deferred msg
+ if (deferredFaceMessage != null
+ && !mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ showBiometricMessage(
+ deferredFaceMessage,
+ mContext.getString(R.string.keyguard_suggest_fingerprint)
+ );
+ return;
+ }
+
+ // otherwise, don't show any message
if (DEBUG) {
Log.d(TAG, "skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
}
return;
}
- // The face timeout message is not very actionable, let's ask the user to
+ // Face-only: The face timeout message is not very actionable, let's ask the user to
// manually retry.
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
- mStatusBarKeyguardViewManager.showBouncerMessage(
- mContext.getResources().getString(R.string.keyguard_try_fingerprint),
- mInitialTextColorState
+ if (deferredFaceMessage != null) {
+ showBiometricMessage(
+ deferredFaceMessage,
+ mContext.getString(R.string.keyguard_unlock)
);
} else {
// suggest swiping up to unlock (try face auth again or swipe up to bouncer)
showActionToUnlock();
}
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
showBiometricMessage(errString);
} else {
@@ -1134,8 +1165,9 @@ public class KeyguardIndicationController {
private boolean shouldSuppressBiometricError(int msgId,
BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
- if (biometricSourceType == BiometricSourceType.FINGERPRINT)
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
return shouldSuppressFingerprintError(msgId, updateMonitor);
+ }
if (biometricSourceType == FACE) {
return shouldSuppressFaceError(msgId, updateMonitor);
}
@@ -1161,7 +1193,8 @@ public class KeyguardIndicationController {
// check of whether non-strong biometric is allowed
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
&& msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
- || msgId == FaceManager.FACE_ERROR_CANCELED);
+ || msgId == FaceManager.FACE_ERROR_CANCELED
+ || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS);
}
@@ -1200,10 +1233,11 @@ public class KeyguardIndicationController {
boolean isStrongBiometric) {
super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric);
hideBiometricMessage();
-
- if (biometricSourceType == FACE
- && !mKeyguardBypassController.canBypass()) {
- showActionToUnlock();
+ if (biometricSourceType == FACE) {
+ mFaceAcquiredMessageDeferral.reset();
+ if (!mKeyguardBypassController.canBypass()) {
+ showActionToUnlock();
+ }
}
}
@@ -1274,4 +1308,14 @@ public class KeyguardIndicationController {
}
}
};
+
+ private final BiometricMessageDeferral mFaceAcquiredMessageDeferral =
+ new BiometricMessageDeferral(
+ Set.of(
+ FACE_ACQUIRED_GOOD,
+ FACE_ACQUIRED_START,
+ FACE_ACQUIRED_FIRST_FRAME_RECEIVED
+ ),
+ Set.of(FACE_ACQUIRED_TOO_DARK)
+ );
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 74d8f1beaec1..d3343dfdcd19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -2,7 +2,6 @@ package com.android.systemui.statusbar
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
-import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Configuration
@@ -880,15 +879,22 @@ class DragDownHelper(
child.actualHeight = (child.collapsedHeight + rubberband).toInt()
}
- private fun cancelChildExpansion(child: ExpandableView) {
+ @VisibleForTesting
+ fun cancelChildExpansion(
+ child: ExpandableView,
+ animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS
+ ) {
if (child.actualHeight == child.collapsedHeight) {
expandCallback.setUserLockedChild(child, false)
return
}
- val anim = ObjectAnimator.ofInt(child, "actualHeight",
- child.actualHeight, child.collapsedHeight)
+ val anim = ValueAnimator.ofInt(child.actualHeight, child.collapsedHeight)
anim.interpolator = Interpolators.FAST_OUT_SLOW_IN
- anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS
+ anim.duration = animationDuration
+ anim.addUpdateListener { animation: ValueAnimator ->
+ // don't use reflection, because the `actualHeight` field may be obfuscated
+ child.actualHeight = animation.animatedValue as Int
+ }
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
expandCallback.setUserLockedChild(child, false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index f6c4a316b211..cb13fcf246cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -81,7 +81,6 @@ class NotificationShadeDepthController @Inject constructor(
}
lateinit var root: View
- private var blurRoot: View? = null
private var keyguardAnimator: Animator? = null
private var notificationAnimator: Animator? = null
private var updateScheduled: Boolean = false
@@ -235,7 +234,7 @@ class NotificationShadeDepthController @Inject constructor(
val opaque = scrimsVisible && !blursDisabledForAppLaunch
Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
- blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur, opaque)
+ blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
lastAppliedBlur = blur
wallpaperController.setNotificationShadeZoom(zoomOut)
listeners.forEach {
@@ -271,7 +270,6 @@ class NotificationShadeDepthController @Inject constructor(
override fun onAnimationEnd(animation: Animator?) {
keyguardAnimator = null
wakeAndUnlockBlurRadius = 0f
- scheduleUpdate()
}
})
start()
@@ -302,7 +300,6 @@ class NotificationShadeDepthController @Inject constructor(
override fun onDozeAmountChanged(linear: Float, eased: Float) {
wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased)
- scheduleUpdate()
}
}
@@ -439,12 +436,11 @@ class NotificationShadeDepthController @Inject constructor(
shadeAnimation.animateTo(blurUtils.blurRadiusOfRatio(targetBlurNormalized).toInt())
}
- private fun scheduleUpdate(viewToBlur: View? = null) {
+ private fun scheduleUpdate() {
if (updateScheduled) {
return
}
updateScheduled = true
- blurRoot = viewToBlur
choreographer.postFrameCallback(updateBlurCallback)
}
@@ -495,16 +491,11 @@ class NotificationShadeDepthController @Inject constructor(
*/
private var pendingRadius = -1
- /**
- * View on {@link Surface} that wants depth.
- */
- private var view: View? = null
-
private var springAnimation = SpringAnimation(this, object :
FloatPropertyCompat<DepthAnimation>("blurRadius") {
override fun setValue(rect: DepthAnimation?, value: Float) {
radius = value
- scheduleUpdate(view)
+ scheduleUpdate()
}
override fun getValue(rect: DepthAnimation?): Float {
@@ -519,11 +510,10 @@ class NotificationShadeDepthController @Inject constructor(
springAnimation.addEndListener { _, _, _, _ -> pendingRadius = -1 }
}
- fun animateTo(newRadius: Int, viewToBlur: View? = null) {
- if (pendingRadius == newRadius && view == viewToBlur) {
+ fun animateTo(newRadius: Int) {
+ if (pendingRadius == newRadius) {
return
}
- view = viewToBlur
pendingRadius = newRadius
springAnimation.animateToFinalPosition(newRadius.toFloat())
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index cea3debe6379..41c0367772b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -90,6 +92,12 @@ public class NotificationShelf extends ActivatableNotificationView implements
super(context, attrs);
}
+ @VisibleForTesting
+ public NotificationShelf(Context context, AttributeSet attrs, boolean showNotificationShelf) {
+ super(context, attrs);
+ mShowNotificationShelf = showNotificationShelf;
+ }
+
@Override
@VisibleForTesting
public void onFinishInflate() {
@@ -175,7 +183,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
if (ambientState.isExpansionChanging() && !ambientState.isOnKeyguard()) {
float expansion = ambientState.getExpansionFraction();
- viewState.alpha = ShadeInterpolation.getContentAlpha(expansion);
+ if (ambientState.isBouncerInTransit()) {
+ viewState.alpha = aboutToShowBouncerProgress(expansion);
+ } else {
+ viewState.alpha = ShadeInterpolation.getContentAlpha(expansion);
+ }
} else {
viewState.alpha = 1f - ambientState.getHideAmount();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 6302ef11cfe7..bbff0466cf50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
-import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Configuration
import android.os.PowerManager
@@ -28,6 +28,7 @@ import android.util.IndentingPrintWriter
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.ViewConfiguration
+import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
@@ -276,15 +277,22 @@ constructor(
}
}
- private fun reset(child: ExpandableView) {
+ @VisibleForTesting
+ fun reset(
+ child: ExpandableView,
+ animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
+ ) {
if (child.actualHeight == child.collapsedHeight) {
setUserLocked(child, false)
return
}
- val anim = ObjectAnimator.ofInt(child, "actualHeight",
- child.actualHeight, child.collapsedHeight)
+ val anim = ValueAnimator.ofInt(child.actualHeight, child.collapsedHeight)
anim.interpolator = Interpolators.FAST_OUT_SLOW_IN
- anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
+ anim.duration = animationDuration
+ anim.addUpdateListener { animation: ValueAnimator ->
+ // don't use reflection, because the `actualHeight` field may be obfuscated
+ child.actualHeight = animation.animatedValue as Int
+ }
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
setUserLocked(child, false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 3627b4084ec7..52f4c1cd054b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -144,11 +144,6 @@ public class BubbleCoordinator implements Coordinator {
public void invalidateNotifications(String reason) {
mNotifFilter.invalidateList(reason);
}
-
- @Override
- public void maybeCancelSummary(NotificationEntry entry) {
- // no-op
- }
};
private boolean isInterceptingDismissal(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ea28452d63c4..ce465bcfb42d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -23,6 +23,8 @@ import android.annotation.Nullable;
import android.content.Context;
import android.util.MathUtils;
+import androidx.annotation.VisibleForTesting;
+
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -137,7 +139,17 @@ public class AmbientState implements Dumpable {
* True right after we swipe up on lockscreen and have not finished the fling down that follows.
* False when we stop flinging or leave lockscreen.
*/
- private boolean mNeedFlingAfterLockscreenSwipeUp = false;
+ private boolean mIsFlingRequiredAfterLockScreenSwipeUp = false;
+
+ @VisibleForTesting
+ public boolean isFlingRequiredAfterLockScreenSwipeUp() {
+ return mIsFlingRequiredAfterLockScreenSwipeUp;
+ }
+
+ @VisibleForTesting
+ public void setFlingRequiredAfterLockScreenSwipeUp(boolean value) {
+ mIsFlingRequiredAfterLockScreenSwipeUp = value;
+ }
/**
* @return Height of the notifications panel without top padding when expansion completes.
@@ -181,7 +193,7 @@ public class AmbientState implements Dumpable {
public void setSwipingUp(boolean isSwipingUp) {
if (!isSwipingUp && mIsSwipingUp) {
// Just stopped swiping up.
- mNeedFlingAfterLockscreenSwipeUp = true;
+ mIsFlingRequiredAfterLockScreenSwipeUp = true;
}
mIsSwipingUp = isSwipingUp;
}
@@ -196,10 +208,10 @@ public class AmbientState implements Dumpable {
/**
* @param isFlinging Whether we are flinging the shade open or closed.
*/
- public void setIsFlinging(boolean isFlinging) {
+ public void setFlinging(boolean isFlinging) {
if (isOnKeyguard() && !isFlinging && mIsFlinging) {
// Just stopped flinging.
- mNeedFlingAfterLockscreenSwipeUp = false;
+ mIsFlingRequiredAfterLockScreenSwipeUp = false;
}
mIsFlinging = isFlinging;
}
@@ -508,7 +520,7 @@ public class AmbientState implements Dumpable {
public void setStatusBarState(int statusBarState) {
if (mStatusBarState != StatusBarState.KEYGUARD) {
- mNeedFlingAfterLockscreenSwipeUp = false;
+ mIsFlingRequiredAfterLockScreenSwipeUp = false;
}
mStatusBarState = statusBarState;
}
@@ -576,7 +588,7 @@ public class AmbientState implements Dumpable {
* @return Whether we need to do a fling down after swiping up on lockscreen.
*/
public boolean isFlingingAfterSwipeUpOnLockscreen() {
- return mIsFlinging && mNeedFlingAfterLockscreenSwipeUp;
+ return mIsFlinging && mIsFlingRequiredAfterLockScreenSwipeUp;
}
/**
@@ -744,7 +756,8 @@ public class AmbientState implements Dumpable {
pw.println("mIsSwipingUp=" + mIsSwipingUp);
pw.println("mPanelTracking=" + mPanelTracking);
pw.println("mIsFlinging=" + mIsFlinging);
- pw.println("mNeedFlingAfterLockscreenSwipeUp=" + mNeedFlingAfterLockscreenSwipeUp);
+ pw.println("mIsFlingRequiredAfterLockScreenSwipeUp="
+ + mIsFlingRequiredAfterLockScreenSwipeUp);
pw.println("mZDistanceBetweenElements=" + mZDistanceBetweenElements);
pw.println("mBaseZHeight=" + mBaseZHeight);
}
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 1fb265fea0b1..5fbaa515d5d7 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
@@ -2794,10 +2794,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (mDebugRemoveAnimation) {
Log.d(TAG, "generateRemove " + key
+ "\nmIsExpanded " + mIsExpanded
- + "\nmAnimationsEnabled " + mAnimationsEnabled
- + "\n!invisible group " + !isChildInInvisibleGroup(child));
+ + "\nmAnimationsEnabled " + mAnimationsEnabled);
}
- if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
+ if (mIsExpanded && mAnimationsEnabled) {
if (!mChildrenToAddAnimated.contains(child)) {
if (mDebugRemoveAnimation) {
Log.d(TAG, "needsAnimation = true " + key);
@@ -2845,26 +2844,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return hasAddEvent && mAddedHeadsUpChildren.contains(child);
}
- // TODO (b/162832756): remove since this won't happen in new pipeline (we prune groups in
- // ShadeListBuilder)
- /**
- * @param child the child to query
- * @return whether a view is not a top level child but a child notification and that group is
- * not expanded
- */
- @ShadeViewRefactor(RefactorComponent.ADAPTER)
- private boolean isChildInInvisibleGroup(View child) {
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- NotificationEntry groupSummary =
- mGroupMembershipManager.getGroupSummary(row.getEntry());
- if (groupSummary != null && groupSummary.getRow() != row) {
- return row.getVisibility() == View.INVISIBLE;
- }
- }
- return false;
- }
-
/**
* Updates the scroll position when a child was removed
*
@@ -5042,7 +5021,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setPanelFlinging(boolean flinging) {
- mAmbientState.setIsFlinging(flinging);
+ mAmbientState.setFlinging(flinging);
if (!flinging) {
// re-calculate the stack height which was frozen while flinging
updateStackPosition();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 353355bef7bb..eeed07014c11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -163,7 +163,7 @@ public class StackScrollAlgorithm {
// which is more expensive.
if (shelfState.hidden) {
// When the shelf is hidden, it won't clip views, so we don't hide rows
- return;
+ continue;
}
final float shelfTop = shelfState.yTranslation;
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 3259ae6e0a69..961b43347a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -40,6 +40,7 @@ import androidx.lifecycle.LifecycleOwner;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -220,6 +221,9 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
ViewGroup getBouncerContainer();
+ /** Get the Keyguard Message Area that displays auth messages. */
+ AuthKeyguardMessageArea getKeyguardMessageArea();
+
int getStatusBarHeight();
void updateQsExpansionEnabled();
@@ -390,8 +394,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void fadeKeyguardAfterLaunchTransition(Runnable beforeFading,
Runnable endRunnable, Runnable cancelRunnable);
- void fadeKeyguardWhilePulsing();
-
void animateKeyguardUnoccluding();
void startLaunchTransitionTimeout();
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 7f2b6c3df8c4..7463dac6b5d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -119,6 +119,7 @@ import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -1565,6 +1566,11 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
@Override
+ public AuthKeyguardMessageArea getKeyguardMessageArea() {
+ return mNotificationShadeWindowViewController.getKeyguardMessageArea();
+ }
+
+ @Override
public int getStatusBarHeight() {
return mStatusBarWindowController.getStatusBarHeight();
}
@@ -2999,19 +3005,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
/**
- * Fades the content of the Keyguard while we are dozing and makes it invisible when finished
- * fading.
- */
- @Override
- public void fadeKeyguardWhilePulsing() {
- mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
- ()-> {
- hideKeyguard();
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
- }).start();
- }
-
- /**
* Plays the animation when an activity that was occluding Keyguard goes away.
*/
@Override
@@ -3300,14 +3293,12 @@ public class CentralSurfacesImpl extends CoreStartable implements
// show the bouncer/lockscreen.
if (!mKeyguardViewMediator.isHiding()
&& !mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
- if (mState == StatusBarState.SHADE_LOCKED
- && mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
+ if (mState == StatusBarState.SHADE_LOCKED) {
// shade is showing while locked on the keyguard, so go back to showing the
// lock screen where users can use the UDFPS affordance to enter the device
mStatusBarKeyguardViewManager.reset(true);
- } else if ((mState == StatusBarState.KEYGUARD
- && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing())
- || mState == StatusBarState.SHADE_LOCKED) {
+ } else if (mState == StatusBarState.KEYGUARD
+ && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()) {
mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index ddff7d8e32af..24ce5e98bdd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -28,8 +28,6 @@ import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
-import androidx.annotation.Nullable;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.assist.AssistManager;
@@ -50,11 +48,9 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.unfold.FoldAodAnimationController;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.util.Assert;
import java.util.ArrayList;
-import java.util.Optional;
import javax.inject.Inject;
@@ -80,8 +76,6 @@ public final class DozeServiceHost implements DozeHost {
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final SysuiStatusBarStateController mStatusBarStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
- @Nullable
- private final FoldAodAnimationController mFoldAodAnimationController;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
private final BatteryController mBatteryController;
private final ScrimController mScrimController;
@@ -114,7 +108,6 @@ public final class DozeServiceHost implements DozeHost {
Lazy<AssistManager> assistManagerLazy,
DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor,
PulseExpansionHandler pulseExpansionHandler,
- Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
NotificationShadeWindowController notificationShadeWindowController,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
AuthController authController,
@@ -138,8 +131,6 @@ public final class DozeServiceHost implements DozeHost {
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
- mFoldAodAnimationController = sysUIUnfoldComponent
- .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
}
// TODO: we should try to not pass status bar in here if we can avoid it.
@@ -167,6 +158,7 @@ public final class DozeServiceHost implements DozeHost {
}
void firePowerSaveChanged(boolean active) {
+ Assert.isMainThread();
for (Callback callback : mCallbacks) {
callback.onPowerSaveChanged(active);
}
@@ -177,6 +169,7 @@ public final class DozeServiceHost implements DozeHost {
entry.setPulseSuppressed(true);
mNotificationIconAreaController.updateAodNotificationIcons();
};
+ Assert.isMainThread();
for (Callback callback : mCallbacks) {
callback.onNotificationAlerted(pulseSuppressedListener);
}
@@ -193,11 +186,13 @@ public final class DozeServiceHost implements DozeHost {
@Override
public void addCallback(@NonNull Callback callback) {
+ Assert.isMainThread();
mCallbacks.add(callback);
}
@Override
public void removeCallback(@NonNull Callback callback) {
+ Assert.isMainThread();
mCallbacks.remove(callback);
}
@@ -212,12 +207,10 @@ public final class DozeServiceHost implements DozeHost {
}
void updateDozing() {
- // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
- boolean
- dozing =
- mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD
- || mBiometricUnlockControllerLazy.get().getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ Assert.isMainThread();
+
+ boolean dozing =
+ mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
// When in wake-and-unlock we may not have received a change to StatusBarState
// but we still should not be dozing, manually set to false.
if (mBiometricUnlockControllerLazy.get().getMode()
@@ -225,10 +218,10 @@ public final class DozeServiceHost implements DozeHost {
dozing = false;
}
- mStatusBarStateController.setIsDozing(dozing);
- if (mFoldAodAnimationController != null) {
- mFoldAodAnimationController.setIsDozing(dozing);
+ for (Callback callback : mCallbacks) {
+ callback.onDozingChanged(dozing);
}
+ mStatusBarStateController.setIsDozing(dozing);
}
@Override
@@ -452,6 +445,7 @@ public final class DozeServiceHost implements DozeHost {
return;
}
mAlwaysOnSuppressed = suppressed;
+ Assert.isMainThread();
for (Callback callback : mCallbacks) {
callback.onAlwaysOnSuppressedChanged(suppressed);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
deleted file mode 100644
index cc451d9ef0cd..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ /dev/null
@@ -1,682 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
-import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE;
-
-import android.app.ActivityTaskManager;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.service.quickaccesswallet.GetWalletCardsError;
-import android.service.quickaccesswallet.GetWalletCardsResponse;
-import android.service.quickaccesswallet.QuickAccessWalletClient;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewPropertyAnimator;
-import android.view.WindowInsets;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.Interpolators;
-import com.android.systemui.controls.dagger.ControlsComponent;
-import com.android.systemui.controls.management.ControlsListingController;
-import com.android.systemui.controls.ui.ControlsActivity;
-import com.android.systemui.controls.ui.ControlsUiController;
-import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.wallet.controller.QuickAccessWalletController;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
- * text.
- */
-public class KeyguardBottomAreaView extends FrameLayout {
-
- private static final String TAG = "CentralSurfaces/KeyguardBottomAreaView";
- private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
-
- private ImageView mWalletButton;
- private ImageView mQRCodeScannerButton;
- private ImageView mControlsButton;
- private boolean mHasCard = false;
- private final WalletCardRetriever mCardRetriever = new WalletCardRetriever();
- private QuickAccessWalletController mQuickAccessWalletController;
- private QRCodeScannerController mQRCodeScannerController;
- private ControlsComponent mControlsComponent;
- private boolean mControlServicesAvailable = false;
-
- @Nullable private View mAmbientIndicationArea;
- private ViewGroup mIndicationArea;
- private TextView mIndicationText;
- private TextView mIndicationTextBottom;
- private ViewGroup mOverlayContainer;
-
- private ActivityStarter mActivityStarter;
- private KeyguardStateController mKeyguardStateController;
- private FalsingManager mFalsingManager;
-
- private boolean mDozing;
- private int mIndicationBottomMargin;
- private int mIndicationPadding;
- private float mDarkAmount;
- private int mBurnInXOffset;
- private int mBurnInYOffset;
-
- private final ControlsListingController.ControlsListingCallback mListingCallback =
- serviceInfos -> post(() -> {
- boolean available = !serviceInfos.isEmpty();
-
- if (available != mControlServicesAvailable) {
- mControlServicesAvailable = available;
- updateControlsVisibility();
- updateAffordanceColors();
- }
- });
-
- private final KeyguardStateController.Callback mKeyguardStateCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- if (mKeyguardStateController.isShowing()) {
- if (mQuickAccessWalletController != null) {
- mQuickAccessWalletController.queryWalletCards(mCardRetriever);
- }
- }
- }
- };
-
- @Nullable private KeyguardBottomAreaViewBinder.Binding mBinding;
- private boolean mUsesBinder;
-
- public KeyguardBottomAreaView(Context context) {
- this(context, null);
- }
-
- public KeyguardBottomAreaView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- /**
- * Initializes the view.
- */
- public void init(
- final KeyguardBottomAreaViewModel viewModel,
- final FalsingManager falsingManager) {
- Log.i(TAG, System.identityHashCode(this) + " initialized with a binder");
- mUsesBinder = true;
- mBinding = KeyguardBottomAreaViewBinder.bind(this, viewModel, falsingManager);
- }
-
- /**
- * Initializes the {@link KeyguardBottomAreaView} with the given dependencies
- *
- * @deprecated Use
- * {@link #init(KeyguardBottomAreaViewModel, FalsingManager)} instead
- */
- @Deprecated
- public void init(
- FalsingManager falsingManager,
- QuickAccessWalletController controller,
- ControlsComponent controlsComponent,
- QRCodeScannerController qrCodeScannerController) {
- if (mUsesBinder) {
- return;
- }
-
- Log.i(TAG, "initialized without a binder");
- mFalsingManager = falsingManager;
-
- mQuickAccessWalletController = controller;
- mQuickAccessWalletController.setupWalletChangeObservers(
- mCardRetriever, WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE);
- mQuickAccessWalletController.updateWalletPreference();
- mQuickAccessWalletController.queryWalletCards(mCardRetriever);
- updateWalletVisibility();
-
- mControlsComponent = controlsComponent;
- mControlsComponent.getControlsListingController().ifPresent(
- c -> c.addCallback(mListingCallback));
-
- mQRCodeScannerController = qrCodeScannerController;
- mQRCodeScannerController.registerQRCodeScannerChangeObservers(
- QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
- QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE);
- updateQRCodeButtonVisibility();
-
- updateAffordanceColors();
- }
-
- /**
- * Initializes this instance of {@link KeyguardBottomAreaView} based on the given instance of
- * another {@link KeyguardBottomAreaView}
- */
- public void initFrom(KeyguardBottomAreaView oldBottomArea) {
- if (mUsesBinder) {
- return;
- }
-
- // if it exists, continue to use the original ambient indication container
- // instead of the newly inflated one
- if (mAmbientIndicationArea != null) {
- // remove old ambient indication from its parent
- View originalAmbientIndicationView =
- oldBottomArea.findViewById(R.id.ambient_indication_container);
- ((ViewGroup) originalAmbientIndicationView.getParent())
- .removeView(originalAmbientIndicationView);
-
- // remove current ambient indication from its parent (discard)
- ViewGroup ambientIndicationParent = (ViewGroup) mAmbientIndicationArea.getParent();
- int ambientIndicationIndex =
- ambientIndicationParent.indexOfChild(mAmbientIndicationArea);
- ambientIndicationParent.removeView(mAmbientIndicationArea);
-
- // add the old ambient indication to this view
- ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex);
- mAmbientIndicationArea = originalAmbientIndicationView;
-
- // update burn-in offsets
- dozeTimeTick();
- }
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- if (mUsesBinder) {
- return;
- }
-
- mOverlayContainer = findViewById(R.id.overlay_container);
- mWalletButton = findViewById(R.id.wallet_button);
- mQRCodeScannerButton = findViewById(R.id.qr_code_scanner_button);
- mControlsButton = findViewById(R.id.controls_button);
- mIndicationArea = findViewById(R.id.keyguard_indication_area);
- mAmbientIndicationArea = findViewById(R.id.ambient_indication_container);
- mIndicationText = findViewById(R.id.keyguard_indication_text);
- mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom);
- mIndicationBottomMargin = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_margin_bottom);
- mBurnInYOffset = getResources().getDimensionPixelSize(
- R.dimen.default_burn_in_prevention_offset);
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mKeyguardStateController.addCallback(mKeyguardStateCallback);
- setClipChildren(false);
- setClipToPadding(false);
- mActivityStarter = Dependency.get(ActivityStarter.class);
-
- mIndicationPadding = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_area_padding);
- updateWalletVisibility();
- updateQRCodeButtonVisibility();
- updateControlsVisibility();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mUsesBinder) {
- return;
- }
-
- final IntentFilter filter = new IntentFilter();
- filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- mKeyguardStateController.addCallback(mKeyguardStateCallback);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mUsesBinder) {
- return;
- }
-
- mKeyguardStateController.removeCallback(mKeyguardStateCallback);
-
- if (mQuickAccessWalletController != null) {
- mQuickAccessWalletController.unregisterWalletChangeObservers(
- WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE);
- }
-
- if (mQRCodeScannerController != null) {
- mQRCodeScannerController.unregisterQRCodeScannerChangeObservers(
- QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
- QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE);
- }
-
- if (mControlsComponent != null) {
- mControlsComponent.getControlsListingController().ifPresent(
- c -> c.removeCallback(mListingCallback));
- }
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (mUsesBinder) {
- if (mBinding != null) {
- mBinding.onConfigurationChanged();
- }
- return;
- }
-
- mIndicationBottomMargin = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_margin_bottom);
- mBurnInYOffset = getResources().getDimensionPixelSize(
- R.dimen.default_burn_in_prevention_offset);
- MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams();
- if (mlp.bottomMargin != mIndicationBottomMargin) {
- mlp.bottomMargin = mIndicationBottomMargin;
- mIndicationArea.setLayoutParams(mlp);
- }
-
- // Respect font size setting.
- mIndicationTextBottom.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.text_size_small_material));
- mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.text_size_small_material));
-
- ViewGroup.LayoutParams lp = mWalletButton.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width);
- lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height);
- mWalletButton.setLayoutParams(lp);
-
- lp = mQRCodeScannerButton.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width);
- lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height);
- mQRCodeScannerButton.setLayoutParams(lp);
-
- lp = mControlsButton.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width);
- lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height);
- mControlsButton.setLayoutParams(lp);
-
- mIndicationPadding = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_area_padding);
-
- updateWalletVisibility();
- updateQRCodeButtonVisibility();
- updateAffordanceColors();
- }
-
- private void updateWalletVisibility() {
- if (mUsesBinder) {
- return;
- }
-
- if (mDozing
- || mQuickAccessWalletController == null
- || !mQuickAccessWalletController.isWalletEnabled()
- || !mHasCard) {
- mWalletButton.setVisibility(GONE);
-
- if (mControlsButton.getVisibility() == GONE) {
- mIndicationArea.setPadding(0, 0, 0, 0);
- }
- } else {
- mWalletButton.setVisibility(VISIBLE);
- mWalletButton.setOnClickListener(this::onWalletClick);
- mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0);
- }
- }
-
- private void updateControlsVisibility() {
- if (mUsesBinder) {
- return;
- }
-
- if (mControlsComponent == null) return;
-
- mControlsButton.setImageResource(mControlsComponent.getTileImageId());
- mControlsButton.setContentDescription(getContext()
- .getString(mControlsComponent.getTileTitleId()));
- updateAffordanceColors();
-
- boolean hasFavorites = mControlsComponent.getControlsController()
- .map(c -> c.getFavorites().size() > 0)
- .orElse(false);
- if (mDozing
- || !hasFavorites
- || !mControlServicesAvailable
- || mControlsComponent.getVisibility() != AVAILABLE) {
- mControlsButton.setVisibility(GONE);
- if (mWalletButton.getVisibility() == GONE) {
- mIndicationArea.setPadding(0, 0, 0, 0);
- }
- } else {
- mControlsButton.setVisibility(VISIBLE);
- mControlsButton.setOnClickListener(this::onControlsClick);
- mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0);
- }
- }
-
- public void setDarkAmount(float darkAmount) {
- if (mUsesBinder) {
- return;
- }
-
- if (darkAmount == mDarkAmount) {
- return;
- }
- mDarkAmount = darkAmount;
- dozeTimeTick();
- }
-
- /**
- * Returns a list of animators to use to animate the indication areas.
- */
- public List<ViewPropertyAnimator> getIndicationAreaAnimators() {
- if (mUsesBinder) {
- return checkNotNull(mBinding).getIndicationAreaAnimators();
- }
-
- List<ViewPropertyAnimator> animators =
- new ArrayList<>(mAmbientIndicationArea != null ? 2 : 1);
- animators.add(mIndicationArea.animate());
- if (mAmbientIndicationArea != null) {
- animators.add(mAmbientIndicationArea.animate());
- }
- return animators;
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
- private void startFinishDozeAnimation() {
- long delay = 0;
- if (mWalletButton.getVisibility() == View.VISIBLE) {
- startFinishDozeAnimationElement(mWalletButton, delay);
- }
- if (mQRCodeScannerButton.getVisibility() == View.VISIBLE) {
- startFinishDozeAnimationElement(mQRCodeScannerButton, delay);
- }
- if (mControlsButton.getVisibility() == View.VISIBLE) {
- startFinishDozeAnimationElement(mControlsButton, delay);
- }
- }
-
- private void startFinishDozeAnimationElement(View element, long delay) {
- element.setAlpha(0f);
- element.setTranslationY(element.getHeight() / 2);
- element.animate()
- .alpha(1f)
- .translationY(0f)
- .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
- .setStartDelay(delay)
- .setDuration(DOZE_ANIMATION_ELEMENT_DURATION);
- }
-
- public void setDozing(boolean dozing, boolean animate) {
- if (mUsesBinder) {
- return;
- }
-
- mDozing = dozing;
-
- updateWalletVisibility();
- updateControlsVisibility();
- updateQRCodeButtonVisibility();
-
- if (dozing) {
- mOverlayContainer.setVisibility(INVISIBLE);
- } else {
- mOverlayContainer.setVisibility(VISIBLE);
- if (animate) {
- startFinishDozeAnimation();
- }
- }
- }
-
- public void dozeTimeTick() {
- if (mUsesBinder) {
- return;
- }
-
- int burnInYOffset = getBurnInOffset(mBurnInYOffset * 2, false /* xAxis */)
- - mBurnInYOffset;
- mIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
- if (mAmbientIndicationArea != null) {
- mAmbientIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
- }
- }
-
- public void setAntiBurnInOffsetX(int burnInXOffset) {
- if (mUsesBinder) {
- return;
- }
-
- if (mBurnInXOffset == burnInXOffset) {
- return;
- }
- mBurnInXOffset = burnInXOffset;
- mIndicationArea.setTranslationX(burnInXOffset);
- if (mAmbientIndicationArea != null) {
- mAmbientIndicationArea.setTranslationX(burnInXOffset);
- }
- }
-
- /**
- * Sets the alpha of various sub-components, for example the indication areas and bottom quick
- * action buttons. Does not set the alpha of the lock icon.
- */
- public void setComponentAlphas(float alpha) {
- if (mUsesBinder) {
- return;
- }
-
- setImportantForAccessibility(
- alpha == 0f
- ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- if (mAmbientIndicationArea != null) {
- mAmbientIndicationArea.setAlpha(alpha);
- }
- mIndicationArea.setAlpha(alpha);
- mWalletButton.setAlpha(alpha);
- mQRCodeScannerButton.setAlpha(alpha);
- mControlsButton.setAlpha(alpha);
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- int bottom = insets.getDisplayCutout() != null
- ? insets.getDisplayCutout().getSafeInsetBottom() : 0;
- if (isPaddingRelative()) {
- setPaddingRelative(getPaddingStart(), getPaddingTop(), getPaddingEnd(), bottom);
- } else {
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), bottom);
- }
- return insets;
- }
-
- private void updateQRCodeButtonVisibility() {
- if (mUsesBinder) {
- return;
- }
-
- if (mQuickAccessWalletController != null
- && mQuickAccessWalletController.isWalletEnabled()) {
- // Don't enable if quick access wallet is enabled
- return;
- }
-
- if (mQRCodeScannerController != null
- && mQRCodeScannerController.isEnabledForLockScreenButton()) {
- mQRCodeScannerButton.setVisibility(VISIBLE);
- mQRCodeScannerButton.setOnClickListener(this::onQRCodeScannerClicked);
- mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0);
- } else {
- mQRCodeScannerButton.setVisibility(GONE);
- if (mControlsButton.getVisibility() == GONE) {
- mIndicationArea.setPadding(0, 0, 0, 0);
- }
- }
- }
-
- private void onQRCodeScannerClicked(View view) {
- if (mUsesBinder) {
- return;
- }
-
- Intent intent = mQRCodeScannerController.getIntent();
- if (intent != null) {
- try {
- ActivityTaskManager.getService().startActivityAsUser(
- null, getContext().getBasePackageName(),
- getContext().getAttributionTag(), intent,
- intent.resolveTypeIfNeeded(getContext().getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null,
- UserHandle.CURRENT.getIdentifier());
- } catch (RemoteException e) {
- // This is unexpected. Nonetheless, just log the error and prevent the UI from
- // crashing
- Log.e(TAG, "Unexpected intent: " + intent
- + " when the QR code scanner button was clicked");
- }
- }
- }
-
- private void updateAffordanceColors() {
- if (mUsesBinder) {
- return;
- }
-
- int iconColor = Utils.getColorAttrDefaultColor(
- mContext,
- com.android.internal.R.attr.textColorPrimary);
- mWalletButton.getDrawable().setTint(iconColor);
- mControlsButton.getDrawable().setTint(iconColor);
- mQRCodeScannerButton.getDrawable().setTint(iconColor);
-
- ColorStateList bgColor = Utils.getColorAttr(
- mContext,
- com.android.internal.R.attr.colorSurface);
- mWalletButton.setBackgroundTintList(bgColor);
- mControlsButton.setBackgroundTintList(bgColor);
- mQRCodeScannerButton.setBackgroundTintList(bgColor);
- }
-
- private void onWalletClick(View v) {
- if (mUsesBinder) {
- return;
- }
-
- // More coming here; need to inform the user about how to proceed
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return;
- }
-
- ActivityLaunchAnimator.Controller animationController = createLaunchAnimationController(v);
- mQuickAccessWalletController.startQuickAccessUiIntent(
- mActivityStarter, animationController, mHasCard);
- }
-
- protected ActivityLaunchAnimator.Controller createLaunchAnimationController(View view) {
- return ActivityLaunchAnimator.Controller.fromView(view, null);
- }
-
- private void onControlsClick(View v) {
- if (mUsesBinder) {
- return;
- }
-
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return;
- }
-
- Intent intent = new Intent(mContext, ControlsActivity.class)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
- .putExtra(ControlsUiController.EXTRA_ANIMATE, true);
-
- ActivityLaunchAnimator.Controller controller =
- v != null ? ActivityLaunchAnimator.Controller.fromView(v, null /* cujType */)
- : null;
- if (mControlsComponent.getVisibility() == AVAILABLE) {
- mActivityStarter.startActivity(intent, true /* dismissShade */, controller,
- true /* showOverLockscreenWhenLocked */);
- } else {
- mActivityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */, controller);
- }
- }
-
- private class WalletCardRetriever implements
- QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
-
- @Override
- public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
- mHasCard = !response.getWalletCards().isEmpty();
- Drawable tileIcon = mQuickAccessWalletController.getWalletClient().getTileIcon();
- post(() -> {
- if (tileIcon != null) {
- mWalletButton.setImageDrawable(tileIcon);
- }
- updateWalletVisibility();
- updateAffordanceColors();
- });
- }
-
- @Override
- public void onWalletCardRetrievalError(@NonNull GetWalletCardsError error) {
- mHasCard = false;
- post(() -> {
- updateWalletVisibility();
- updateAffordanceColors();
- });
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
new file mode 100644
index 000000000000..4897c529dd51
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.statusbar.phone
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewPropertyAnimator
+import android.view.WindowInsets
+import android.widget.FrameLayout
+import com.android.systemui.R
+import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder
+import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
+import com.android.systemui.plugins.FalsingManager
+
+/**
+ * Renders the bottom area of the lock-screen. Concerned primarily with the quick affordance UI
+ * elements. A secondary concern is the interaction of the quick affordance elements with the
+ * indication area between them, though the indication area is primarily controlled elsewhere.
+ */
+class KeyguardBottomAreaView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0,
+) :
+ FrameLayout(
+ context,
+ attrs,
+ defStyleAttr,
+ defStyleRes,
+ ) {
+
+ private var ambientIndicationArea: View? = null
+ private lateinit var binding: KeyguardBottomAreaViewBinder.Binding
+
+ /** Initializes the view. */
+ fun init(
+ viewModel: KeyguardBottomAreaViewModel,
+ falsingManager: FalsingManager,
+ ) {
+ binding = bind(this, viewModel, falsingManager)
+ }
+
+ /**
+ * Initializes this instance of [KeyguardBottomAreaView] based on the given instance of another
+ * [KeyguardBottomAreaView]
+ */
+ fun initFrom(oldBottomArea: KeyguardBottomAreaView) {
+ // if it exists, continue to use the original ambient indication container
+ // instead of the newly inflated one
+ ambientIndicationArea?.let { nonNullAmbientIndicationArea ->
+ // remove old ambient indication from its parent
+ val originalAmbientIndicationView =
+ oldBottomArea.findViewById<View>(R.id.ambient_indication_container)
+ (originalAmbientIndicationView.parent as ViewGroup).removeView(
+ originalAmbientIndicationView
+ )
+
+ // remove current ambient indication from its parent (discard)
+ val ambientIndicationParent = nonNullAmbientIndicationArea.parent as ViewGroup
+ val ambientIndicationIndex =
+ ambientIndicationParent.indexOfChild(nonNullAmbientIndicationArea)
+ ambientIndicationParent.removeView(nonNullAmbientIndicationArea)
+
+ // add the old ambient indication to this view
+ ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex)
+ ambientIndicationArea = originalAmbientIndicationView
+ }
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ ambientIndicationArea = findViewById(R.id.ambient_indication_container)
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ binding.onConfigurationChanged()
+ }
+
+ /** Returns a list of animators to use to animate the indication areas. */
+ val indicationAreaAnimators: List<ViewPropertyAnimator>
+ get() = binding.getIndicationAreaAnimators()
+
+ override fun hasOverlappingRendering(): Boolean {
+ return false
+ }
+
+ override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
+ val bottom = insets.displayCutout?.safeInsetBottom ?: 0
+ if (isPaddingRelative) {
+ setPaddingRelative(paddingStart, paddingTop, paddingEnd, bottom)
+ } else {
+ setPadding(paddingLeft, paddingTop, paddingRight, bottom)
+ }
+ return insets
+ }
+}
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 0dd1f4485215..985f3cd44808 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -44,7 +44,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardMessageArea;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.KeyguardMessageAreaController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -122,7 +122,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final DreamOverlayStateController mDreamOverlayStateController;
@Nullable
private final FoldAodAnimationController mFoldAodAnimationController;
- private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
private final Lazy<com.android.systemui.shade.ShadeController> mShadeController;
private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@@ -311,7 +311,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBypassController = bypassController;
mNotificationContainer = notificationContainer;
mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
- KeyguardMessageArea.findSecurityMessageDisplay(container));
+ centralSurfaces.getKeyguardMessageArea());
registerListeners();
}
@@ -378,11 +378,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
return;
} else if (mNotificationPanelViewController.isUnlockHintRunning()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
- } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
- && mKeyguardUpdateManager.isUdfpsEnrolled()) {
+ } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
// Don't expand to the bouncer. Instead transition back to the lock screen (see
- // CentralSurfaces#showBouncerOrLockScreenIfKeyguard) where the user can use the UDFPS
- // affordance to enter the device (or swipe up to the input bouncer)
+ // CentralSurfaces#showBouncerOrLockScreenIfKeyguard)
return;
} else if (bouncerNeedsScrimming()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
@@ -616,7 +614,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private void updateAlternateAuthShowing(boolean updateScrim) {
final boolean isShowingAltAuth = isShowingAlternateAuth();
if (mKeyguardMessageAreaController != null) {
- mKeyguardMessageAreaController.setAltBouncerShowing(isShowingAltAuth);
+ mKeyguardMessageAreaController.setIsVisible(isShowingAltAuth);
+ mKeyguardMessageAreaController.setMessage("");
}
mBypassController.setAltBouncerShowing(isShowingAltAuth);
mKeyguardUpdateManager.setUdfpsBouncerShowing(isShowingAltAuth);
@@ -837,30 +836,24 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
});
} else {
executeAfterKeyguardGoneAction();
- boolean wakeUnlockPulsing =
- mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
mCentralSurfaces.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
mBiometricUnlockController.startKeyguardFadingAway();
hideBouncer(true /* destroyView */);
- if (wakeUnlockPulsing) {
- mCentralSurfaces.fadeKeyguardWhilePulsing();
+
+ boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
+ if (!staying) {
+ mNotificationShadeWindowController.setKeyguardFadingAway(true);
wakeAndUnlockDejank();
+ mCentralSurfaces.hideKeyguard();
+ // hide() will happen asynchronously and might arrive after the scrims
+ // were already hidden, this means that the transition callback won't
+ // be triggered anymore and StatusBarWindowController will be forever in
+ // the fadingAway state.
+ mCentralSurfaces.updateScrimController();
} else {
- boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
- if (!staying) {
- mNotificationShadeWindowController.setKeyguardFadingAway(true);
- wakeAndUnlockDejank();
- mCentralSurfaces.hideKeyguard();
- // hide() will happen asynchronously and might arrive after the scrims
- // were already hidden, this means that the transition callback won't
- // be triggered anymore and StatusBarWindowController will be forever in
- // the fadingAway state.
- mCentralSurfaces.updateScrimController();
- } else {
- mCentralSurfaces.hideKeyguard();
- mCentralSurfaces.finishKeyguardFadingAway();
- mBiometricUnlockController.finishKeyguardFadingAway();
- }
+ mCentralSurfaces.hideKeyguard();
+ mCentralSurfaces.finishKeyguardFadingAway();
+ mBiometricUnlockController.finishKeyguardFadingAway();
}
updateStates();
@@ -1042,7 +1035,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
mNotificationShadeWindowController.setBouncerShowing(bouncerShowing);
mCentralSurfaces.setBouncerShowing(bouncerShowing);
- mKeyguardMessageAreaController.setBouncerShowing(bouncerShowing);
}
if (occluded != mLastOccluded || mFirstUpdate) {
@@ -1191,7 +1183,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
- public void showBouncerMessage(String message, ColorStateList colorState) {
+ /** Display security message to relevant KeyguardMessageArea. */
+ public void setKeyguardMessage(String message, ColorStateList colorState) {
if (isShowingAlternateAuth()) {
if (mKeyguardMessageAreaController != null) {
mKeyguardMessageAreaController.setMessage(message);
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 88eccb5ba47f..681dc6fc13a2 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
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.pipeline.dagger
import com.android.systemui.CoreStartable
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
+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 dagger.Binds
@@ -34,5 +36,8 @@ abstract class StatusBarPipelineModule {
abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
@Binds
+ abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
+
+ @Binds
abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
index 2a89309f7200..88d8a86d39f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
@@ -34,7 +34,7 @@ class ConnectivityPipelineLogger @Inject constructor(
/**
* Logs a change in one of the **raw inputs** to the connectivity pipeline.
*/
- fun logInputChange(callbackName: String, changeInfo: String) {
+ fun logInputChange(callbackName: String, changeInfo: String?) {
buffer.log(
SB_LOGGING_TAG,
LogLevel.INFO,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt
new file mode 100644
index 000000000000..d52d0fb91b82
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.data.model
+
+import android.content.Context
+import com.android.internal.R
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A container for all the different types of connectivity slots: wifi, mobile, etc.
+ */
+@SysUISingleton
+class ConnectivitySlots @Inject constructor(context: Context) {
+ private val airplaneSlot: String = context.getString(R.string.status_bar_airplane)
+ private val mobileSlot: String = context.getString(R.string.status_bar_mobile)
+ private val wifiSlot: String = context.getString(R.string.status_bar_wifi)
+ private val ethernetSlot: String = context.getString(R.string.status_bar_ethernet)
+
+ private val slotByName: Map<String, ConnectivitySlot> = mapOf(
+ airplaneSlot to ConnectivitySlot.AIRPLANE,
+ mobileSlot to ConnectivitySlot.MOBILE,
+ wifiSlot to ConnectivitySlot.WIFI,
+ ethernetSlot to ConnectivitySlot.ETHERNET
+ )
+
+ /**
+ * Given a string name of a slot, returns the instance of [ConnectivitySlot] that it corresponds
+ * to, or null if we couldn't find that slot name.
+ */
+ fun getSlotFromName(slotName: String): ConnectivitySlot? {
+ return slotByName[slotName]
+ }
+}
+
+/** The different types of connectivity slots. */
+enum class ConnectivitySlot {
+ AIRPLANE,
+ ETHERNET,
+ MOBILE,
+ WIFI,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
new file mode 100644
index 000000000000..6b1750d11562
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.data.repository
+
+import android.content.Context
+import androidx.annotation.ArrayRes
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+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.dump.DumpManager
+import com.android.systemui.statusbar.phone.StatusBarIconController
+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.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
+import com.android.systemui.tuner.TunerService
+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.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Provides data related to the connectivity state that needs to be shared across multiple different
+ * types of connectivity (wifi, mobile, ethernet, etc.)
+ */
+interface ConnectivityRepository {
+ /**
+ * Observable for the current set of connectivity icons that should be force-hidden.
+ */
+ val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>>
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class ConnectivityRepositoryImpl @Inject constructor(
+ private val connectivitySlots: ConnectivitySlots,
+ context: Context,
+ dumpManager: DumpManager,
+ logger: ConnectivityPipelineLogger,
+ @Application scope: CoroutineScope,
+ tunerService: TunerService,
+) : ConnectivityRepository, Dumpable {
+ init {
+ dumpManager.registerDumpable("$SB_LOGGING_TAG:ConnectivityRepository", this)
+ }
+
+ // The default set of hidden icons to use if we don't get any from [TunerService].
+ private val defaultHiddenIcons: Set<ConnectivitySlot> =
+ context.resources.getStringArray(DEFAULT_HIDDEN_ICONS_RESOURCE)
+ .asList()
+ .toSlotSet(connectivitySlots)
+
+ override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> = conflatedCallbackFlow {
+ val callback = object : TunerService.Tunable {
+ override fun onTuningChanged(key: String, newHideList: String?) {
+ if (key != HIDDEN_ICONS_TUNABLE_KEY) {
+ return
+ }
+ logger.logInputChange("onTuningChanged", newHideList)
+
+ val outputList = newHideList?.split(",")?.toSlotSet(connectivitySlots)
+ ?: defaultHiddenIcons
+ trySend(outputList)
+ }
+ }
+ tunerService.addTunable(callback, HIDDEN_ICONS_TUNABLE_KEY)
+
+ awaitClose { tunerService.removeTunable(callback) }
+ }
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = defaultHiddenIcons
+ )
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.apply {
+ println("defaultHiddenIcons=$defaultHiddenIcons")
+ }
+ }
+
+ companion object {
+ @VisibleForTesting
+ internal const val HIDDEN_ICONS_TUNABLE_KEY = StatusBarIconController.ICON_HIDE_LIST
+ @VisibleForTesting
+ @ArrayRes
+ internal val DEFAULT_HIDDEN_ICONS_RESOURCE = R.array.config_statusBarIconsToExclude
+
+ /** Converts a list of string slot names to a set of [ConnectivitySlot] instances. */
+ private fun List<String>.toSlotSet(
+ connectivitySlots: ConnectivitySlots
+ ): Set<ConnectivitySlot> {
+ return this
+ .filter { it.isNotBlank() }
+ .mapNotNull { connectivitySlots.getSlotFromName(it) }
+ .toSet()
+ }
+ }
+}
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 afe19afff55f..952525d243f9 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
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
import android.net.wifi.WifiManager
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import javax.inject.Inject
@@ -33,9 +35,10 @@ import kotlinx.coroutines.flow.map
*/
@SysUISingleton
class WifiInteractor @Inject constructor(
- repository: WifiRepository,
+ connectivityRepository: ConnectivityRepository,
+ wifiRepository: WifiRepository,
) {
- private val ssid: Flow<String?> = repository.wifiNetwork.map { info ->
+ private val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
when (info) {
is WifiNetworkModel.Inactive -> null
is WifiNetworkModel.CarrierMerged -> null
@@ -49,10 +52,16 @@ class WifiInteractor @Inject constructor(
}
/** Our current wifi network. See [WifiNetworkModel]. */
- val wifiNetwork: Flow<WifiNetworkModel> = repository.wifiNetwork
+ val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
+
+ /** True if we're configured to force-hide the wifi icon and false otherwise. */
+ val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
+ it.contains(ConnectivitySlot.WIFI)
+ }
/** True if our wifi network has activity in (download), and false otherwise. */
- val hasActivityIn: Flow<Boolean> = combine(repository.wifiActivity, ssid) { activity, ssid ->
+ val hasActivityIn: Flow<Boolean> =
+ combine(wifiRepository.wifiActivity, ssid) { activity, ssid ->
activity.hasActivityIn && ssid != null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index 7607ddf4fe8c..4fad3274d12f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -24,6 +24,7 @@ import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R
+import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
import kotlinx.coroutines.InternalCoroutinesApi
@@ -54,14 +55,15 @@ object WifiViewBinder {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
- viewModel.wifiIconResId.distinctUntilChanged().collect { iconResId ->
- iconView.setImageDrawable(
- if (iconResId != null && iconResId > 0) {
- iconView.context.getDrawable(iconResId)
- } else {
- null
- }
- )
+ viewModel.wifiIcon.distinctUntilChanged().collect { wifiIcon ->
+ // TODO(b/238425913): Right now, if !isVisible, there's just an empty space
+ // where the wifi icon would be. We need to pipe isVisible through to
+ // [ModernStatusBarWifiView.isIconVisible], which is what actually makes
+ // the view GONE.
+ view.isVisible = wifiIcon != null
+ wifiIcon?.let {
+ IconViewBinder.bind(wifiIcon, iconView)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index 4fdcc44f1802..3c243ac90831 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -16,8 +16,16 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
+import android.content.Context
import android.graphics.Color
import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import androidx.annotation.VisibleForTesting
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
@@ -29,6 +37,7 @@ import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiIntera
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -39,6 +48,7 @@ import kotlinx.coroutines.flow.map
class WifiViewModel @Inject constructor(
statusBarPipelineFlags: StatusBarPipelineFlags,
private val constants: WifiConstants,
+ private val context: Context,
private val logger: ConnectivityPipelineLogger,
private val interactor: WifiInteractor,
) {
@@ -46,7 +56,7 @@ class WifiViewModel @Inject constructor(
* The drawable resource ID to use for the wifi icon. Null if we shouldn't display any icon.
*/
@DrawableRes
- val wifiIconResId: Flow<Int?> = interactor.wifiNetwork.map {
+ private val iconResId: Flow<Int?> = interactor.wifiNetwork.map {
when (it) {
is WifiNetworkModel.CarrierMerged -> null
is WifiNetworkModel.Inactive -> WIFI_NO_NETWORK
@@ -59,6 +69,49 @@ class WifiViewModel @Inject constructor(
}
}
+ /** The content description for the wifi icon. */
+ private val contentDescription: Flow<ContentDescription?> = interactor.wifiNetwork.map {
+ when (it) {
+ is WifiNetworkModel.CarrierMerged -> null
+ is WifiNetworkModel.Inactive ->
+ ContentDescription.Loaded(
+ "${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}"
+ )
+ is WifiNetworkModel.Active ->
+ when (it.level) {
+ null -> null
+ else -> {
+ val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[it.level])
+ when {
+ it.isValidated -> ContentDescription.Loaded(levelDesc)
+ else -> ContentDescription.Loaded(
+ "$levelDesc,${context.getString(NO_INTERNET)}"
+ )
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * The wifi icon that should be displayed. Null if we shouldn't display any icon.
+ */
+ val wifiIcon: Flow<Icon?> = combine(
+ interactor.isForceHidden,
+ iconResId,
+ contentDescription,
+ ) { isForceHidden, iconResId, contentDescription ->
+ when {
+ isForceHidden ||
+ iconResId == null ||
+ iconResId <= 0 -> null
+ else -> Icon.Resource(iconResId, contentDescription)
+ }
+ }
+
+ /**
+ * True if the activity in icon should be displayed and false otherwise.
+ */
val isActivityInVisible: Flow<Boolean>
get() =
if (!constants.shouldShowActivityConfig) {
@@ -74,4 +127,10 @@ class WifiViewModel @Inject constructor(
} else {
flowOf(Color.CYAN)
}
+
+ companion object {
+ @StringRes
+ @VisibleForTesting
+ internal val NO_INTERNET = R.string.data_connection_no_internet
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index bdac88837969..f4d08e01d5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -31,6 +31,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -60,6 +61,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
new UpdateMonitorCallback();
private final Lazy<KeyguardUnlockAnimationController> mUnlockAnimationControllerLazy;
+ private final KeyguardUpdateMonitorLogger mLogger;
private boolean mCanDismissLockScreen;
private boolean mShowing;
@@ -107,8 +109,10 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
KeyguardUpdateMonitor keyguardUpdateMonitor,
LockPatternUtils lockPatternUtils,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
+ KeyguardUpdateMonitorLogger logger,
DumpManager dumpManager) {
mContext = context;
+ mLogger = logger;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
@@ -245,6 +249,8 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
mTrusted = trusted;
mTrustManaged = trustManaged;
mFaceAuthEnabled = faceAuthEnabled;
+ mLogger.logKeyguardStateUpdate(
+ mSecure, mCanDismissLockScreen, mTrusted, mTrustManaged);
notifyUnlockedChanged();
}
Trace.endSection();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index e2d16015235e..3b8ed338db80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -86,6 +86,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.CreateUserActivity;
import com.android.systemui.user.data.source.UserRecord;
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -100,14 +101,20 @@ import java.util.function.Consumer;
import javax.inject.Inject;
+import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlowKt;
+
/**
* Keeps a list of all users on the device for user switching.
*/
@SysUISingleton
public class UserSwitcherController implements Dumpable {
- public static final float USER_SWITCH_ENABLED_ALPHA = 1.0f;
- public static final float USER_SWITCH_DISABLED_ALPHA = 0.38f;
+ public static final float USER_SWITCH_ENABLED_ALPHA =
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA;
+ public static final float USER_SWITCH_DISABLED_ALPHA =
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA;
private static final String TAG = "UserSwitcherController";
private static final boolean DEBUG = false;
@@ -155,7 +162,8 @@ public class UserSwitcherController implements Dumpable {
private boolean mSimpleUserSwitcher;
// When false, there won't be any visual affordance to add a new user from the keyguard even if
// the user is unlocked
- private boolean mAddUsersFromLockScreen;
+ private final MutableStateFlow<Boolean> mAddUsersFromLockScreen =
+ StateFlowKt.MutableStateFlow(false);
private boolean mUserSwitcherEnabled;
@VisibleForTesting
boolean mPauseRefreshUsers;
@@ -258,8 +266,11 @@ public class UserSwitcherController implements Dumpable {
@Override
public void onChange(boolean selfChange) {
mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
- mAddUsersFromLockScreen = mGlobalSettings.getIntForUser(
- Settings.Global.ADD_USERS_WHEN_LOCKED, 0, UserHandle.USER_SYSTEM) != 0;
+ mAddUsersFromLockScreen.setValue(
+ mGlobalSettings.getIntForUser(
+ Settings.Global.ADD_USERS_WHEN_LOCKED,
+ 0,
+ UserHandle.USER_SYSTEM) != 0);
mUserSwitcherEnabled = mGlobalSettings.getIntForUser(
Settings.Global.USER_SWITCHER_ENABLED, 0, UserHandle.USER_SYSTEM) != 0;
refreshUsers(UserHandle.USER_NULL);
@@ -323,7 +334,6 @@ public class UserSwitcherController implements Dumpable {
}
mForcePictureLoadForUserId.clear();
- final boolean addUsersWhenLocked = mAddUsersFromLockScreen;
mBgExecutor.execute(() -> {
List<UserInfo> infos = mUserManager.getAliveUsers();
if (infos == null) {
@@ -434,7 +444,7 @@ public class UserSwitcherController implements Dumpable {
}
boolean anyoneCanCreateUsers() {
- return systemCanCreateUsers() && mAddUsersFromLockScreen;
+ return systemCanCreateUsers() && mAddUsersFromLockScreen.getValue();
}
boolean canCreateGuest(boolean hasExistingGuest) {
@@ -450,7 +460,7 @@ public class UserSwitcherController implements Dumpable {
}
boolean createIsRestricted() {
- return !mAddUsersFromLockScreen;
+ return !mAddUsersFromLockScreen.getValue();
}
boolean canCreateSupervisedUser() {
@@ -516,17 +526,48 @@ public class UserSwitcherController implements Dumpable {
return null;
}
+ /**
+ * Notifies that a user has been selected.
+ *
+ * <p>This will trigger the right user journeys to create a guest user, switch users, and/or
+ * navigate to the correct destination.
+ *
+ * <p>If a user with the given ID is not found, this method is a no-op.
+ *
+ * @param userId The ID of the user to switch to.
+ * @param dialogShower An optional {@link DialogShower} in case we need to show dialogs.
+ */
+ public void onUserSelected(int userId, @Nullable DialogShower dialogShower) {
+ UserRecord userRecord = mUsers.stream()
+ .filter(x -> x.resolveId() == userId)
+ .findFirst()
+ .orElse(null);
+ if (userRecord == null) {
+ return;
+ }
+
+ onUserListItemClicked(userRecord, dialogShower);
+ }
+
+ /** Whether it is allowed to add users while the device is locked. */
+ public Flow<Boolean> getAddUsersFromLockScreen() {
+ return mAddUsersFromLockScreen;
+ }
+
+ /** Returns {@code true} if the guest user is configured to always be present on the device. */
+ public boolean isGuestUserAutoCreated() {
+ return mGuestUserAutoCreated;
+ }
+
+ /** Returns {@code true} if the guest user is currently being reset. */
+ public boolean isGuestUserResetting() {
+ return mGuestIsResetting.get();
+ }
+
@VisibleForTesting
void onUserListItemClicked(UserRecord record, DialogShower dialogShower) {
if (record.isGuest && record.info == null) {
- // No guest user. Create one.
- createGuestAsync(guestId -> {
- // guestId may be USER_NULL if we haven't reloaded the user list yet.
- if (guestId != UserHandle.USER_NULL) {
- mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_ADD);
- onUserListItemClicked(guestId, record, dialogShower);
- }
- });
+ createAndSwitchToGuestUser(dialogShower);
} else if (record.isAddUser) {
showAddUserDialog(dialogShower);
} else if (record.isAddSupervisedUser) {
@@ -604,7 +645,23 @@ public class UserSwitcherController implements Dumpable {
}
}
- private void showAddUserDialog(DialogShower dialogShower) {
+ /**
+ * Creates and switches to the guest user.
+ */
+ public void createAndSwitchToGuestUser(@Nullable DialogShower dialogShower) {
+ createGuestAsync(guestId -> {
+ // guestId may be USER_NULL if we haven't reloaded the user list yet.
+ if (guestId != UserHandle.USER_NULL) {
+ mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_ADD);
+ onUserListItemClicked(guestId, UserRecord.createForGuest(), dialogShower);
+ }
+ });
+ }
+
+ /**
+ * Shows the add user dialog.
+ */
+ public void showAddUserDialog(@Nullable DialogShower dialogShower) {
if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
mAddUserDialog.cancel();
}
@@ -620,7 +677,10 @@ public class UserSwitcherController implements Dumpable {
}
}
- private void startSupervisedUserActivity() {
+ /**
+ * Starts an activity to add a supervised user to the device.
+ */
+ public void startSupervisedUserActivity() {
final Intent intent = new Intent()
.setAction(UserManager.ACTION_CREATE_SUPERVISED_USER)
.setPackage(mCreateSupervisedUserPackage)
@@ -772,7 +832,7 @@ public class UserSwitcherController implements Dumpable {
* Removes guest user and switches to target user. The guest must be the current user and its id
* must be {@code guestUserId}.
*
- * <p>If {@code targetUserId} is {@link UserHandle.USER_NULL}, then create a new guest user in
+ * <p>If {@code targetUserId} is {@link UserHandle#USER_NULL}, then create a new guest user in
* the foreground, and immediately switch to it. This is used for wiping the current guest and
* replacing it with a new one.
*
@@ -782,11 +842,11 @@ public class UserSwitcherController implements Dumpable {
* <p>If device is configured with {@link
* com.android.internal.R.bool.config_guestUserAutoCreated}, then after guest user is removed, a
* new one is created in the background. This has no effect if {@code targetUserId} is {@link
- * UserHandle.USER_NULL}.
+ * UserHandle#USER_NULL}.
*
* @param guestUserId id of the guest user to remove
* @param targetUserId id of the user to switch to after guest is removed. If {@link
- * UserHandle.USER_NULL}, then switch immediately to the newly created guest user.
+ * UserHandle#USER_NULL}, then switch immediately to the newly created guest user.
*/
public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) {
UserInfo currentUser = mUserTracker.getUserInfo();
@@ -839,7 +899,7 @@ public class UserSwitcherController implements Dumpable {
* user.
*
* @param guestUserId user id of the guest user to exit
- * @param targetUserId user id of the guest user to exit, set to UserHandle.USER_NULL when
+ * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when
* target user id is not known
* @param forceRemoveGuestOnExit true: remove guest before switching user,
* false: remove guest only if its ephemeral, else keep guest
@@ -952,7 +1012,7 @@ public class UserSwitcherController implements Dumpable {
* {@link UserManager} to create a new one.
*
* @return The multi-user user ID of the newly created guest user, or
- * {@link UserHandle.USER_NULL} if the guest couldn't be created.
+ * {@link UserHandle#USER_NULL} if the guest couldn't be created.
*/
public @UserIdInt int createGuest() {
UserInfo guest;
@@ -1062,38 +1122,11 @@ public class UserSwitcherController implements Dumpable {
}
public String getName(Context context, UserRecord item) {
- if (item.isGuest) {
- if (item.isCurrent) {
- return context.getString(
- com.android.settingslib.R.string.guest_exit_quick_settings_button);
- } else {
- if (item.info != null) {
- return context.getString(com.android.internal.R.string.guest_name);
- } else {
- if (mController.mGuestUserAutoCreated) {
- // If mGuestIsResetting=true, we expect the guest user to be created
- // shortly, so display a "Resetting guest..." as an indicator that we
- // are busy. Otherwise, if mGuestIsResetting=false, we probably failed
- // to create a guest at some point. In this case, always show guest
- // nickname instead of "Add guest" to make it seem as though the device
- // always has a guest ready for use.
- return context.getString(
- mController.mGuestIsResetting.get()
- ? com.android.settingslib.R.string.guest_resetting
- : com.android.internal.R.string.guest_name);
- } else {
- // we always show "guest" as string, instead of "add guest"
- return context.getString(com.android.internal.R.string.guest_name);
- }
- }
- }
- } else if (item.isAddUser) {
- return context.getString(com.android.settingslib.R.string.user_add_user);
- } else if (item.isAddSupervisedUser) {
- return context.getString(R.string.add_user_supervised);
- } else {
- return item.info.name;
- }
+ return LegacyUserUiHelper.getUserRecordName(
+ context,
+ item,
+ mController.isGuestUserAutoCreated(),
+ mController.isGuestUserResetting());
}
protected static ColorFilter getDisabledUserAvatarColorFilter() {
@@ -1103,17 +1136,8 @@ public class UserSwitcherController implements Dumpable {
}
protected static Drawable getIconDrawable(Context context, UserRecord item) {
- int iconRes;
- if (item.isAddUser) {
- iconRes = R.drawable.ic_add;
- } else if (item.isGuest) {
- iconRes = R.drawable.ic_account_circle;
- } else if (item.isAddSupervisedUser) {
- iconRes = R.drawable.ic_add_supervised_user;
- } else {
- iconRes = R.drawable.ic_avatar_user;
- }
-
+ int iconRes = LegacyUserUiHelper.getUserSwitcherActionIconResourceId(
+ item.isAddUser, item.isGuest, item.isAddSupervisedUser);
return context.getDrawable(iconRes);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 3a0ac1b7d9b0..734eeecec215 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.temporarydisplay
import android.annotation.LayoutRes
import android.annotation.SuppressLint
@@ -37,30 +37,30 @@ import com.android.internal.widget.CachingIconView
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.view.ViewUtil
/**
- * A superclass controller that provides common functionality for showing chips on the sender device
- * and the receiver device.
+ * A generic controller that can temporarily display a new view in a new window.
*
- * Subclasses need to override and implement [updateChipView], which is where they can control what
+ * Subclasses need to override and implement [updateView], which is where they can control what
* gets displayed to the user.
*
* The generic type T is expected to contain all the information necessary for the subclasses to
- * display the chip in a certain state, since they receive <T> in [updateChipView].
+ * display the view in a certain state, since they receive <T> in [updateView].
+ *
+ * TODO(b/245610654): Remove all the media-specific logic from this class.
*/
-abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
- internal val context: Context,
- internal val logger: MediaTttLogger,
- internal val windowManager: WindowManager,
- private val viewUtil: ViewUtil,
- @Main private val mainExecutor: DelayableExecutor,
- private val accessibilityManager: AccessibilityManager,
- private val configurationController: ConfigurationController,
- private val powerManager: PowerManager,
- @LayoutRes private val chipLayoutRes: Int,
+abstract class TemporaryViewDisplayController<T : TemporaryViewInfo>(
+ internal val context: Context,
+ internal val logger: MediaTttLogger,
+ internal val windowManager: WindowManager,
+ @Main private val mainExecutor: DelayableExecutor,
+ private val accessibilityManager: AccessibilityManager,
+ private val configurationController: ConfigurationController,
+ private val powerManager: PowerManager,
+ @LayoutRes private val viewLayoutRes: Int,
) {
/**
* Window layout params that will be used as a starting point for the [windowLayoutParams] of
@@ -85,31 +85,31 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
*/
internal abstract val windowLayoutParams: WindowManager.LayoutParams
- /** The chip view currently being displayed. Null if the chip is not being displayed. */
- private var chipView: ViewGroup? = null
+ /** The view currently being displayed. Null if the view is not being displayed. */
+ private var view: ViewGroup? = null
- /** The chip info currently being displayed. Null if the chip is not being displayed. */
- internal var chipInfo: T? = null
+ /** The info currently being displayed. Null if the view is not being displayed. */
+ internal var info: T? = null
- /** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
- private var cancelChipViewTimeout: Runnable? = null
+ /** A [Runnable] that, when run, will cancel the pending timeout of the view. */
+ private var cancelViewTimeout: Runnable? = null
/**
- * Displays the chip with the provided [newChipInfo].
+ * Displays the view with the provided [newInfo].
*
- * This method handles inflating and attaching the view, then delegates to [updateChipView] to
- * display the correct information in the chip.
+ * This method handles inflating and attaching the view, then delegates to [updateView] to
+ * display the correct information in the view.
*/
- fun displayChip(newChipInfo: T) {
- val currentChipView = chipView
+ fun displayView(newInfo: T) {
+ val currentView = view
- if (currentChipView != null) {
- updateChipView(newChipInfo, currentChipView)
+ if (currentView != null) {
+ updateView(newInfo, currentView)
} else {
- // The chip is new, so set up all our callbacks and inflate the view
+ // The view is new, so set up all our callbacks and inflate the view
configurationController.addCallback(displayScaleListener)
- // Wake the screen if necessary so the user will see the chip. (Per b/239426653, we want
- // the chip to show over the dream state, so we should only wake up if the screen is
+ // Wake the screen if necessary so the user will see the view. (Per b/239426653, we want
+ // the view to show over the dream state, so we should only wake up if the screen is
// completely off.)
if (!powerManager.isScreenOn) {
powerManager.wakeUp(
@@ -119,79 +119,79 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
)
}
- inflateAndUpdateChip(newChipInfo)
+ inflateAndUpdateView(newInfo)
}
- // Cancel and re-set the chip timeout each time we get a new state.
+ // Cancel and re-set the view timeout each time we get a new state.
val timeout = accessibilityManager.getRecommendedTimeoutMillis(
- newChipInfo.getTimeoutMs().toInt(),
- // Not all chips have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
+ newInfo.getTimeoutMs().toInt(),
+ // Not all views have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
// include it just to be safe.
FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS
)
- cancelChipViewTimeout?.run()
- cancelChipViewTimeout = mainExecutor.executeDelayed(
- { removeChip(MediaTttRemovalReason.REASON_TIMEOUT) },
+ cancelViewTimeout?.run()
+ cancelViewTimeout = mainExecutor.executeDelayed(
+ { removeView(TemporaryDisplayRemovalReason.REASON_TIMEOUT) },
timeout.toLong()
)
}
- /** Inflates a new chip view, updates it with [newChipInfo], and adds the view to the window. */
- private fun inflateAndUpdateChip(newChipInfo: T) {
- val newChipView = LayoutInflater
+ /** Inflates a new view, updates it with [newInfo], and adds the view to the window. */
+ private fun inflateAndUpdateView(newInfo: T) {
+ val newView = LayoutInflater
.from(context)
- .inflate(chipLayoutRes, null) as ViewGroup
- chipView = newChipView
- updateChipView(newChipInfo, newChipView)
- windowManager.addView(newChipView, windowLayoutParams)
- animateChipIn(newChipView)
+ .inflate(viewLayoutRes, null) as ViewGroup
+ view = newView
+ updateView(newInfo, newView)
+ windowManager.addView(newView, windowLayoutParams)
+ animateViewIn(newView)
}
- /** Removes then re-inflates the chip. */
- private fun reinflateChip() {
- val currentChipInfo = chipInfo
- if (chipView == null || currentChipInfo == null) { return }
+ /** Removes then re-inflates the view. */
+ private fun reinflateView() {
+ val currentInfo = info
+ if (view == null || currentInfo == null) { return }
- windowManager.removeView(chipView)
- inflateAndUpdateChip(currentChipInfo)
+ windowManager.removeView(view)
+ inflateAndUpdateView(currentInfo)
}
private val displayScaleListener = object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
- reinflateChip()
+ reinflateView()
}
}
/**
- * Hides the chip.
+ * Hides the view.
*
- * @param removalReason a short string describing why the chip was removed (timeout, state
+ * @param removalReason a short string describing why the view was removed (timeout, state
* change, etc.)
*/
- open fun removeChip(removalReason: String) {
- if (chipView == null) { return }
+ open fun removeView(removalReason: String) {
+ if (view == null) { return }
logger.logChipRemoval(removalReason)
configurationController.removeCallback(displayScaleListener)
- windowManager.removeView(chipView)
- chipView = null
- chipInfo = null
- // No need to time the chip out since it's already gone
- cancelChipViewTimeout?.run()
+ windowManager.removeView(view)
+ view = null
+ info = null
+ // No need to time the view out since it's already gone
+ cancelViewTimeout?.run()
}
/**
- * A method implemented by subclasses to update [currentChipView] based on [newChipInfo].
+ * A method implemented by subclasses to update [currentView] based on [newInfo].
*/
@CallSuper
- open fun updateChipView(newChipInfo: T, currentChipView: ViewGroup) {
- chipInfo = newChipInfo
+ open fun updateView(newInfo: T, currentView: ViewGroup) {
+ info = newInfo
}
/**
- * A method that can be implemented by subclcasses to do custom animations for when the chip
+ * A method that can be implemented by subclasses to do custom animations for when the view
* appears.
*/
- open fun animateChipIn(chipView: ViewGroup) {}
+ open fun animateViewIn(view: ViewGroup) {}
/**
* Returns the size that the icon should be, or null if no size override is needed.
@@ -209,12 +209,12 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
* @return the content description of the icon.
*/
internal fun setIcon(
- currentChipView: ViewGroup,
+ currentView: ViewGroup,
appPackageName: String?,
appIconDrawableOverride: Drawable? = null,
appNameOverride: CharSequence? = null,
): CharSequence {
- val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
+ val appIconView = currentView.requireViewById<CachingIconView>(R.id.app_icon)
val iconInfo = getIconInfo(appPackageName)
getIconSize(iconInfo.isAppIcon)?.let { size ->
@@ -264,9 +264,9 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
// Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
// UpdateMediaTapToTransferReceiverDisplayTest
private const val WINDOW_TITLE = "Media Transfer Chip View"
-private val TAG = MediaTttChipControllerCommon::class.simpleName!!
+private val TAG = TemporaryViewDisplayController::class.simpleName!!
-object MediaTttRemovalReason {
+object TemporaryDisplayRemovalReason {
const val REASON_TIMEOUT = "TIMEOUT"
const val REASON_SCREEN_TAP = "SCREEN_TAP"
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
index a29c5883118c..4fe753a80faf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.temporarydisplay
/**
- * A superclass chip state that will be subclassed by the sender chip and receiver chip.
+ * A superclass view state used with [TemporaryViewDisplayController].
*/
-interface ChipInfoCommon {
+interface TemporaryViewInfo {
/**
- * Returns the amount of time the given chip state should display on the screen before it times
+ * Returns the amount of time the given view state should display on the screen before it times
* out and disappears.
*/
- fun getTimeoutMs(): Long
+ fun getTimeoutMs(): Long = DEFAULT_TIMEOUT_MILLIS
}
const val DEFAULT_TIMEOUT_MILLIS = 10000L
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 8f127fd1fcb4..0f0614414a3f 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -18,22 +18,31 @@ package com.android.systemui.unfold
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
-import android.os.Handler
import android.os.PowerManager
import android.provider.Settings
+import androidx.annotation.VisibleForTesting
import androidx.core.view.OneShotPreDrawListener
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.ScreenOffAnimation
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
+import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.settings.GlobalSettings
-import java.util.concurrent.Executor
+import dagger.Lazy
import java.util.function.Consumer
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
/**
* Controls folding to AOD animation: when AOD is enabled and foldable device is folded we play a
@@ -43,16 +52,16 @@ import javax.inject.Inject
class FoldAodAnimationController
@Inject
constructor(
- @Main private val handler: Handler,
- @Main private val executor: Executor,
+ @Main private val executor: DelayableExecutor,
private val context: Context,
private val deviceStateManager: DeviceStateManager,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val globalSettings: GlobalSettings,
private val latencyTracker: LatencyTracker,
+ private val keyguardInteractor: Lazy<KeyguardInteractor>,
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
- private lateinit var mCentralSurfaces: CentralSurfaces
+ private lateinit var centralSurfaces: CentralSurfaces
private var isFolded = false
private var isFoldHandled = true
@@ -64,12 +73,13 @@ constructor(
private var shouldPlayAnimation = false
private var isAnimationPlaying = false
+ private var cancelAnimation: Runnable? = null
private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
private val foldToAodLatencyTracker = FoldToAodLatencyTracker()
private val startAnimationRunnable = Runnable {
- mCentralSurfaces.notificationPanelViewController.startFoldToAodAnimation(
+ centralSurfaces.notificationPanelViewController.startFoldToAodAnimation(
/* startAction= */ { foldToAodLatencyTracker.onAnimationStarted() },
/* endAction= */ { setAnimationState(playing = false) },
/* cancelAction= */ { setAnimationState(playing = false) },
@@ -77,10 +87,14 @@ constructor(
}
override fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {
- this.mCentralSurfaces = centralSurfaces
+ this.centralSurfaces = centralSurfaces
deviceStateManager.registerCallback(executor, FoldListener())
wakefulnessLifecycle.addObserver(this)
+
+ centralSurfaces.notificationPanelViewController.view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) { listenForDozing(this) }
+ }
}
/** Returns true if we should run fold to AOD animation */
@@ -94,7 +108,7 @@ constructor(
override fun startAnimation(): Boolean =
if (shouldStartAnimation()) {
setAnimationState(playing = true)
- mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
+ centralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
true
} else {
setAnimationState(playing = false)
@@ -104,8 +118,8 @@ constructor(
override fun onStartedWakingUp() {
if (isAnimationPlaying) {
foldToAodLatencyTracker.cancel()
- handler.removeCallbacks(startAnimationRunnable)
- mCentralSurfaces.notificationPanelViewController.cancelFoldToAodAnimation()
+ cancelAnimation?.run()
+ centralSurfaces.notificationPanelViewController.cancelFoldToAodAnimation()
}
setAnimationState(playing = false)
@@ -138,13 +152,13 @@ constructor(
// We should play the folding to AOD animation
setAnimationState(playing = true)
- mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
+ centralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
// We don't need to wait for the scrim as it is already displayed
// but we should wait for the initial animation preparations to be drawn
// (setting initial alpha/translation)
OneShotPreDrawListener.add(
- mCentralSurfaces.notificationPanelViewController.view,
+ centralSurfaces.notificationPanelViewController.view,
onReady
)
} else {
@@ -165,18 +179,14 @@ constructor(
fun onScreenTurnedOn() {
if (shouldPlayAnimation) {
- handler.removeCallbacks(startAnimationRunnable)
+ cancelAnimation?.run()
// Post starting the animation to the next frame to avoid junk due to inset changes
- handler.post(startAnimationRunnable)
+ cancelAnimation = executor.executeDelayed(startAnimationRunnable, /* delayMillis= */ 0)
shouldPlayAnimation = false
}
}
- fun setIsDozing(dozing: Boolean) {
- isDozing = dozing
- }
-
override fun isAnimationPlaying(): Boolean = isAnimationPlaying
override fun isKeyguardHideDelayed(): Boolean = isAnimationPlaying()
@@ -204,6 +214,11 @@ constructor(
statusListeners.remove(listener)
}
+ @VisibleForTesting
+ internal suspend fun listenForDozing(scope: CoroutineScope): Job {
+ return scope.launch { keyguardInteractor.get().isDozing.collect { isDozing = it } }
+ }
+
interface FoldAodAnimationStatus {
fun onFoldToAodAnimationChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
index 469d54ff8ffa..5b522dcc4885 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserModule.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
@@ -19,6 +19,7 @@ package com.android.systemui.user;
import android.app.Activity;
import com.android.settingslib.users.EditUserInfoController;
+import com.android.systemui.user.data.repository.UserRepositoryModule;
import dagger.Binds;
import dagger.Module;
@@ -29,7 +30,11 @@ import dagger.multibindings.IntoMap;
/**
* Dagger module for User related classes.
*/
-@Module
+@Module(
+ includes = {
+ UserRepositoryModule.class,
+ }
+)
public abstract class UserModule {
private static final String FILE_PROVIDER_AUTHORITY = "com.android.systemui.fileprovider";
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index ff0f0d48a7c5..8a51cd6c7e94 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -27,6 +27,7 @@ import android.graphics.drawable.LayerDrawable
import android.os.Bundle
import android.os.UserManager
import android.provider.Settings
+import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
@@ -37,6 +38,7 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.constraintlayout.helper.widget.Flow
+import androidx.lifecycle.ViewModelProvider
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.util.UserIcons
import com.android.settingslib.Utils
@@ -44,6 +46,8 @@ import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
import com.android.systemui.settings.UserTracker
@@ -52,6 +56,9 @@ import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdap
import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA
import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA
import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.ui.binder.UserSwitcherViewBinder
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import dagger.Lazy
import javax.inject.Inject
import kotlin.math.ceil
@@ -63,11 +70,12 @@ private const val USER_VIEW = "user_view"
class UserSwitcherActivity @Inject constructor(
private val userSwitcherController: UserSwitcherController,
private val broadcastDispatcher: BroadcastDispatcher,
- private val layoutInflater: LayoutInflater,
private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
private val userManager: UserManager,
- private val userTracker: UserTracker
+ private val userTracker: UserTracker,
+ private val flags: FeatureFlags,
+ private val viewModelFactory: Lazy<UserSwitcherViewModel.Factory>,
) : ComponentActivity() {
private lateinit var parent: UserSwitcherRootView
@@ -93,119 +101,31 @@ class UserSwitcherActivity @Inject constructor(
false /* isAddSupervisedUser */
)
- private val adapter = object : BaseUserAdapter(userSwitcherController) {
- override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
- val item = getItem(position)
- var view = convertView as ViewGroup?
- if (view == null) {
- view = layoutInflater.inflate(
- R.layout.user_switcher_fullscreen_item,
- parent,
- false
- ) as ViewGroup
- }
- (view.getChildAt(0) as ImageView).apply {
- setImageDrawable(getDrawable(item))
- }
- (view.getChildAt(1) as TextView).apply {
- setText(getName(getContext(), item))
- }
-
- view.setEnabled(item.isSwitchToEnabled)
- view.setAlpha(
- if (view.isEnabled()) {
- USER_SWITCH_ENABLED_ALPHA
- } else {
- USER_SWITCH_DISABLED_ALPHA
- }
- )
- view.setTag(USER_VIEW)
- return view
- }
-
- override fun getName(context: Context, item: UserRecord): String {
- return if (item == manageUserRecord) {
- getString(R.string.manage_users)
- } else {
- super.getName(context, item)
- }
- }
-
- fun findUserIcon(item: UserRecord): Drawable {
- if (item == manageUserRecord) {
- return getDrawable(R.drawable.ic_manage_users)
- }
- if (item.info == null) {
- return getIconDrawable(this@UserSwitcherActivity, item)
- }
- val userIcon = userManager.getUserIcon(item.info.id)
- if (userIcon != null) {
- return BitmapDrawable(userIcon)
- }
- return UserIcons.getDefaultUserIcon(resources, item.info.id, false)
- }
-
- fun getTotalUserViews(): Int {
- return users.count { item ->
- !doNotRenderUserView(item)
- }
- }
-
- fun doNotRenderUserView(item: UserRecord): Boolean {
- return item.isAddUser ||
- item.isAddSupervisedUser ||
- item.isGuest && item.info == null
- }
-
- private fun getDrawable(item: UserRecord): Drawable {
- var drawable = if (item.isGuest) {
- getDrawable(R.drawable.ic_account_circle)
- } else {
- findUserIcon(item)
- }
- drawable.mutate()
-
- if (!item.isCurrent && !item.isSwitchToEnabled) {
- drawable.setTint(
- resources.getColor(
- R.color.kg_user_switcher_restricted_avatar_icon_color,
- getTheme()
- )
- )
- }
-
- val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate()
- as LayerDrawable
- if (item == userSwitcherController.getCurrentUserRecord()) {
- (ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply {
- val stroke = resources
- .getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width)
- val color = Utils.getColorAttrDefaultColor(
- this@UserSwitcherActivity,
- com.android.internal.R.attr.colorAccentPrimary
- )
-
- setStroke(stroke, color)
- }
- }
-
- ld.setDrawableByLayerId(R.id.user_avatar, drawable)
- return ld
- }
-
- override fun notifyDataSetChanged() {
- super.notifyDataSetChanged()
- buildUserViews()
- }
- }
+ private val adapter: UserAdapter by lazy { UserAdapter() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.user_switcher_fullscreen)
- window.decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+ window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+ if (isUsingModernArchitecture()) {
+ Log.d(TAG, "Using modern architecture.")
+ val viewModel = ViewModelProvider(
+ this, viewModelFactory.get())[UserSwitcherViewModel::class.java]
+ UserSwitcherViewBinder.bind(
+ view = requireViewById(R.id.user_switcher_root),
+ viewModel = viewModel,
+ lifecycleOwner = this,
+ layoutInflater = layoutInflater,
+ falsingCollector = falsingCollector,
+ onFinish = this::finish,
+ )
+ return
+ } else {
+ Log.d(TAG, "Not using modern architecture.")
+ }
parent = requireViewById<UserSwitcherRootView>(R.id.user_switcher_root)
@@ -346,11 +266,18 @@ class UserSwitcherActivity @Inject constructor(
}
override fun onBackPressed() {
+ if (isUsingModernArchitecture()) {
+ return super.onBackPressed()
+ }
+
finish()
}
override fun onDestroy() {
super.onDestroy()
+ if (isUsingModernArchitecture()) {
+ return
+ }
broadcastDispatcher.unregisterReceiver(broadcastReceiver)
userTracker.removeCallback(userSwitchedCallback)
@@ -376,6 +303,10 @@ class UserSwitcherActivity @Inject constructor(
return if (userCount < 5) 4 else ceil(userCount / 2.0).toInt()
}
+ private fun isUsingModernArchitecture(): Boolean {
+ return flags.isEnabled(Flags.MODERN_USER_SWITCHER_ACTIVITY)
+ }
+
private class ItemAdapter(
val parentContext: Context,
val resource: Int,
@@ -398,4 +329,114 @@ class UserSwitcherActivity @Inject constructor(
return view
}
}
+
+ private inner class UserAdapter : BaseUserAdapter(userSwitcherController) {
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val item = getItem(position)
+ var view = convertView as ViewGroup?
+ if (view == null) {
+ view = layoutInflater.inflate(
+ R.layout.user_switcher_fullscreen_item,
+ parent,
+ false
+ ) as ViewGroup
+ }
+ (view.getChildAt(0) as ImageView).apply {
+ setImageDrawable(getDrawable(item))
+ }
+ (view.getChildAt(1) as TextView).apply {
+ setText(getName(getContext(), item))
+ }
+
+ view.setEnabled(item.isSwitchToEnabled)
+ view.setAlpha(
+ if (view.isEnabled()) {
+ USER_SWITCH_ENABLED_ALPHA
+ } else {
+ USER_SWITCH_DISABLED_ALPHA
+ }
+ )
+ view.setTag(USER_VIEW)
+ return view
+ }
+
+ override fun getName(context: Context, item: UserRecord): String {
+ return if (item == manageUserRecord) {
+ getString(R.string.manage_users)
+ } else {
+ super.getName(context, item)
+ }
+ }
+
+ fun findUserIcon(item: UserRecord): Drawable {
+ if (item == manageUserRecord) {
+ return getDrawable(R.drawable.ic_manage_users)
+ }
+ if (item.info == null) {
+ return getIconDrawable(this@UserSwitcherActivity, item)
+ }
+ val userIcon = userManager.getUserIcon(item.info.id)
+ if (userIcon != null) {
+ return BitmapDrawable(userIcon)
+ }
+ return UserIcons.getDefaultUserIcon(resources, item.info.id, false)
+ }
+
+ fun getTotalUserViews(): Int {
+ return users.count { item ->
+ !doNotRenderUserView(item)
+ }
+ }
+
+ fun doNotRenderUserView(item: UserRecord): Boolean {
+ return item.isAddUser ||
+ item.isAddSupervisedUser ||
+ item.isGuest && item.info == null
+ }
+
+ private fun getDrawable(item: UserRecord): Drawable {
+ var drawable = if (item.isGuest) {
+ getDrawable(R.drawable.ic_account_circle)
+ } else {
+ findUserIcon(item)
+ }
+ drawable.mutate()
+
+ if (!item.isCurrent && !item.isSwitchToEnabled) {
+ drawable.setTint(
+ resources.getColor(
+ R.color.kg_user_switcher_restricted_avatar_icon_color,
+ getTheme()
+ )
+ )
+ }
+
+ val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate()
+ as LayerDrawable
+ if (item == userSwitcherController.getCurrentUserRecord()) {
+ (ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply {
+ val stroke = resources
+ .getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width)
+ val color = Utils.getColorAttrDefaultColor(
+ this@UserSwitcherActivity,
+ com.android.internal.R.attr.colorAccentPrimary
+ )
+
+ setStroke(stroke, color)
+ }
+ }
+
+ ld.setDrawableByLayerId(R.id.user_avatar, drawable)
+ return ld
+ }
+
+ override fun notifyDataSetChanged() {
+ super.notifyDataSetChanged()
+ buildUserViews()
+ }
+ }
+
+ companion object {
+ private const val TAG = "UserSwitcherActivity"
+ }
}
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
new file mode 100644
index 000000000000..305b5ee920a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.data.repository
+
+import android.content.Context
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.os.UserManager
+import androidx.appcompat.content.res.AppCompatResources
+import com.android.internal.util.UserIcons
+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.common.shared.model.Text
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * Acts as source of truth for user related data.
+ *
+ * Abstracts-away data sources and their schemas so the rest of the app doesn't need to worry about
+ * upstream changes.
+ */
+interface UserRepository {
+ /** List of all users on the device. */
+ val users: Flow<List<UserModel>>
+
+ /** The currently-selected user. */
+ val selectedUser: Flow<UserModel>
+
+ /** List of available user-related actions. */
+ val actions: Flow<List<UserActionModel>>
+
+ /** Whether actions are available even when locked. */
+ val isActionableWhenLocked: Flow<Boolean>
+
+ /** Whether the device is configured to always have a guest user available. */
+ val isGuestUserAutoCreated: Boolean
+
+ /** Whether the guest user is currently being reset. */
+ val isGuestUserResetting: Boolean
+}
+
+@SysUISingleton
+class UserRepositoryImpl
+@Inject
+constructor(
+ @Application private val appContext: Context,
+ private val manager: UserManager,
+ controller: UserSwitcherController,
+) : UserRepository {
+
+ private val userRecords: Flow<List<UserRecord>> = conflatedCallbackFlow {
+ fun send() {
+ trySendWithFailureLogging(
+ controller.users,
+ TAG,
+ )
+ }
+
+ val callback = UserSwitcherController.UserSwitchCallback { send() }
+
+ controller.addUserSwitchCallback(callback)
+ send()
+
+ awaitClose { controller.removeUserSwitchCallback(callback) }
+ }
+
+ override val users: Flow<List<UserModel>> =
+ userRecords.map { records -> records.filter { it.isUser() }.map { it.toUserModel() } }
+
+ override val selectedUser: Flow<UserModel> =
+ users.map { users -> users.first { user -> user.isSelected } }
+
+ override val actions: Flow<List<UserActionModel>> =
+ userRecords.map { records -> records.filter { it.isNotUser() }.map { it.toActionModel() } }
+
+ override val isActionableWhenLocked: Flow<Boolean> = controller.addUsersFromLockScreen
+
+ override val isGuestUserAutoCreated: Boolean = controller.isGuestUserAutoCreated
+
+ override val isGuestUserResetting: Boolean = controller.isGuestUserResetting
+
+ private fun UserRecord.isUser(): Boolean {
+ return when {
+ isAddUser -> false
+ isAddSupervisedUser -> false
+ isGuest -> info != null
+ else -> true
+ }
+ }
+
+ private fun UserRecord.isNotUser(): Boolean {
+ return !isUser()
+ }
+
+ private fun UserRecord.toUserModel(): UserModel {
+ return UserModel(
+ id = resolveId(),
+ name = getUserName(this),
+ image = getUserImage(this),
+ isSelected = isCurrent,
+ isSelectable = isSwitchToEnabled || isGuest,
+ )
+ }
+
+ private fun UserRecord.toActionModel(): UserActionModel {
+ return when {
+ isAddUser -> UserActionModel.ADD_USER
+ isAddSupervisedUser -> UserActionModel.ADD_SUPERVISED_USER
+ isGuest -> UserActionModel.ENTER_GUEST_MODE
+ else -> error("Don't know how to convert to UserActionModel: $this")
+ }
+ }
+
+ private fun getUserName(record: UserRecord): Text {
+ val resourceId: Int? = LegacyUserUiHelper.getGuestUserRecordNameResourceId(record)
+ return if (resourceId != null) {
+ Text.Resource(resourceId)
+ } else {
+ Text.Loaded(checkNotNull(record.info).name)
+ }
+ }
+
+ private fun getUserImage(record: UserRecord): Drawable {
+ if (record.isGuest) {
+ return checkNotNull(
+ AppCompatResources.getDrawable(appContext, R.drawable.ic_account_circle)
+ )
+ }
+
+ val userId = checkNotNull(record.info?.id)
+ return manager.getUserIcon(userId)?.let { userSelectedIcon ->
+ BitmapDrawable(userSelectedIcon)
+ }
+ ?: UserIcons.getDefaultUserIcon(appContext.resources, userId, /* light= */ false)
+ }
+
+ companion object {
+ private const val TAG = "UserRepository"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt
new file mode 100644
index 000000000000..18ae1070e1bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface UserRepositoryModule {
+ @Binds fun bindRepository(impl: UserRepositoryImpl): UserRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt b/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
index 6ab6d7d7891a..cf6da9a60d78 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
@@ -20,38 +20,29 @@ import android.content.pm.UserInfo
import android.graphics.Bitmap
import android.os.UserHandle
-/**
- * Encapsulates raw data for a user or an option item related to managing users on the device.
- */
+/** Encapsulates raw data for a user or an option item related to managing users on the device. */
data class UserRecord(
/** Relevant user information. If `null`, this record is not a user but an option item. */
- @JvmField
- val info: UserInfo?,
+ @JvmField val info: UserInfo? = null,
/** An image representing the user. */
- @JvmField
- val picture: Bitmap?,
+ @JvmField val picture: Bitmap? = null,
/** Whether this record represents an option to switch to a guest user. */
- @JvmField
- val isGuest: Boolean,
+ @JvmField val isGuest: Boolean = false,
/** Whether this record represents the currently-selected user. */
- @JvmField
- val isCurrent: Boolean,
+ @JvmField val isCurrent: Boolean = false,
/** Whether this record represents an option to add another user to the device. */
- @JvmField
- val isAddUser: Boolean,
- /** If true, the record is only visible to the owner and only when unlocked. */
- @JvmField
- val isRestricted: Boolean,
+ @JvmField val isAddUser: Boolean = false,
+ /**
+ * If true, the record is only available if unlocked or if the user has granted permission to
+ * access this user action whilst on the device is locked.
+ */
+ @JvmField val isRestricted: Boolean = false,
/** Whether it is possible to switch to this user. */
- @JvmField
- val isSwitchToEnabled: Boolean,
+ @JvmField val isSwitchToEnabled: Boolean = false,
/** Whether this record represents an option to add another supervised user to the device. */
- @JvmField
- val isAddSupervisedUser: Boolean,
+ @JvmField val isAddSupervisedUser: Boolean = false,
) {
- /**
- * Returns a new instance of [UserRecord] with its [isCurrent] set to the given value.
- */
+ /** Returns a new instance of [UserRecord] with its [isCurrent] set to the given value. */
fun copyWithIsCurrent(isCurrent: Boolean): UserRecord {
return copy(isCurrent = isCurrent)
}
@@ -67,4 +58,11 @@ data class UserRecord(
info.id
}
}
+
+ companion object {
+ @JvmStatic
+ fun createForGuest(): UserRecord {
+ return UserRecord(isGuest = true)
+ }
+ }
}
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
new file mode 100644
index 000000000000..3c5b9697c013
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates business logic to interact with user data and systems. */
+@SysUISingleton
+class UserInteractor
+@Inject
+constructor(
+ repository: UserRepository,
+ private val controller: UserSwitcherController,
+ private val activityStarter: ActivityStarter,
+ keyguardInteractor: KeyguardInteractor,
+) {
+ /** List of current on-device users to select from. */
+ val users: Flow<List<UserModel>> = repository.users
+
+ /** The currently-selected user. */
+ val selectedUser: Flow<UserModel> = repository.selectedUser
+
+ /** List of user-switcher related actions that are available. */
+ val actions: Flow<List<UserActionModel>> =
+ combine(
+ repository.isActionableWhenLocked,
+ keyguardInteractor.isKeyguardShowing,
+ ) { isActionableWhenLocked, isLocked ->
+ isActionableWhenLocked || !isLocked
+ }
+ .flatMapLatest { isActionable ->
+ if (isActionable) {
+ repository.actions.map { actions ->
+ actions +
+ if (actions.isNotEmpty()) {
+ // If we have actions, we add NAVIGATE_TO_USER_MANAGEMENT because
+ // that's a user
+ // switcher specific action that is not known to the our data source
+ // or other
+ // features.
+ listOf(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
+ } else {
+ // If no actions, don't add the navigate action.
+ emptyList()
+ }
+ }
+ } else {
+ // If not actionable it means that we're not allowed to show actions when locked
+ // and we
+ // are locked. Therefore, we should show no actions.
+ flowOf(emptyList())
+ }
+ }
+
+ /** Whether the device is configured to always have a guest user available. */
+ val isGuestUserAutoCreated: Boolean = repository.isGuestUserAutoCreated
+
+ /** Whether the guest user is currently being reset. */
+ val isGuestUserResetting: Boolean = repository.isGuestUserResetting
+
+ /** Switches to the user with the given user ID. */
+ fun selectUser(
+ userId: Int,
+ ) {
+ controller.onUserSelected(userId, /* dialogShower= */ null)
+ }
+
+ /** Executes the given action. */
+ fun executeAction(action: UserActionModel) {
+ when (action) {
+ UserActionModel.ENTER_GUEST_MODE -> controller.createAndSwitchToGuestUser(null)
+ UserActionModel.ADD_USER -> controller.showAddUserDialog(null)
+ UserActionModel.ADD_SUPERVISED_USER -> controller.startSupervisedUserActivity()
+ UserActionModel.NAVIGATE_TO_USER_MANAGEMENT ->
+ activityStarter.startActivity(
+ Intent(Settings.ACTION_USER_SETTINGS),
+ /* dismissShade= */ false,
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
new file mode 100644
index 000000000000..18369d9a71d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.legacyhelper.ui
+
+import android.content.Context
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import com.android.systemui.R
+import com.android.systemui.user.data.source.UserRecord
+import kotlin.math.ceil
+
+/**
+ * Defines utility functions for helping with legacy UI code for users.
+ *
+ * We need these to avoid code duplication between logic inside the UserSwitcherController and in
+ * modern architecture classes such as repositories, interactors, and view-models. If we ever
+ * simplify UserSwitcherController (or delete it), the code here could be moved into its call-sites.
+ */
+object LegacyUserUiHelper {
+
+ /** Returns the maximum number of columns for user items in the user switcher. */
+ fun getMaxUserSwitcherItemColumns(userCount: Int): Int {
+ // TODO(b/243844097): remove this once we remove the old user switcher implementation.
+ return if (userCount < 5) {
+ 4
+ } else {
+ ceil(userCount / 2.0).toInt()
+ }
+ }
+
+ @JvmStatic
+ @DrawableRes
+ fun getUserSwitcherActionIconResourceId(
+ isAddUser: Boolean,
+ isGuest: Boolean,
+ isAddSupervisedUser: Boolean,
+ ): Int {
+ return if (isAddUser) {
+ R.drawable.ic_add
+ } else if (isGuest) {
+ R.drawable.ic_account_circle
+ } else if (isAddSupervisedUser) {
+ R.drawable.ic_add_supervised_user
+ } else {
+ R.drawable.ic_avatar_user
+ }
+ }
+
+ @JvmStatic
+ fun getUserRecordName(
+ context: Context,
+ record: UserRecord,
+ isGuestUserAutoCreated: Boolean,
+ isGuestUserResetting: Boolean,
+ ): String {
+ val resourceId: Int? = getGuestUserRecordNameResourceId(record)
+ return when {
+ resourceId != null -> context.getString(resourceId)
+ record.info != null -> record.info.name
+ else ->
+ context.getString(
+ getUserSwitcherActionTextResourceId(
+ isGuest = record.isGuest,
+ isGuestUserAutoCreated = isGuestUserAutoCreated,
+ isGuestUserResetting = isGuestUserResetting,
+ isAddUser = record.isAddUser,
+ isAddSupervisedUser = record.isAddSupervisedUser,
+ )
+ )
+ }
+ }
+
+ /**
+ * Returns the resource ID for a string for the name of the guest user.
+ *
+ * If the given record is not the guest user, returns `null`.
+ */
+ @StringRes
+ fun getGuestUserRecordNameResourceId(record: UserRecord): Int? {
+ return when {
+ record.isGuest && record.isCurrent ->
+ com.android.settingslib.R.string.guest_exit_quick_settings_button
+ record.isGuest && record.info != null -> com.android.internal.R.string.guest_name
+ else -> null
+ }
+ }
+
+ @JvmStatic
+ @StringRes
+ fun getUserSwitcherActionTextResourceId(
+ isGuest: Boolean,
+ isGuestUserAutoCreated: Boolean,
+ isGuestUserResetting: Boolean,
+ isAddUser: Boolean,
+ isAddSupervisedUser: Boolean,
+ ): Int {
+ check(isGuest || isAddUser || isAddSupervisedUser)
+
+ return when {
+ isGuest && isGuestUserAutoCreated && isGuestUserResetting ->
+ com.android.settingslib.R.string.guest_resetting
+ isGuest && isGuestUserAutoCreated -> com.android.internal.R.string.guest_name
+ isGuest -> com.android.internal.R.string.guest_name
+ isAddUser -> com.android.settingslib.R.string.user_add_user
+ isAddSupervisedUser -> R.string.add_user_supervised
+ else -> error("This should never happen!")
+ }
+ }
+
+ /** Alpha value to apply to a user view in the user switcher when it's selectable. */
+ const val USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA = 1.0f
+
+ /** Alpha value to apply to a user view in the user switcher when it's not selectable. */
+ const val USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA = 0.38f
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt
new file mode 100644
index 000000000000..823bf74dc0f0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.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.user.shared.model
+
+enum class UserActionModel {
+ ENTER_GUEST_MODE,
+ ADD_USER,
+ ADD_SUPERVISED_USER,
+ NAVIGATE_TO_USER_MANAGEMENT,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/shared/model/UserModel.kt b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserModel.kt
new file mode 100644
index 000000000000..bf7977a600e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserModel.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.user.shared.model
+
+import android.graphics.drawable.Drawable
+import com.android.systemui.common.shared.model.Text
+
+/** Represents a single user on the device. */
+data class UserModel(
+ /** ID of the user, unique across all users on this device. */
+ val id: Int,
+ /** Human-facing name for this user. */
+ val name: Text,
+ /** Human-facing image for this user. */
+ val image: Drawable,
+ /** Whether this user is the currently-selected user. */
+ val isSelected: Boolean,
+ /** Whether this use is selectable. A non-selectable user cannot be switched to. */
+ val isSelectable: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
new file mode 100644
index 000000000000..83a3d0d0457a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.ui.binder
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseAdapter
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.constraintlayout.helper.widget.Flow as FlowWidget
+import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Gefingerpoken
+import com.android.systemui.R
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.user.UserSwitcherPopupMenu
+import com.android.systemui.user.UserSwitcherRootView
+import com.android.systemui.user.ui.viewmodel.UserActionViewModel
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import com.android.systemui.util.children
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
+
+/** Binds a user switcher to its view-model. */
+object UserSwitcherViewBinder {
+
+ private const val USER_VIEW_TAG = "user_view"
+
+ /** Binds the given view to the given view-model. */
+ fun bind(
+ view: ViewGroup,
+ viewModel: UserSwitcherViewModel,
+ lifecycleOwner: LifecycleOwner,
+ layoutInflater: LayoutInflater,
+ falsingCollector: FalsingCollector,
+ onFinish: () -> Unit,
+ ) {
+ val rootView: UserSwitcherRootView = view.requireViewById(R.id.user_switcher_root)
+ val flowWidget: FlowWidget = view.requireViewById(R.id.flow)
+ val addButton: View = view.requireViewById(R.id.add)
+ val cancelButton: View = view.requireViewById(R.id.cancel)
+ val popupMenuAdapter = MenuAdapter(layoutInflater)
+ var popupMenu: UserSwitcherPopupMenu? = null
+
+ rootView.touchHandler =
+ object : Gefingerpoken {
+ override fun onTouchEvent(ev: MotionEvent?): Boolean {
+ falsingCollector.onTouchEvent(ev)
+ return false
+ }
+ }
+ addButton.setOnClickListener { viewModel.onOpenMenuButtonClicked() }
+ cancelButton.setOnClickListener { viewModel.onCancelButtonClicked() }
+
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ viewModel.isFinishRequested
+ .filter { it }
+ .collect {
+ onFinish()
+ viewModel.onFinished()
+ }
+ }
+ }
+ }
+
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch { viewModel.isOpenMenuButtonVisible.collect { addButton.isVisible = it } }
+
+ launch {
+ viewModel.isMenuVisible.collect { isVisible ->
+ if (isVisible && popupMenu?.isShowing != true) {
+ popupMenu?.dismiss()
+ // Use post to make sure we show the popup menu *after* the activity is
+ // ready to show one to avoid a WindowManager$BadTokenException.
+ view.post {
+ popupMenu =
+ createAndShowPopupMenu(
+ context = view.context,
+ anchorView = addButton,
+ adapter = popupMenuAdapter,
+ onDismissed = viewModel::onMenuClosed,
+ )
+ }
+ } else if (!isVisible && popupMenu?.isShowing == true) {
+ popupMenu?.dismiss()
+ popupMenu = null
+ }
+ }
+ }
+
+ launch {
+ viewModel.menu.collect { menuViewModels ->
+ popupMenuAdapter.setItems(menuViewModels)
+ }
+ }
+
+ launch {
+ viewModel.maximumUserColumns.collect { maximumColumns ->
+ flowWidget.setMaxElementsWrap(maximumColumns)
+ }
+ }
+
+ launch {
+ viewModel.users.collect { users ->
+ val viewPool =
+ view.children.filter { it.tag == USER_VIEW_TAG }.toMutableList()
+ viewPool.forEach { view.removeView(it) }
+ users.forEach { userViewModel ->
+ val userView =
+ if (viewPool.isNotEmpty()) {
+ viewPool.removeAt(0)
+ } else {
+ val inflatedView =
+ layoutInflater.inflate(
+ R.layout.user_switcher_fullscreen_item,
+ view,
+ false,
+ )
+ inflatedView.tag = USER_VIEW_TAG
+ inflatedView
+ }
+ userView.id = View.generateViewId()
+ view.addView(userView)
+ flowWidget.addView(userView)
+ UserViewBinder.bind(
+ view = userView,
+ viewModel = userViewModel,
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun createAndShowPopupMenu(
+ context: Context,
+ anchorView: View,
+ adapter: MenuAdapter,
+ onDismissed: () -> Unit,
+ ): UserSwitcherPopupMenu {
+ return UserSwitcherPopupMenu(context).apply {
+ this.anchorView = anchorView
+ setAdapter(adapter)
+ setOnDismissListener { onDismissed() }
+ setOnItemClickListener { _, _, position, _ ->
+ val itemPositionExcludingHeader = position - 1
+ adapter.getItem(itemPositionExcludingHeader).onClicked()
+ }
+
+ show()
+ }
+ }
+
+ /** Adapter for the menu that can be opened. */
+ private class MenuAdapter(
+ private val layoutInflater: LayoutInflater,
+ ) : BaseAdapter() {
+
+ private val items = mutableListOf<UserActionViewModel>()
+
+ override fun getCount(): Int {
+ return items.size
+ }
+
+ override fun getItem(position: Int): UserActionViewModel {
+ return items[position]
+ }
+
+ override fun getItemId(position: Int): Long {
+ return getItem(position).viewKey
+ }
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view =
+ convertView
+ ?: layoutInflater.inflate(
+ R.layout.user_switcher_fullscreen_popup_item,
+ parent,
+ false
+ )
+ val viewModel = getItem(position)
+ view.requireViewById<ImageView>(R.id.icon).setImageResource(viewModel.iconResourceId)
+ view.requireViewById<TextView>(R.id.text).text =
+ view.resources.getString(viewModel.textResourceId)
+ return view
+ }
+
+ fun setItems(items: List<UserActionViewModel>) {
+ this.items.clear()
+ this.items.addAll(items)
+ notifyDataSetChanged()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt
new file mode 100644
index 000000000000..e78807e675b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserViewBinder.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.ui.binder
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.LayerDrawable
+import android.view.View
+import android.widget.ImageView
+import androidx.core.content.res.ResourcesCompat
+import com.android.settingslib.Utils
+import com.android.systemui.R
+import com.android.systemui.common.ui.binder.TextViewBinder
+import com.android.systemui.user.ui.viewmodel.UserViewModel
+
+/** Binds a user view to its view-model. */
+object UserViewBinder {
+ /** Binds the given view to the given view-model. */
+ fun bind(view: View, viewModel: UserViewModel) {
+ TextViewBinder.bind(view.requireViewById(R.id.user_switcher_text), viewModel.name)
+ view
+ .requireViewById<ImageView>(R.id.user_switcher_icon)
+ .setImageDrawable(getSelectableDrawable(view.context, viewModel))
+ view.alpha = viewModel.alpha
+ if (viewModel.onClicked != null) {
+ view.setOnClickListener { viewModel.onClicked.invoke() }
+ } else {
+ view.setOnClickListener(null)
+ }
+ }
+
+ private fun getSelectableDrawable(context: Context, viewModel: UserViewModel): Drawable {
+ val layerDrawable =
+ checkNotNull(
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.user_switcher_icon_large,
+ context.theme,
+ )
+ )
+ .mutate() as LayerDrawable
+ if (viewModel.isSelectionMarkerVisible) {
+ (layerDrawable.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply {
+ val stroke =
+ context.resources.getDimensionPixelSize(
+ R.dimen.user_switcher_icon_selected_width
+ )
+ val color =
+ Utils.getColorAttrDefaultColor(
+ context,
+ com.android.internal.R.attr.colorAccentPrimary
+ )
+
+ setStroke(stroke, color)
+ }
+ }
+
+ layerDrawable.setDrawableByLayerId(R.id.user_avatar, viewModel.image)
+ return layerDrawable
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserActionViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserActionViewModel.kt
new file mode 100644
index 000000000000..149b1ffdaff0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserActionViewModel.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.user.ui.viewmodel
+
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+
+/** Models UI state for an action that can be performed on a user. */
+data class UserActionViewModel(
+ /**
+ * Key to use with the view or compose system to keep track of the view/composable across
+ * changes to the collection of [UserActionViewModel] instances.
+ */
+ val viewKey: Long,
+ @DrawableRes val iconResourceId: Int,
+ @StringRes val textResourceId: Int,
+ val onClicked: () -> Unit,
+)
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
new file mode 100644
index 000000000000..66ce01b7a86e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.ui.viewmodel
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.R
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+/** Models UI state for the user switcher feature. */
+class UserSwitcherViewModel
+private constructor(
+ private val userInteractor: UserInteractor,
+ private val powerInteractor: PowerInteractor,
+) : ViewModel() {
+
+ /** On-device users. */
+ val users: Flow<List<UserViewModel>> =
+ userInteractor.users.map { models -> models.map { user -> toViewModel(user) } }
+
+ /** The maximum number of columns that the user selection grid should use. */
+ val maximumUserColumns: Flow<Int> =
+ users.map { LegacyUserUiHelper.getMaxUserSwitcherItemColumns(it.size) }
+
+ /** Whether the button to open the user action menu is visible. */
+ val isOpenMenuButtonVisible: Flow<Boolean> = userInteractor.actions.map { it.isNotEmpty() }
+
+ private val _isMenuVisible = MutableStateFlow(false)
+ /**
+ * Whether the user action menu should be shown. Once the action menu is dismissed/closed, the
+ * consumer must invoke [onMenuClosed].
+ */
+ val isMenuVisible: Flow<Boolean> = _isMenuVisible
+ /** The user action menu. */
+ val menu: Flow<List<UserActionViewModel>> =
+ userInteractor.actions.map { actions -> actions.map { action -> toViewModel(action) } }
+
+ private val hasCancelButtonBeenClicked = MutableStateFlow(false)
+
+ /**
+ * Whether the observer should finish the experience. Once consumed, [onFinished] must be called
+ * by the consumer.
+ */
+ val isFinishRequested: Flow<Boolean> = createFinishRequestedFlow()
+
+ /** Notifies that the user has clicked the cancel button. */
+ fun onCancelButtonClicked() {
+ hasCancelButtonBeenClicked.value = true
+ }
+
+ /**
+ * Notifies that the user experience is finished.
+ *
+ * Call this after consuming [isFinishRequested] with a `true` value in order to mark it as
+ * consumed such that the next consumer doesn't immediately finish itself.
+ */
+ fun onFinished() {
+ hasCancelButtonBeenClicked.value = false
+ }
+
+ /** Notifies that the user has clicked the "open menu" button. */
+ fun onOpenMenuButtonClicked() {
+ _isMenuVisible.value = true
+ }
+
+ /**
+ * Notifies that the user has dismissed or closed the user action menu.
+ *
+ * Call this after consuming [isMenuVisible] with a `true` value in order to reset it to `false`
+ * such that the next consumer doesn't immediately show the menu again.
+ */
+ fun onMenuClosed() {
+ _isMenuVisible.value = false
+ }
+
+ private fun createFinishRequestedFlow(): Flow<Boolean> {
+ var mostRecentSelectedUserId: Int? = null
+ var mostRecentIsInteractive: Boolean? = null
+
+ return combine(
+ // When the user is switched, we should finish.
+ userInteractor.selectedUser
+ .map { it.id }
+ .map {
+ val selectedUserChanged =
+ mostRecentSelectedUserId != null && mostRecentSelectedUserId != it
+ mostRecentSelectedUserId = it
+ selectedUserChanged
+ },
+ // When the screen turns off, we should finish.
+ powerInteractor.isInteractive.map {
+ val screenTurnedOff = mostRecentIsInteractive == true && !it
+ mostRecentIsInteractive = it
+ screenTurnedOff
+ },
+ // When the cancel button is clicked, we should finish.
+ hasCancelButtonBeenClicked,
+ ) { selectedUserChanged, screenTurnedOff, cancelButtonClicked ->
+ selectedUserChanged || screenTurnedOff || cancelButtonClicked
+ }
+ }
+
+ private fun toViewModel(
+ model: UserModel,
+ ): UserViewModel {
+ return UserViewModel(
+ viewKey = model.id,
+ name = model.name,
+ image = model.image,
+ isSelectionMarkerVisible = model.isSelected,
+ alpha =
+ if (model.isSelectable) {
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA
+ } else {
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA
+ },
+ onClicked = createOnSelectedCallback(model),
+ )
+ }
+
+ private fun toViewModel(
+ model: UserActionModel,
+ ): UserActionViewModel {
+ return UserActionViewModel(
+ viewKey = model.ordinal.toLong(),
+ iconResourceId =
+ if (model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) {
+ R.drawable.ic_manage_users
+ } else {
+ LegacyUserUiHelper.getUserSwitcherActionIconResourceId(
+ isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER,
+ isAddUser = model == UserActionModel.ADD_USER,
+ isGuest = model == UserActionModel.ENTER_GUEST_MODE,
+ )
+ },
+ textResourceId =
+ if (model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) {
+ R.string.manage_users
+ } else {
+ LegacyUserUiHelper.getUserSwitcherActionTextResourceId(
+ isGuest = model == UserActionModel.ENTER_GUEST_MODE,
+ isGuestUserAutoCreated = userInteractor.isGuestUserAutoCreated,
+ isGuestUserResetting = userInteractor.isGuestUserResetting,
+ isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER,
+ isAddUser = model == UserActionModel.ADD_USER,
+ )
+ },
+ onClicked = { userInteractor.executeAction(action = model) },
+ )
+ }
+
+ private fun createOnSelectedCallback(model: UserModel): (() -> Unit)? {
+ return if (!model.isSelectable) {
+ null
+ } else {
+ { userInteractor.selectUser(model.id) }
+ }
+ }
+
+ class Factory
+ @Inject
+ constructor(
+ private val userInteractor: UserInteractor,
+ private val powerInteractor: PowerInteractor,
+ ) : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST")
+ return UserSwitcherViewModel(
+ userInteractor = userInteractor,
+ powerInteractor = powerInteractor,
+ )
+ as T
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserViewModel.kt
new file mode 100644
index 000000000000..d57bba0fa86a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserViewModel.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.ui.viewmodel
+
+import android.graphics.drawable.Drawable
+import com.android.systemui.common.shared.model.Text
+
+/** Models UI state for representing a single user. */
+data class UserViewModel(
+ /**
+ * Key to use with the view or compose system to keep track of the view/composable across
+ * changes to the collection of [UserViewModel] instances.
+ */
+ val viewKey: Int,
+ val name: Text,
+ val image: Drawable,
+ /** Whether a marker should be shown to highlight that this user is the selected one. */
+ val isSelectionMarkerVisible: Boolean,
+ val alpha: Float,
+ val onClicked: (() -> Unit)?,
+)
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 7baebf4ef600..f71d596ff835 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -16,11 +16,15 @@
package com.android.systemui.util.kotlin
+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.drop
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.zip
+import kotlinx.coroutines.launch
/**
* Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
@@ -29,15 +33,16 @@ import kotlinx.coroutines.flow.zip
*
* Useful for code that needs to compare the current value to the previous value.
*/
-fun <T, R> Flow<T>.pairwiseBy(transform: suspend (old: T, new: T) -> R): Flow<R> {
- // same as current flow, but with the very first event skipped
- val nextEvents = drop(1)
- // zip current flow and nextEvents; transform will receive a pair of old and new value. This
- // works because zip will suppress emissions until both flows have emitted something; since in
- // this case both flows are emitting at the same rate, but the current flow just has one extra
- // thing emitted at the start, the effect is that zip will cache the most recent value while
- // waiting for the next emission from nextEvents.
- return zip(nextEvents, transform)
+fun <T, R> Flow<T>.pairwiseBy(transform: suspend (old: T, new: T) -> R): Flow<R> = flow {
+ val noVal = Any()
+ var previousValue: Any? = noVal
+ collect { newVal ->
+ if (previousValue != noVal) {
+ @Suppress("UNCHECKED_CAST")
+ emit(transform(previousValue as T, newVal))
+ }
+ previousValue = newVal
+ }
}
/**
@@ -74,10 +79,19 @@ data class WithPrev<T>(val previousValue: T, val newValue: T)
/**
* Returns a new [Flow] that combines the [Set] changes between each emission from [this] using
* [transform].
+ *
+ * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
+ * a change event to be emitted that contains no removals, and all elements from that first [Set]
+ * as additions.
+ *
+ * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
+ * until a second [Set] has been emitted from the upstream [Flow].
*/
fun <T, R> Flow<Set<T>>.setChangesBy(
transform: suspend (removed: Set<T>, added: Set<T>) -> R,
-): Flow<R> = onStart { emit(emptySet()) }.distinctUntilChanged()
+ emitFirstEvent: Boolean = true,
+): Flow<R> = (if (emitFirstEvent) onStart { emit(emptySet()) } else this)
+ .distinctUntilChanged()
.pairwiseBy { old: Set<T>, new: Set<T> ->
// If an element was present in the old set, but not the new one, then it was removed
val removed = old - new
@@ -86,8 +100,18 @@ fun <T, R> Flow<Set<T>>.setChangesBy(
transform(removed, added)
}
-/** Returns a new [Flow] that produces the [Set] changes between each emission from [this]. */
-fun <T> Flow<Set<T>>.setChanges(): Flow<SetChanges<T>> = setChangesBy(::SetChanges)
+/**
+ * Returns a new [Flow] that produces the [Set] changes between each emission from [this].
+ *
+ * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
+ * a change event to be emitted that contains no removals, and all elements from that first [Set]
+ * as additions.
+ *
+ * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
+ * until a second [Set] has been emitted from the upstream [Flow].
+ */
+fun <T> Flow<Set<T>>.setChanges(emitFirstEvent: Boolean = true): Flow<SetChanges<T>> =
+ setChangesBy(::SetChanges, emitFirstEvent)
/** Contains the difference in elements between two [Set]s. */
data class SetChanges<T>(
@@ -96,3 +120,35 @@ data class SetChanges<T>(
/** Elements that are present in the second [Set] but not in the first. */
val added: Set<T>,
)
+
+/**
+ * Returns a new [Flow] that emits at the same rate as [this], but combines the emitted value with
+ * the most recent emission from [other] using [transform].
+ *
+ * Note that the returned Flow will not emit anything until [other] has emitted at least one value.
+ */
+fun <A, B, C> Flow<A>.sample(other: Flow<B>, transform: suspend (A, B) -> C): Flow<C> = flow {
+ coroutineScope {
+ val noVal = Any()
+ val sampledRef = AtomicReference(noVal)
+ val job = launch(Dispatchers.Unconfined) {
+ other.collect { sampledRef.set(it) }
+ }
+ collect {
+ val sampled = sampledRef.get()
+ if (sampled != noVal) {
+ @Suppress("UNCHECKED_CAST")
+ emit(transform(it, sampled as B))
+ }
+ }
+ job.cancel()
+ }
+}
+
+/**
+ * Returns a new [Flow] that emits at the same rate as [this], but emits the most recently emitted
+ * value from [other] instead.
+ *
+ * Note that the returned Flow will not emit anything until [other] has emitted at least one value.
+ */
+fun <A> Flow<*>.sample(other: Flow<A>): Flow<A> = sample(other) { _, a -> a }
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt
new file mode 100644
index 000000000000..2e551f1e1bee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.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.util.kotlin
+
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Runs the given [blocks] in parallel, returning the result of the first one to complete, and
+ * cancelling all others.
+ */
+suspend fun <R> race(vararg blocks: suspend () -> R): R = coroutineScope {
+ val completion = CompletableDeferred<R>()
+ val raceJob = launch {
+ for (block in blocks) {
+ launch { completion.complete(block()) }
+ }
+ }
+ completion.await().also { raceJob.cancel() }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
index 2c74fb911688..87a167baf9bf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
@@ -52,6 +52,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.MediaOutputConstants;
import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.util.ArrayList;
@@ -69,6 +70,7 @@ public class VolumePanelDialog extends SystemUIDialog implements LifecycleOwner
private static final int DURATION_SLICE_BINDING_TIMEOUT_MS = 200;
private static final int DEFAULT_SLICE_SIZE = 4;
+ private final ActivityStarter mActivityStarter;
private RecyclerView mVolumePanelSlices;
private VolumePanelSlicesAdapter mVolumePanelSlicesAdapter;
private final LifecycleRegistry mLifecycleRegistry;
@@ -78,8 +80,10 @@ public class VolumePanelDialog extends SystemUIDialog implements LifecycleOwner
private boolean mSlicesReadyToLoad;
private LocalBluetoothProfileManager mProfileManager;
- public VolumePanelDialog(Context context, boolean aboveStatusBar) {
+ public VolumePanelDialog(Context context,
+ ActivityStarter activityStarter, boolean aboveStatusBar) {
super(context);
+ mActivityStarter = activityStarter;
mLifecycleRegistry = new LifecycleRegistry(this);
if (!aboveStatusBar) {
getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
@@ -100,9 +104,11 @@ public class VolumePanelDialog extends SystemUIDialog implements LifecycleOwner
doneButton.setOnClickListener(v -> dismiss());
Button settingsButton = dialogView.findViewById(R.id.settings_button);
settingsButton.setOnClickListener(v -> {
- getContext().startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS).addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK));
dismiss();
+
+ Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mActivityStarter.startActivity(intent, /* dismissShade= */ true);
});
LocalBluetoothManager localBluetoothManager = LocalBluetoothManager.getInstance(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
index c2fafbf9f55b..0debe0e4cd80 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
@@ -21,6 +21,7 @@ import android.util.Log
import android.view.View
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
import javax.inject.Inject
private const val TAG = "VolumePanelFactory"
@@ -33,6 +34,7 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
@SysUISingleton
class VolumePanelFactory @Inject constructor(
private val context: Context,
+ private val activityStarter: ActivityStarter,
private val dialogLaunchAnimator: DialogLaunchAnimator
) {
companion object {
@@ -45,7 +47,7 @@ class VolumePanelFactory @Inject constructor(
return
}
- val dialog = VolumePanelDialog(context, aboveStatusBar)
+ val dialog = VolumePanelDialog(context, activityStarter, aboveStatusBar)
volumePanelDialog = dialog
// Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 8c41374fd058..4e7751464564 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -311,18 +311,6 @@ public class BubblesManager {
}
@Override
- public void notifyMaybeCancelSummary(String key) {
- sysuiMainExecutor.execute(() -> {
- final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
- if (entry != null) {
- for (NotifCallback cb : mCallbacks) {
- cb.maybeCancelSummary(entry);
- }
- }
- });
- }
-
- @Override
public void updateNotificationBubbleButton(String key) {
sysuiMainExecutor.execute(() -> {
final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
@@ -646,15 +634,5 @@ public class BubblesManager {
* filtered from the shade.
*/
void invalidateNotifications(@NonNull String reason);
-
- /**
- * Called on a bubbled entry that has been removed when there are no longer
- * bubbled entries in its group.
- *
- * Checks whether its group has any other (non-bubbled) children. If it doesn't,
- * removes all remnants of the group's summary from the notification pipeline.
- * TODO: (b/145659174) Only old pipeline needs this - delete post-migration.
- */
- void maybeCancelSummary(@NonNull NotificationEntry entry);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
index 013c298d69d7..0a9c745525c2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
@@ -33,14 +33,14 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
-public class KeyguardMessageAreaTest extends SysuiTestCase {
+public class AuthKeyguardMessageAreaTest extends SysuiTestCase {
private KeyguardMessageArea mKeyguardMessageArea;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mKeyguardMessageArea = new KeyguardMessageArea(mContext, null);
- mKeyguardMessageArea.setBouncerShowing(true);
+ mKeyguardMessageArea = new AuthKeyguardMessageArea(mContext, null);
+ mKeyguardMessageArea.setIsVisible(true);
}
@Test
@@ -53,7 +53,7 @@ public class KeyguardMessageAreaTest extends SysuiTestCase {
@Test
public void testHiddenWhenBouncerHidden() {
- mKeyguardMessageArea.setBouncerShowing(false);
+ mKeyguardMessageArea.setIsVisible(false);
mKeyguardMessageArea.setVisibility(View.INVISIBLE);
mKeyguardMessageArea.setMessage("oobleck");
assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 90f7fda69663..8bbaf3dff1e5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -56,7 +56,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
@Mock
private PasswordTextView mPasswordEntry;
@Mock
- private KeyguardMessageArea mKeyguardMessageArea;
+ private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
@@ -85,7 +85,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1);
when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry);
when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true);
- when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area))
+ when(mAbsKeyInputView.requireViewById(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea);
mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index 8293cc21f3d1..69524e5a4537 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -89,8 +89,8 @@ public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
@Test
public void testSetBouncerVisible() {
- mMessageAreaController.setBouncerShowing(true);
- verify(mKeyguardMessageArea).setBouncerShowing(true);
+ mMessageAreaController.setIsVisible(true);
+ verify(mKeyguardMessageArea).setIsVisible(true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index ec856031c23c..b89dbd98968a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -64,9 +64,10 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
@Mock
lateinit var keyguardViewController: KeyguardViewController
@Mock
- private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+ private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
@Mock
- private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+ private lateinit var mKeyguardMessageAreaController:
+ KeyguardMessageAreaController<BouncerKeyguardMessageArea>
private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
@@ -74,7 +75,8 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
fun setup() {
MockitoAnnotations.initMocks(this)
Mockito.`when`(
- keyguardPasswordView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area)
+ keyguardPasswordView
+ .requireViewById<BouncerKeyguardMessageArea>(R.id.bouncer_message_area)
).thenReturn(mKeyguardMessageArea)
Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea))
.thenReturn(mKeyguardMessageAreaController)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 616a1056d161..3262a77b7711 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -66,10 +66,11 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
@Mock
- private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+ private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
@Mock
- private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+ private lateinit var mKeyguardMessageAreaController:
+ KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@Mock
private lateinit var mLockPatternView: LockPatternView
@@ -83,7 +84,8 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
fun setup() {
MockitoAnnotations.initMocks(this)
`when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
- `when`(mKeyguardPatternView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area))
+ `when`(mKeyguardPatternView
+ .requireViewById<BouncerKeyguardMessageArea>(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea)
`when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
.thenReturn(mLockPatternView)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 7bc8e8a722f0..97d556b04aa4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -51,7 +51,7 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
@Mock
private PasswordTextView mPasswordEntry;
@Mock
- private KeyguardMessageArea mKeyguardMessageArea;
+ private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
@@ -90,7 +90,7 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
when(mPinBasedInputView.getButtons()).thenReturn(mButtons);
- when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
+ when(mPinBasedInputView.requireViewById(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea);
when(mPinBasedInputView.findViewById(R.id.delete_button))
.thenReturn(mDeleteButton);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index d68e8bd36c40..c6ebaa8bb46c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -41,6 +41,7 @@ import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsetsController;
@@ -117,7 +118,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
@Mock
private KeyguardMessageAreaController mKeyguardMessageAreaController;
@Mock
- private KeyguardMessageArea mKeyguardMessageArea;
+ private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
private ConfigurationController mConfigurationController;
@Mock
@@ -163,9 +164,10 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
- mKeyguardPasswordView = spy(new KeyguardPasswordView(getContext()));
+ mKeyguardPasswordView = spy((KeyguardPasswordView) LayoutInflater.from(mContext).inflate(
+ R.layout.keyguard_password_view, null));
when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper);
- when(mKeyguardPasswordView.findViewById(R.id.keyguard_message_area))
+ when(mKeyguardPasswordView.requireViewById(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea);
when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
.thenReturn(mKeyguardMessageAreaController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt
new file mode 100644
index 000000000000..419fedf99c15
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BiometricMessageDeferralTest : SysuiTestCase() {
+
+ @Test
+ fun testProcessNoMessages_noDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf())
+
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testProcessNonDeferredMessages_noDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
+
+ // WHEN there are no deferred messages processed
+ for (i in 0..3) {
+ biometricMessageDeferral.processMessage(4, "test")
+ }
+
+ // THEN getDeferredMessage is null
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testAllProcessedMessagesWereDeferred() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1))
+
+ // WHEN all the processed messages are a deferred message
+ for (i in 0..3) {
+ biometricMessageDeferral.processMessage(1, "test")
+ }
+
+ // THEN deferredMessage will return the string associated with the deferred msgId
+ assertEquals("test", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testReturnsMostFrequentDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
+
+ // WHEN there's two msgId=1 processed and one msgId=2 processed
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(2, "msgId-2")
+
+ // THEN the most frequent deferred message is that meets the threshold is returned
+ assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testDeferredMessage_mustMeetThreshold() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1))
+
+ // WHEN more nonDeferredMessages are shown than the deferred message
+ val totalMessages = 10
+ val nonDeferredMessagesCount =
+ (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1
+ for (i in 0 until nonDeferredMessagesCount) {
+ biometricMessageDeferral.processMessage(4, "non-deferred-msg")
+ }
+ for (i in nonDeferredMessagesCount until totalMessages) {
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ }
+
+ // THEN there's no deferred message because it didn't meet the threshold
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testDeferredMessage_manyExcludedMessages_getDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1))
+
+ // WHEN more excludedMessages are shown than the deferred message
+ val totalMessages = 10
+ val excludedMessagesCount = (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1
+ for (i in 0 until excludedMessagesCount) {
+ biometricMessageDeferral.processMessage(3, "excluded-msg")
+ }
+ for (i in excludedMessagesCount until totalMessages) {
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ }
+
+ // THEN there IS a deferred message because the deferred msg meets the threshold amongst the
+ // non-excluded messages
+ assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testResetClearsOutCounts() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
+
+ // GIVEN two msgId=1 events processed
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+
+ // WHEN counts are reset and then a single deferred message is processed (msgId=2)
+ biometricMessageDeferral.reset()
+ biometricMessageDeferral.processMessage(2, "msgId-2")
+
+ // THEN msgId-2 is the deferred message since the two msgId=1 events were reset
+ assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testShouldDefer() {
+ // GIVEN should defer msgIds 1 and 2
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1, 2))
+
+ // THEN shouldDefer returns true for ids 1 & 2
+ assertTrue(biometricMessageDeferral.shouldDefer(1))
+ assertTrue(biometricMessageDeferral.shouldDefer(2))
+
+ // THEN should defer returns false for ids 3 & 4
+ assertFalse(biometricMessageDeferral.shouldDefer(3))
+ assertFalse(biometricMessageDeferral.shouldDefer(4))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index a4d223853b12..eecbee56d203 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -27,11 +27,11 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor.forClass
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -218,4 +218,18 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
assertFalse(keyguardUnlockAnimationController.canPerformInWindowLauncherAnimations())
assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
}
-} \ No newline at end of file
+
+ @Test
+ fun playCannedUnlockAnimation_nullSmartspaceView_doesNotThrowExecption() {
+ keyguardUnlockAnimationController.lockscreenSmartspace = null
+ keyguardUnlockAnimationController.willUnlockWithInWindowLauncherAnimations = true
+
+ keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
+ }
+}
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 3aa22669bbf2..ba1e168bc316 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
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.data.repository
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position
+import com.android.systemui.doze.DozeHost
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor
@@ -40,6 +41,7 @@ import org.mockito.MockitoAnnotations
class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
private lateinit var underTest: KeyguardRepositoryImpl
@@ -48,7 +50,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
- underTest = KeyguardRepositoryImpl(statusBarStateController, keyguardStateController)
+ underTest =
+ KeyguardRepositoryImpl(
+ statusBarStateController,
+ keyguardStateController,
+ dozeHost,
+ )
}
@Test
@@ -129,8 +136,8 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
var latest: Boolean? = null
val job = underTest.isDozing.onEach { latest = it }.launchIn(this)
- val captor = argumentCaptor<StatusBarStateController.StateListener>()
- verify(statusBarStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<DozeHost.Callback>()
+ verify(dozeHost).addCallback(captor.capture())
captor.value.onDozingChanged(true)
assertThat(latest).isTrue()
@@ -139,7 +146,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
assertThat(latest).isFalse()
job.cancel()
- verify(statusBarStateController).removeCallback(captor.value)
+ verify(dozeHost).removeCallback(captor.value)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index 9acd21cc6398..9a91ea91f3a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -51,18 +51,19 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes
@Parameters(
name =
"feature enabled = {0}, has favorites = {1}, has service infos = {2}, can show" +
- " while locked = {3} - expected visible = {4}"
+ " while locked = {3}, visibility is AVAILABLE {4} - expected visible = {5}"
)
@JvmStatic
fun data() =
- (0 until 16)
+ (0 until 32)
.map { combination ->
arrayOf(
- /* isFeatureEnabled= */ combination and 0b1000 != 0,
- /* hasFavorites= */ combination and 0b0100 != 0,
- /* hasServiceInfos= */ combination and 0b0010 != 0,
- /* canShowWhileLocked= */ combination and 0b0001 != 0,
- /* isVisible= */ combination == 0b1111,
+ /* isFeatureEnabled= */ combination and 0b10000 != 0,
+ /* hasFavorites= */ combination and 0b01000 != 0,
+ /* hasServiceInfos= */ combination and 0b00100 != 0,
+ /* canShowWhileLocked= */ combination and 0b00010 != 0,
+ /* visibilityAvailable= */ combination and 0b00001 != 0,
+ /* isVisible= */ combination == 0b11111,
)
}
.toList()
@@ -81,7 +82,8 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes
@JvmField @Parameter(1) var hasFavorites: Boolean = false
@JvmField @Parameter(2) var hasServiceInfos: Boolean = false
@JvmField @Parameter(3) var canShowWhileLocked: Boolean = false
- @JvmField @Parameter(4) var isVisible: Boolean = false
+ @JvmField @Parameter(4) var isVisibilityAvailable: Boolean = false
+ @JvmField @Parameter(5) var isVisibleExpected: Boolean = false
@Before
fun setUp() {
@@ -93,6 +95,14 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes
.thenReturn(Optional.of(controlsListingController))
whenever(component.canShowWhileLockedSetting)
.thenReturn(MutableStateFlow(canShowWhileLocked))
+ whenever(component.getVisibility())
+ .thenReturn(
+ if (isVisibilityAvailable) {
+ ControlsComponent.Visibility.AVAILABLE
+ } else {
+ ControlsComponent.Visibility.UNAVAILABLE
+ }
+ )
underTest =
HomeControlsKeyguardQuickAffordanceConfig(
@@ -128,7 +138,7 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes
assertThat(values.last())
.isInstanceOf(
- if (isVisible) {
+ if (isVisibleExpected) {
KeyguardQuickAffordanceConfig.State.Visible::class.java
} else {
KeyguardQuickAffordanceConfig.State.Hidden::class.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 059487dfdbc8..dede4ec0210c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -69,6 +69,7 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
val controlsController = mock<ControlsController>()
whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
whenever(component.getControlsListingController()).thenReturn(Optional.empty())
+ whenever(component.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
@@ -87,6 +88,7 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
val controlsController = mock<ControlsController>()
whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
whenever(component.getControlsListingController()).thenReturn(Optional.empty())
+ whenever(component.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
index d3fc29f1a0f1..19d841222a01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
@@ -36,6 +36,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -110,6 +111,10 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
.onEach { latest = it }
.launchIn(this)
+ // The interactor has an onStart { emit(Hidden) } to cover for upstream configs that don't
+ // produce an initial value. We yield to give the coroutine time to emit the first real
+ // value from our config.
+ yield()
assertThat(latest).isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
val visibleModel = latest as KeyguardQuickAffordanceModel.Visible
@@ -136,6 +141,10 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
.onEach { latest = it }
.launchIn(this)
+ // The interactor has an onStart { emit(Hidden) } to cover for upstream configs that don't
+ // produce an initial value. We yield to give the coroutine time to emit the first real
+ // value from our config.
+ yield()
assertThat(latest).isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
val visibleModel = latest as KeyguardQuickAffordanceModel.Visible
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 14b85b8b5e56..c612091382db 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
@@ -224,7 +224,10 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
repository.setAnimateDozingTransitions(false)
yield()
- assertThat(values).isEqualTo(listOf(false, true, false))
+ // Note the extra false value in the beginning. This is to cover for the initial value
+ // inserted by the quick affordance interactor which it does to cover for config
+ // implementations that don't emit an initial value.
+ assertThat(values).isEqualTo(listOf(false, false, true, false))
job.cancel()
}
@@ -372,6 +375,10 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
var latest: KeyguardQuickAffordanceViewModel? = null
val job = underTest.startButton.onEach { latest = it }.launchIn(this)
+ // The interactor has an onStart { emit(Hidden) } to cover for upstream configs that don't
+ // produce an initial value. We yield to give the coroutine time to emit the first real
+ // value from our config.
+ yield()
assertQuickAffordanceViewModel(
viewModel = latest,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 18bfd04102b7..954b4386b71d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -29,6 +29,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.testing.FakeNotifPanelEvents
import com.android.systemui.statusbar.StatusBarState
@@ -38,6 +39,8 @@ import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
@@ -79,6 +82,9 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
private lateinit var statusBarCallback: ArgumentCaptor<(StatusBarStateController.StateListener)>
+ @Captor
+ private lateinit var dreamOverlayCallback:
+ ArgumentCaptor<(DreamOverlayStateController.Callback)>
@JvmField
@Rule
val mockito = MockitoJUnit.rule()
@@ -113,6 +119,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
fakeHandler,)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
verify(statusBarStateController).addCallback(statusBarCallback.capture())
+ verify(dreamOverlayStateController).addCallback(dreamOverlayCallback.capture())
setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN, LOCKSCREEN_TOP)
setupHost(qsHost, MediaHierarchyManager.LOCATION_QS, QS_TOP)
setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS, QQS_TOP)
@@ -332,6 +339,27 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isFalse()
}
+ @Test
+ fun testDream() {
+ goToDream()
+ setMediaDreamComplicationEnabled(true)
+ verify(mediaCarouselController).onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_DREAM_OVERLAY),
+ nullable(),
+ eq(false),
+ anyLong(),
+ anyLong())
+ clearInvocations(mediaCarouselController)
+
+ setMediaDreamComplicationEnabled(false)
+ verify(mediaCarouselController).onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_QQS),
+ any(MediaHostState::class.java),
+ eq(false),
+ anyLong(),
+ anyLong())
+ }
+
private fun enableSplitShade() {
context.getOrCreateTestableResources().addOverride(
R.bool.config_use_split_notification_shade, true
@@ -343,6 +371,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 1)
statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+ whenever(dreamOverlayStateController.isOverlayActive).thenReturn(false)
+ dreamOverlayCallback.value.onStateChanged()
clearInvocations(mediaCarouselController)
}
@@ -354,6 +384,17 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
)
}
+ private fun goToDream() {
+ whenever(dreamOverlayStateController.isOverlayActive).thenReturn(true)
+ dreamOverlayCallback.value.onStateChanged()
+ }
+
+ private fun setMediaDreamComplicationEnabled(enabled: Boolean) {
+ val complications = if (enabled) listOf(mock<MediaDreamComplication>()) else emptyList()
+ whenever(dreamOverlayStateController.complications).thenReturn(complications)
+ dreamOverlayCallback.value.onComplicationsChanged()
+ }
+
private fun expandQS() {
mediaHierarchyManager.qsExpansion = 1.0f
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 171d893640d6..e7b4593b0ebb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -41,7 +41,6 @@ import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -74,8 +73,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Mock
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -102,7 +99,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
context,
logger,
windowManager,
- viewUtil,
FakeExecutor(FakeSystemClock()),
accessibilityManager,
configurationController,
@@ -182,7 +178,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Test
fun setIcon_isAppIcon_usesAppIconSize() {
- controllerReceiver.displayChip(getChipReceiverInfo())
+ controllerReceiver.displayView(getChipReceiverInfo())
val chipView = getChipView()
controllerReceiver.setIcon(chipView, PACKAGE_NAME)
@@ -198,7 +194,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Test
fun setIcon_notAppIcon_usesGenericIconSize() {
- controllerReceiver.displayChip(getChipReceiverInfo())
+ controllerReceiver.displayView(getChipReceiverInfo())
val chipView = getChipView()
controllerReceiver.setIcon(chipView, appPackageName = null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 1061e3c6b0d5..52b6eed9a14d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -42,7 +42,6 @@ import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -51,8 +50,8 @@ import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -75,8 +74,6 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Mock
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -110,7 +107,6 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
context,
logger,
windowManager,
- viewUtil,
fakeExecutor,
accessibilityManager,
configurationController,
@@ -309,7 +305,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun almostCloseToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
val state = almostCloseToStartCast()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -325,7 +321,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun almostCloseToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
val state = almostCloseToEndCast()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -341,7 +337,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToReceiverTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
val state = transferToReceiverTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -357,7 +353,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToThisDeviceTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
val state = transferToThisDeviceTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -373,7 +369,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToReceiverSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
val state = transferToReceiverSucceeded()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -387,7 +383,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToReceiverSucceeded_nullUndoRunnable_noUndo() {
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback = null))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback = null))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -398,7 +394,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
@@ -414,7 +410,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
}
}
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(undoCallbackCalled).isTrue()
@@ -425,7 +421,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
@@ -440,7 +436,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToThisDeviceSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
val state = transferToThisDeviceSucceeded()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -454,7 +450,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToThisDeviceSucceeded_nullUndoRunnable_noUndo() {
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback = null))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback = null))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -465,7 +461,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
@@ -481,7 +477,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
}
}
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(undoCallbackCalled).isTrue()
@@ -492,7 +488,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
@@ -507,7 +503,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToReceiverFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
val state = transferToReceiverFailed()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -523,7 +519,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToThisDeviceFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
val state = transferToThisDeviceFailed()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -538,24 +534,24 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun changeFromAlmostCloseToStartToTransferTriggered_loadingIconAppears() {
- controllerSender.displayChip(almostCloseToStartCast())
- controllerSender.displayChip(transferToReceiverTriggered())
+ controllerSender.displayView(almostCloseToStartCast())
+ controllerSender.displayView(transferToReceiverTriggered())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferTriggeredToTransferSucceeded_loadingIconDisappears() {
- controllerSender.displayChip(transferToReceiverTriggered())
- controllerSender.displayChip(transferToReceiverSucceeded())
+ controllerSender.displayView(transferToReceiverTriggered())
+ controllerSender.displayView(transferToReceiverSucceeded())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
}
@Test
fun changeFromTransferTriggeredToTransferSucceeded_undoButtonAppears() {
- controllerSender.displayChip(transferToReceiverTriggered())
- controllerSender.displayChip(
+ controllerSender.displayView(transferToReceiverTriggered())
+ controllerSender.displayView(
transferToReceiverSucceeded(
object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
@@ -568,26 +564,26 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun changeFromTransferSucceededToAlmostCloseToStart_undoButtonDisappears() {
- controllerSender.displayChip(transferToReceiverSucceeded())
- controllerSender.displayChip(almostCloseToStartCast())
+ controllerSender.displayView(transferToReceiverSucceeded())
+ controllerSender.displayView(almostCloseToStartCast())
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() {
- controllerSender.displayChip(transferToReceiverTriggered())
- controllerSender.displayChip(transferToReceiverFailed())
+ controllerSender.displayView(transferToReceiverTriggered())
+ controllerSender.displayView(transferToReceiverFailed())
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
- fun transferToReceiverTriggeredThenRemoveChip_chipStillDisplayed() {
- controllerSender.displayChip(transferToReceiverTriggered())
+ fun transferToReceiverTriggeredThenRemoveView_viewStillDisplayed() {
+ controllerSender.displayView(transferToReceiverTriggered())
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
@@ -596,9 +592,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToReceiverTriggeredThenFarFromReceiver_eventuallyTimesOut() {
val state = transferToReceiverTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
@@ -606,11 +602,11 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
}
@Test
- fun transferToThisDeviceTriggeredThenRemoveChip_chipStillDisplayed() {
- controllerSender.displayChip(transferToThisDeviceTriggered())
+ fun transferToThisDeviceTriggeredThenRemoveView_viewStillDisplayed() {
+ controllerSender.displayView(transferToThisDeviceTriggered())
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
@@ -619,9 +615,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test
fun transferToThisDeviceTriggeredThenFarFromReceiver_eventuallyTimesOut() {
val state = transferToThisDeviceTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
new file mode 100644
index 000000000000..249a91b0982a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.power.data.repository
+
+import android.content.BroadcastReceiver
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.PowerManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class PowerRepositoryImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var manager: PowerManager
+ @Mock private lateinit var dispatcher: BroadcastDispatcher
+ @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
+ @Captor private lateinit var filterCaptor: ArgumentCaptor<IntentFilter>
+
+ private lateinit var underTest: PowerRepositoryImpl
+
+ private var isInteractive = true
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ isInteractive = true
+ whenever(manager.isInteractive).then { isInteractive }
+
+ underTest = PowerRepositoryImpl(manager = manager, dispatcher = dispatcher)
+ }
+
+ @Test
+ fun `isInteractive - registers for broadcasts`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isInteractive.onEach {}.launchIn(this)
+
+ verifyRegistered()
+ assertThat(filterCaptor.value.hasAction(Intent.ACTION_SCREEN_ON)).isTrue()
+ assertThat(filterCaptor.value.hasAction(Intent.ACTION_SCREEN_OFF)).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - unregisters from broadcasts`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isInteractive.onEach {}.launchIn(this)
+ verifyRegistered()
+
+ job.cancel()
+
+ verify(dispatcher).unregisterReceiver(receiverCaptor.value)
+ }
+
+ @Test
+ fun `isInteractive - emits initial true value if screen was on`() =
+ runBlocking(IMMEDIATE) {
+ isInteractive = true
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+
+ verifyRegistered()
+
+ assertThat(value).isTrue()
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - emits initial false value if screen was off`() =
+ runBlocking(IMMEDIATE) {
+ isInteractive = false
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+
+ verifyRegistered()
+
+ assertThat(value).isFalse()
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - emits true when the screen turns on`() =
+ runBlocking(IMMEDIATE) {
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+ verifyRegistered()
+
+ isInteractive = true
+ receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_ON))
+
+ assertThat(value).isTrue()
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - emits false when the screen turns off`() =
+ runBlocking(IMMEDIATE) {
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+ verifyRegistered()
+
+ isInteractive = false
+ receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))
+
+ assertThat(value).isFalse()
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - emits correctly over time`() =
+ runBlocking(IMMEDIATE) {
+ val values = mutableListOf<Boolean>()
+ val job = underTest.isInteractive.onEach(values::add).launchIn(this)
+ verifyRegistered()
+
+ isInteractive = false
+ receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))
+ isInteractive = true
+ receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_ON))
+ isInteractive = false
+ receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))
+
+ assertThat(values).isEqualTo(listOf(true, false, true, false))
+ job.cancel()
+ }
+
+ private fun verifyRegistered() {
+ // We must verify with all arguments, even those that are optional because they have default
+ // values because Mockito is forcing us to. Once we can use mockito-kotlin, we should be
+ // able to remove this.
+ verify(dispatcher)
+ .registerReceiver(
+ capture(receiverCaptor),
+ capture(filterCaptor),
+ isNull(),
+ isNull(),
+ anyInt(),
+ isNull(),
+ )
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
new file mode 100644
index 000000000000..bf6a37ec8eff
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.power.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class PowerInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: PowerInteractor
+ private lateinit var repository: FakePowerRepository
+
+ @Before
+ fun setUp() {
+ repository =
+ FakePowerRepository(
+ initialInteractive = true,
+ )
+ underTest = PowerInteractor(repository = repository)
+ }
+
+ @Test
+ fun `isInteractive - screen turns off`() =
+ runBlocking(IMMEDIATE) {
+ repository.setInteractive(true)
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+
+ repository.setInteractive(false)
+
+ assertThat(value).isFalse()
+ job.cancel()
+ }
+
+ @Test
+ fun `isInteractive - becomes interactive`() =
+ runBlocking(IMMEDIATE) {
+ repository.setInteractive(false)
+ var value: Boolean? = null
+ val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
+
+ repository.setInteractive(true)
+
+ assertThat(value).isTrue()
+ job.cancel()
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
+}
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 e4751d135035..2a4996f259dc 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
@@ -70,9 +70,13 @@ class FooterActionsViewModelTest : SysuiTestCase() {
val underTest = utils.footerActionsViewModel(showPowerButton = false)
val settings = underTest.settings
- assertThat(settings.contentDescription)
- .isEqualTo(ContentDescription.Resource(R.string.accessibility_quick_settings_settings))
- assertThat(settings.icon).isEqualTo(Icon.Resource(R.drawable.ic_settings))
+ assertThat(settings.icon)
+ .isEqualTo(
+ Icon.Resource(
+ R.drawable.ic_settings,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+ )
+ )
assertThat(settings.background).isEqualTo(R.drawable.qs_footer_action_circle)
assertThat(settings.iconTint).isNull()
}
@@ -87,11 +91,13 @@ class FooterActionsViewModelTest : SysuiTestCase() {
val underTestWithPower = utils.footerActionsViewModel(showPowerButton = true)
val power = underTestWithPower.power
assertThat(power).isNotNull()
- assertThat(power!!.contentDescription)
+ assertThat(power!!.icon)
.isEqualTo(
- ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ Icon.Resource(
+ android.R.drawable.ic_lock_power_off,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ )
)
- assertThat(power.icon).isEqualTo(Icon.Resource(android.R.drawable.ic_lock_power_off))
assertThat(power.background).isEqualTo(R.drawable.qs_footer_action_circle_color)
assertThat(power.iconTint)
.isEqualTo(
@@ -164,14 +170,13 @@ class FooterActionsViewModelTest : SysuiTestCase() {
utils.setUserSwitcherEnabled(settings, true, userId)
val userSwitcher = currentUserSwitcher()
assertThat(userSwitcher).isNotNull()
- assertThat(userSwitcher!!.contentDescription)
- .isEqualTo(ContentDescription.Loaded("Signed in as foo"))
- assertThat(userSwitcher.icon).isEqualTo(Icon.Loaded(picture))
+ assertThat(userSwitcher!!.icon)
+ .isEqualTo(Icon.Loaded(picture, ContentDescription.Loaded("Signed in as foo")))
assertThat(userSwitcher.background).isEqualTo(R.drawable.qs_footer_action_circle)
// Change the current user name.
userSwitcherControllerWrapper.currentUserName = "bar"
- assertThat(currentUserSwitcher()?.contentDescription)
+ assertThat(currentUserSwitcher()?.icon?.contentDescription)
.isEqualTo(ContentDescription.Loaded("Signed in as bar"))
fun iconTint(): Int? = currentUserSwitcher()!!.iconTint
@@ -243,7 +248,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
// Map any SecurityModel into a non-null SecurityButtonConfig.
val buttonConfig =
SecurityButtonConfig(
- icon = Icon.Resource(0),
+ icon = Icon.Resource(res = 0, contentDescription = null),
text = "foo",
isClickable = true,
)
@@ -340,7 +345,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
assertThat(foregroundServices.displayText).isTrue()
securityToConfig = {
SecurityButtonConfig(
- icon = Icon.Resource(0),
+ icon = Icon.Resource(res = 0, contentDescription = null),
text = "foo",
isClickable = true,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index ed988810fc83..f7b9438e6535 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -33,17 +33,18 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.controller.ControlInfo
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -57,13 +58,13 @@ import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when`
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.nullable
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
import java.util.Optional
@SmallTest
@@ -93,8 +94,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
private lateinit var serviceInfo: ControlsServiceInfo
@Mock
private lateinit var uiEventLogger: UiEventLogger
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
@Captor
private lateinit var listingCallbackCaptor:
ArgumentCaptor<ControlsListingController.ControlsListingCallback>
@@ -119,7 +118,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
`when`(qsHost.context).thenReturn(spiedContext)
`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
`when`(controlsComponent.isEnabled()).thenReturn(true)
- `when`(keyguardStateController.isUnlocked()).thenReturn(true)
`when`(controlsController.getPreferredStructure())
.thenReturn(StructureInfo(ComponentName("pkg", "cls"), "structure", listOf()))
secureSettings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 1)
@@ -222,12 +220,19 @@ class DeviceControlsTileTest : SysuiTestCase() {
}
@Test
- fun testStateAvailableIfListings() {
+ fun testStateActiveIfListingsHasControlsFavorited() {
verify(controlsListingController).observe(
any(LifecycleOwner::class.java),
capture(listingCallbackCaptor)
)
`when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
+ `when`(controlsController.getPreferredStructure()).thenReturn(
+ StructureInfo(
+ ComponentName("pkg", "cls"),
+ "structure",
+ listOf(ControlInfo("id", "title", "subtitle", 1))
+ )
+ )
listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
testableLooper.processAllMessages()
@@ -236,6 +241,22 @@ class DeviceControlsTileTest : SysuiTestCase() {
}
@Test
+ fun testStateInactiveIfListingsHasNoControlsFavorited() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+ `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
+ `when`(controlsController.getPreferredStructure())
+ .thenReturn(StructureInfo(ComponentName("pkg", "cls"), "structure", listOf()))
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ }
+
+ @Test
fun testStateInactiveIfLocked() {
verify(controlsListingController).observe(
any(LifecycleOwner::class.java),
@@ -281,7 +302,14 @@ class DeviceControlsTileTest : SysuiTestCase() {
capture(listingCallbackCaptor)
)
`when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
- `when`(keyguardStateController.isUnlocked).thenReturn(true)
+ `when`(controlsUiController.resolveActivity()).thenReturn(ControlsActivity::class.java)
+ `when`(controlsController.getPreferredStructure()).thenReturn(
+ StructureInfo(
+ ComponentName("pkg", "cls"),
+ "structure",
+ listOf(ControlInfo("id", "title", "subtitle", 1))
+ )
+ )
listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
testableLooper.processAllMessages()
@@ -305,7 +333,14 @@ class DeviceControlsTileTest : SysuiTestCase() {
)
`when`(controlsComponent.getVisibility())
.thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK)
- `when`(keyguardStateController.isUnlocked).thenReturn(false)
+ `when`(controlsUiController.resolveActivity()).thenReturn(ControlsActivity::class.java)
+ `when`(controlsController.getPreferredStructure()).thenReturn(
+ StructureInfo(
+ ComponentName("pkg", "cls"),
+ "structure",
+ listOf(ControlInfo("id", "title", "subtitle", 1))
+ )
+ )
listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
testableLooper.processAllMessages()
@@ -345,8 +380,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
statusBarStateController,
activityStarter,
qsLogger,
- controlsComponent,
- keyguardStateController
+ controlsComponent
).also {
it.initialize()
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
index 5db3b9caa007..da52a9b1a3c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
@@ -53,7 +53,6 @@ class UserDetailViewAdapterTest : SysuiTestCase() {
@Mock private lateinit var mUserDetailItemView: UserDetailItemView
@Mock private lateinit var mOtherView: View
@Mock private lateinit var mInflatedUserDetailItemView: UserDetailItemView
- @Mock private lateinit var mUserInfo: UserInfo
@Mock private lateinit var mLayoutInflater: LayoutInflater
private var falsingManagerFake: FalsingManagerFake = FalsingManagerFake()
private lateinit var adapter: UserDetailView.Adapter
@@ -142,7 +141,7 @@ class UserDetailViewAdapterTest : SysuiTestCase() {
private fun createUserRecord(current: Boolean, guest: Boolean) =
UserRecord(
- mUserInfo,
+ UserInfo(0 /* id */, "name", 0 /* flags */),
mPicture,
guest,
current,
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 f2670131081f..3224a6f970be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -547,8 +547,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mNavigationModeController,
mFragmentService,
mContentResolver,
- mQuickAccessWalletController,
- mQrCodeScannerController,
mRecordingController,
mLargeScreenShadeHeaderController,
mScreenOffAnimationController,
@@ -556,7 +554,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mPanelExpansionStateManager,
mNotificationRemoteInputManager,
mSysUIUnfoldComponent,
- mControlsComponent,
mInteractionJankMonitor,
mQsFrameTranslateController,
mSysUiState,
@@ -569,8 +566,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mShadeTransitionController,
mSystemClock,
mock(CameraGestureHelper.class),
- () -> mKeyguardBottomAreaViewModel,
- () -> mKeyguardBottomAreaInteractor);
+ mKeyguardBottomAreaViewModel,
+ mKeyguardBottomAreaInteractor);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
() -> {},
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 43fc8983011d..2adc389a964e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -19,8 +19,10 @@ package com.android.systemui.shade
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.MotionEvent
+import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.keyguard.LockIconViewController
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
@@ -246,6 +248,18 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
assertThat(returnVal).isTrue()
}
+
+ @Test
+ fun testGetBouncerContainer() {
+ underTest.bouncerContainer
+ verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
+ }
+
+ @Test
+ fun testGetKeyguardMessageArea() {
+ underTest.keyguardMessageArea
+ verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
+ }
}
private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
index d2970a63a860..97c0bb2c09ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
@@ -27,6 +27,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.tuner.TunerService
import com.android.systemui.tuner.TunerService.Tunable
@@ -63,6 +64,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
private lateinit var tunerService: TunerService
@Mock
private lateinit var dumpManager: DumpManager
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
private lateinit var tunableCaptor: ArgumentCaptor<Tunable>
private lateinit var underTest: PulsingGestureListener
@@ -77,6 +80,7 @@ class PulsingGestureListenerTest : SysuiTestCase() {
dockManager,
centralSurfaces,
ambientDisplayConfiguration,
+ statusBarStateController,
tunerService,
dumpManager
)
@@ -85,6 +89,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
@Test
fun testGestureDetector_singleTapEnabled() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -102,6 +108,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
@Test
fun testGestureDetector_doubleTapEnabled() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN double tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -119,6 +127,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
@Test
fun testGestureDetector_singleTapEnabled_falsing() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -135,7 +145,23 @@ class PulsingGestureListenerTest : SysuiTestCase() {
}
@Test
+ fun testGestureDetector_notPulsing_noFalsingCheck() {
+ whenever(statusBarStateController.isPulsing).thenReturn(false)
+
+ // GIVEN tap is enabled, prox not covered
+ whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
+ // WHEN there's a tap
+ underTest.onSingleTapConfirmed(downEv)
+
+ // THEN the falsing manager never gets a call (because the device wasn't pulsing
+ // during the tap)
+ verify(falsingManager, never()).isFalseTap(anyInt())
+ }
+
+ @Test
fun testGestureDetector_doubleTapEnabled_falsing() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN double tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -153,6 +179,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
@Test
fun testGestureDetector_singleTapEnabled_proxCovered() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN tap is enabled, not a false tap based on classifiers
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -170,6 +198,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
@Test
fun testGestureDetector_doubleTapEnabled_proxCovered() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN double tap is enabled, not a false tap based on classifiers
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
new file mode 100644
index 000000000000..d925d0acd8d8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.ExpandHelper
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.atLeast
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class DragDownHelperTest : SysuiTestCase() {
+
+ private lateinit var dragDownHelper: DragDownHelper
+
+ private val collapsedHeight = 300
+ private val falsingManager: FalsingManager = mock()
+ private val falsingCollector: FalsingCollector = mock()
+ private val dragDownloadCallback: LockscreenShadeTransitionController = mock()
+ private val expandableView: ExpandableView = mock()
+ private val expandCallback: ExpandHelper.Callback = mock()
+
+ @Before
+ fun setUp() {
+ whenever(expandableView.collapsedHeight).thenReturn(collapsedHeight)
+
+ dragDownHelper = DragDownHelper(
+ falsingManager,
+ falsingCollector,
+ dragDownloadCallback,
+ mContext
+ ).also {
+ it.expandCallback = expandCallback
+ }
+ }
+
+ @Test
+ fun cancelChildExpansion_updateHeight() {
+ whenever(expandableView.actualHeight).thenReturn(500)
+
+ dragDownHelper.cancelChildExpansion(expandableView, animationDuration = 0)
+
+ verify(expandableView, atLeast(1)).actualHeight = collapsedHeight
+ }
+
+ @Test
+ fun cancelChildExpansion_dontUpdateHeight() {
+ whenever(expandableView.actualHeight).thenReturn(collapsedHeight)
+
+ dragDownHelper.cancelChildExpansion(expandableView, animationDuration = 0)
+
+ verify(expandableView, never()).actualHeight = anyInt()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 05692b3e6749..74e274705e55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
@@ -65,7 +66,6 @@ import android.content.pm.UserInfo;
import android.graphics.Color;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricSourceType;
-import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
import android.os.Looper;
@@ -602,7 +602,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
String message = mContext.getString(R.string.keyguard_unlock);
mController.setVisible(true);
- mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
+ mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
@@ -636,10 +636,10 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true);
mController.setVisible(true);
- mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
+ mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verify(mStatusBarKeyguardViewManager).showBouncerMessage(eq(message), any());
+ verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any());
}
@Test
@@ -651,7 +651,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mController.setVisible(true);
mController.getKeyguardCallback().onBiometricError(
- FaceManager.FACE_ERROR_TIMEOUT, message, BiometricSourceType.FACE);
+ FACE_ERROR_TIMEOUT, message, BiometricSourceType.FACE);
verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
}
@@ -698,8 +698,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT,
BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH,
BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT
};
for (int msgId : msgIds) {
mKeyguardUpdateMonitorCallback.onBiometricHelp(
@@ -728,8 +727,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT,
BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH,
BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT
};
for (int msgId : msgIds) {
final String numberedHelpString = helpString + msgId;
@@ -743,6 +741,64 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
+ public void sendTooDarkFaceHelpMessages_onTimeout_noFpEnrolled() {
+ createController();
+
+ // GIVEN fingerprint NOT enrolled
+ when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ 0)).thenReturn(false);
+
+ // WHEN help message received
+ final String helpString = "helpMsg";
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK,
+ helpString,
+ BiometricSourceType.FACE
+ );
+
+ // THEN help message not shown yet
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+
+ // WHEN face timeout error received
+ mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_TIMEOUT, "face timeout",
+ BiometricSourceType.FACE);
+
+ // THEN the low light message shows with suggestion to swipe up to unlock
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, helpString);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_unlock));
+ }
+
+ @Test
+ public void sendTooDarkFaceHelpMessages_onTimeout_fingerprintEnrolled() {
+ createController();
+
+ // GIVEN fingerprint enrolled
+ when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ 0)).thenReturn(true);
+
+ // WHEN help message received
+ final String helpString = "helpMsg";
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK,
+ helpString,
+ BiometricSourceType.FACE
+ );
+
+ // THEN help message not shown yet
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+
+ // WHEN face timeout error received
+ mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_TIMEOUT, "face timeout",
+ BiometricSourceType.FACE);
+
+ // THEN the low light message shows and suggests trying fingerprint
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, helpString);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_suggest_fingerprint));
+ }
+
+ @Test
public void updateMonitor_listenerUpdatesIndication() {
createController();
String restingIndication = "Resting indication";
@@ -997,14 +1053,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
- public void onTrustGrantedMessageDoesShowsOnTrustGranted() {
+ public void onTrustGrantedMessageShowsOnTrustGranted() {
createController();
mController.setVisible(true);
// GIVEN trust is granted
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
- // WHEN the showTrustGranted message is called
+ // WHEN the showTrustGranted method is called
final String trustGrantedMsg = "testing trust granted message";
mController.getKeyguardCallback().showTrustGrantedMessage(trustGrantedMsg);
@@ -1015,6 +1071,38 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
+ public void onTrustGrantedMessage_nullMessage_showsDefaultMessage() {
+ createController();
+ mController.setVisible(true);
+
+ // GIVEN trust is granted
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+ // WHEN the showTrustGranted method is called with a null message
+ mController.getKeyguardCallback().showTrustGrantedMessage(null);
+
+ // THEN verify the default trust granted message shows
+ verifyIndicationMessage(
+ INDICATION_TYPE_TRUST,
+ getContext().getString(R.string.keyguard_indication_trust_unlocked));
+ }
+
+ @Test
+ public void onTrustGrantedMessage_emptyString_showsNoMessage() {
+ createController();
+ mController.setVisible(true);
+
+ // GIVEN trust is granted
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+ // WHEN the showTrustGranted method is called with an EMPTY string
+ mController.getKeyguardCallback().showTrustGrantedMessage("");
+
+ // THEN verify NO trust message is shown
+ verifyNoMessage(INDICATION_TYPE_TRUST);
+ }
+
+ @Test
public void coEx_faceSuccess_showsPressToOpen() {
// GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, no a11y enabled
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index b166b7367b53..6446fb5d8c81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -48,7 +48,6 @@ import org.mockito.ArgumentMatchers.floatThat
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.anyString
@@ -56,6 +55,7 @@ import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@@ -139,7 +139,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(maxBlur), any())
+ verify(shadeAnimation).animateTo(eq(maxBlur))
}
@Test
@@ -147,7 +147,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 0.01f, expanded = false, tracking = false, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(maxBlur), any())
+ verify(shadeAnimation).animateTo(eq(maxBlur))
}
@Test
@@ -157,7 +157,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 0f, expanded = false, tracking = false, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(0), any())
+ verify(shadeAnimation).animateTo(eq(0))
}
@Test
@@ -168,15 +168,15 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
onPanelExpansionChanged_apliesBlur_ifShade()
clearInvocations(shadeAnimation)
notificationShadeDepthController.onPanelExpansionChanged(event)
- verify(shadeAnimation, never()).animateTo(anyInt(), any())
+ verify(shadeAnimation, never()).animateTo(anyInt())
notificationShadeDepthController.onPanelExpansionChanged(
event.copy(fraction = 0.9f, tracking = true))
- verify(shadeAnimation, never()).animateTo(anyInt(), any())
+ verify(shadeAnimation, never()).animateTo(anyInt())
notificationShadeDepthController.onPanelExpansionChanged(
event.copy(fraction = 0.8f, tracking = false))
- verify(shadeAnimation).animateTo(eq(0), any())
+ verify(shadeAnimation).animateTo(eq(0))
}
@Test
@@ -186,7 +186,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 0.6f, expanded = true, tracking = true, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(maxBlur), any())
+ verify(shadeAnimation).animateTo(eq(maxBlur))
}
@Test
@@ -212,7 +212,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
statusBarState = StatusBarState.KEYGUARD
statusBarStateListener.onStateChanged(statusBarState)
- verify(shadeAnimation).animateTo(eq(0), any())
+ verify(shadeAnimation).animateTo(eq(0))
}
@Test
@@ -395,13 +395,13 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
@Test
fun brightnessMirrorVisible_whenVisible() {
notificationShadeDepthController.brightnessMirrorVisible = true
- verify(brightnessSpring).animateTo(eq(maxBlur), any())
+ verify(brightnessSpring).animateTo(eq(maxBlur))
}
@Test
fun brightnessMirrorVisible_whenHidden() {
notificationShadeDepthController.brightnessMirrorVisible = false
- verify(brightnessSpring).animateTo(eq(0), any())
+ verify(brightnessSpring).animateTo(eq(0))
}
@Test
@@ -424,7 +424,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
fun ignoreShadeBlurUntilHidden_whennNull_ignoresIfShadeHasNoBlur() {
`when`(shadeAnimation.radius).thenReturn(0f)
notificationShadeDepthController.blursDisabledForAppLaunch = true
- verify(shadeAnimation, never()).animateTo(anyInt(), any())
+ verify(shadeAnimation, never()).animateTo(anyInt())
}
private fun enableSplitShade() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
new file mode 100644
index 000000000000..44cbe51a30ac
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.atLeast
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class PulseExpansionHandlerTest : SysuiTestCase() {
+
+ private lateinit var pulseExpansionHandler: PulseExpansionHandler
+
+ private val collapsedHeight = 300
+ private val wakeUpCoordinator: NotificationWakeUpCoordinator = mock()
+ private val bypassController: KeyguardBypassController = mock()
+ private val headsUpManager: HeadsUpManagerPhone = mock()
+ private val roundnessManager: NotificationRoundnessManager = mock()
+ private val configurationController: ConfigurationController = mock()
+ private val statusBarStateController: StatusBarStateController = mock()
+ private val falsingManager: FalsingManager = mock()
+ private val lockscreenShadeTransitionController: LockscreenShadeTransitionController = mock()
+ private val falsingCollector: FalsingCollector = mock()
+ private val dumpManager: DumpManager = mock()
+ private val expandableView: ExpandableView = mock()
+
+ @Before
+ fun setUp() {
+ whenever(expandableView.collapsedHeight).thenReturn(collapsedHeight)
+
+ pulseExpansionHandler = PulseExpansionHandler(
+ mContext,
+ wakeUpCoordinator,
+ bypassController,
+ headsUpManager,
+ roundnessManager,
+ configurationController,
+ statusBarStateController,
+ falsingManager,
+ lockscreenShadeTransitionController,
+ falsingCollector,
+ dumpManager
+ )
+ }
+
+ @Test
+ fun resetChild_updateHeight() {
+ whenever(expandableView.actualHeight).thenReturn(500)
+
+ pulseExpansionHandler.reset(expandableView, animationDuration = 0)
+
+ verify(expandableView, atLeast(1)).actualHeight = collapsedHeight
+ }
+
+ @Test
+ fun resetChild_dontUpdateHeight() {
+ whenever(expandableView.actualHeight).thenReturn(collapsedHeight)
+
+ pulseExpansionHandler.reset(expandableView, animationDuration = 0)
+
+ verify(expandableView, never()).actualHeight = anyInt()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
new file mode 100644
index 000000000000..11798a7a4f96
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.stack
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val MAX_PULSE_HEIGHT = 100000f
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class AmbientStateTest : SysuiTestCase() {
+
+ private val dumpManager = mock<DumpManager>()
+ private val sectionProvider = StackScrollAlgorithm.SectionProvider { _, _ -> false }
+ private val bypassController = StackScrollAlgorithm.BypassController { false }
+ private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
+
+ private lateinit var sut: AmbientState
+
+ @Before
+ fun setUp() {
+ sut =
+ AmbientState(
+ context,
+ dumpManager,
+ sectionProvider,
+ bypassController,
+ statusBarKeyguardViewManager,
+ )
+ }
+
+ // region isDimmed
+ @Test
+ fun isDimmed_whenTrue_shouldReturnTrue() {
+ sut.arrangeDimmed(true)
+
+ assertThat(sut.isDimmed).isTrue()
+ }
+
+ @Test
+ fun isDimmed_whenFalse_shouldReturnFalse() {
+ sut.arrangeDimmed(false)
+
+ assertThat(sut.isDimmed).isFalse()
+ }
+
+ @Test
+ fun isDimmed_whenDozeAmountIsEmpty_shouldReturnTrue() {
+ sut.arrangeDimmed(true)
+ sut.dozeAmount = 0f
+
+ assertThat(sut.isDimmed).isTrue()
+ }
+
+ @Test
+ fun isDimmed_whenPulseExpandingIsFalse_shouldReturnTrue() {
+ sut.arrangeDimmed(true)
+ sut.arrangePulseExpanding(false)
+ sut.dozeAmount = 1f // arrangePulseExpanding changes dozeAmount
+
+ assertThat(sut.isDimmed).isTrue()
+ }
+ // endregion
+
+ // region pulseHeight
+ @Test
+ fun pulseHeight_whenValueChanged_shouldCallListener() {
+ var listenerCalledCount = 0
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+ sut.setOnPulseHeightChangedListener { listenerCalledCount++ }
+
+ sut.pulseHeight = 0f
+
+ assertThat(listenerCalledCount).isEqualTo(1)
+ }
+
+ @Test
+ fun pulseHeight_whenSetSameValue_shouldDoNothing() {
+ var listenerCalledCount = 0
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+ sut.setOnPulseHeightChangedListener { listenerCalledCount++ }
+
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+
+ assertThat(listenerCalledCount).isEqualTo(0)
+ }
+
+ @Test
+ fun pulseHeight_whenValueIsFull_shouldReturn0() {
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun pulseHeight_whenValueIsNotFull_shouldReturnValue() {
+ val expected = MAX_PULSE_HEIGHT - 0.1f
+ sut.pulseHeight = expected
+
+ assertThat(sut.pulseHeight).isEqualTo(expected)
+ }
+ // endregion
+
+ // region statusBarState
+ @Test
+ fun statusBarState_whenPreviousStateIsNotKeyguardAndChange_shouldSetIsFlingRequiredToFalse() {
+ sut.setStatusBarState(StatusBarState.SHADE)
+ sut.isFlingRequiredAfterLockScreenSwipeUp = true
+
+ sut.setStatusBarState(StatusBarState.KEYGUARD)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
+ }
+
+ @Test
+ fun statusBarState_whenPreviousStateIsKeyguardAndChange_shouldDoNothing() {
+ sut.setStatusBarState(StatusBarState.KEYGUARD)
+ sut.isFlingRequiredAfterLockScreenSwipeUp = true
+
+ sut.setStatusBarState(StatusBarState.SHADE)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+ // endregion
+
+ // region hideAmount
+ @Test
+ fun hideAmount_whenSetToFullValue_shouldReturnZeroFromPulseHeight() {
+ sut.hideAmount = 0f
+ sut.pulseHeight = 1f
+
+ sut.hideAmount = 1f
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun hideAmount_whenSetToAnyNotFullValue_shouldDoNothing() {
+ sut.hideAmount = 1f
+ sut.pulseHeight = 1f
+
+ sut.hideAmount = 0f
+
+ assertThat(sut.pulseHeight).isEqualTo(1f)
+ }
+ // endregion
+
+ // region dozeAmount
+ @Test
+ fun dozeAmount_whenDozeAmountIsSetToFullDozing_shouldReturnZeroFromPulseHeight() {
+ sut.dozeAmount = 0f
+ sut.pulseHeight = 1f
+
+ sut.dozeAmount = 1f
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun dozeAmount_whenDozeAmountIsSetToFullAwake_shouldReturnZeroFromPulseHeight() {
+ sut.dozeAmount = 1f
+ sut.pulseHeight = 1f
+
+ sut.dozeAmount = 0f
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun dozeAmount_whenDozeAmountIsSetAnyValueNotFullAwakeOrDozing_shouldDoNothing() {
+ sut.dozeAmount = 1f
+ sut.pulseHeight = 1f
+
+ sut.dozeAmount = 0.5f
+
+ assertThat(sut.pulseHeight).isEqualTo(1f)
+ }
+ // endregion
+
+ // region trackedHeadsUpRow
+ @Test
+ fun trackedHeadsUpRow_whenIsAboveTheShelf_shouldReturnInstance() {
+ sut.trackedHeadsUpRow = mock { whenever(isAboveShelf).thenReturn(true) }
+
+ assertThat(sut.trackedHeadsUpRow).isNotNull()
+ }
+
+ @Test
+ fun trackedHeadsUpRow_whenIsNotAboveTheShelf_shouldReturnNull() {
+ sut.trackedHeadsUpRow = mock { whenever(isAboveShelf).thenReturn(false) }
+
+ assertThat(sut.trackedHeadsUpRow).isNull()
+ }
+ // endregion
+
+ // region isSwipingUp
+ @Test
+ fun isSwipingUp_whenValueChangedToTrue_shouldRequireFling() {
+ sut.isSwipingUp = false
+ sut.isFlingRequiredAfterLockScreenSwipeUp = false
+
+ sut.isSwipingUp = true
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
+ }
+
+ @Test
+ fun isSwipingUp_whenValueChangedToFalse_shouldRequireFling() {
+ sut.isSwipingUp = true
+ sut.isFlingRequiredAfterLockScreenSwipeUp = false
+
+ sut.isSwipingUp = false
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+ // endregion
+
+ // region isFlinging
+ @Test
+ fun isFlinging_shouldNotNeedFling() {
+ sut.arrangeFlinging(true)
+
+ sut.setFlinging(false)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
+ }
+
+ @Test
+ fun isFlinging_whenNotOnLockScreen_shouldDoNothing() {
+ sut.arrangeFlinging(true)
+ sut.setStatusBarState(StatusBarState.SHADE)
+ sut.isFlingRequiredAfterLockScreenSwipeUp = true
+
+ sut.setFlinging(false)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+
+ @Test
+ fun isFlinging_whenValueChangedToTrue_shouldDoNothing() {
+ sut.arrangeFlinging(false)
+
+ sut.setFlinging(true)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+ // endregion
+
+ // region scrollY
+ @Test
+ fun scrollY_shouldSetValueGreaterThanZero() {
+ sut.scrollY = 0
+
+ sut.scrollY = 1
+
+ assertThat(sut.scrollY).isEqualTo(1)
+ }
+
+ @Test
+ fun scrollY_shouldNotSetValueLessThanZero() {
+ sut.scrollY = 0
+
+ sut.scrollY = -1
+
+ assertThat(sut.scrollY).isEqualTo(0)
+ }
+ // endregion
+
+ // region setOverScrollAmount
+ fun setOverScrollAmount_shouldSetValueOnTop() {
+ sut.setOverScrollAmount(/* amount = */ 10f, /* onTop = */ true)
+
+ val resultOnTop = sut.getOverScrollAmount(/* top = */ true)
+ val resultOnBottom = sut.getOverScrollAmount(/* top = */ false)
+
+ assertThat(resultOnTop).isEqualTo(10f)
+ assertThat(resultOnBottom).isEqualTo(0f)
+ }
+
+ fun setOverScrollAmount_shouldSetValueOnBottom() {
+ sut.setOverScrollAmount(/* amount = */ 10f, /* onTop = */ false)
+
+ val resultOnTop = sut.getOverScrollAmount(/* top */ true)
+ val resultOnBottom = sut.getOverScrollAmount(/* top */ false)
+
+ assertThat(resultOnTop).isEqualTo(0f)
+ assertThat(resultOnBottom).isEqualTo(10f)
+ }
+ // endregion
+
+ // region IsPulseExpanding
+ @Test
+ fun isPulseExpanding_shouldReturnTrue() {
+ sut.arrangePulseExpanding(true)
+
+ assertThat(sut.isPulseExpanding).isTrue()
+ }
+
+ @Test
+ fun isPulseExpanding_whenPulseHeightIsMax_shouldReturnFalse() {
+ sut.arrangePulseExpanding(true)
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+
+ assertThat(sut.isPulseExpanding).isFalse()
+ }
+
+ @Test
+ fun isPulseExpanding_whenDozeAmountIsZero_shouldReturnFalse() {
+ sut.arrangePulseExpanding(true)
+ sut.dozeAmount = 0f
+
+ assertThat(sut.isPulseExpanding).isFalse()
+ }
+
+ @Test
+ fun isPulseExpanding_whenHideAmountIsFull_shouldReturnFalse() {
+ sut.arrangePulseExpanding(true)
+ sut.hideAmount = 1f
+
+ assertThat(sut.isPulseExpanding).isFalse()
+ }
+ // endregion
+
+ // region isOnKeyguard
+ @Test
+ fun isOnKeyguard_whenStatusBarStateIsKeyguard_shouldReturnTrue() {
+ sut.setStatusBarState(StatusBarState.KEYGUARD)
+
+ assertThat(sut.isOnKeyguard).isTrue()
+ }
+
+ @Test
+ fun isOnKeyguard_whenStatusBarStateIsNotKeyguard_shouldReturnFalse() {
+ sut.setStatusBarState(StatusBarState.SHADE)
+
+ assertThat(sut.isOnKeyguard).isFalse()
+ }
+ // endregion
+}
+
+// region Arrange helper methods.
+private fun AmbientState.arrangeDimmed(value: Boolean) {
+ isDimmed = value
+ dozeAmount = if (value) 0f else 1f
+ arrangePulseExpanding(!value)
+}
+
+private fun AmbientState.arrangePulseExpanding(value: Boolean) {
+ if (value) {
+ dozeAmount = 1f
+ hideAmount = 0f
+ pulseHeight = 0f
+ } else {
+ dozeAmount = 0f
+ hideAmount = 1f
+ pulseHeight = MAX_PULSE_HEIGHT
+ }
+}
+
+private fun AmbientState.arrangeFlinging(value: Boolean) {
+ setStatusBarState(StatusBarState.KEYGUARD)
+ setFlinging(value)
+ isFlingRequiredAfterLockScreenSwipeUp = true
+}
+// endregion
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 3f190367f284..77418138158b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -3,15 +3,22 @@ package com.android.systemui.statusbar.notification.stack
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
+import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
-import junit.framework.Assert.*
+import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState
+import com.android.systemui.util.mockito.mock
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.*
+import org.mockito.Mockito.mock
import org.mockito.Mockito.`when` as whenever
/**
@@ -22,13 +29,18 @@ import org.mockito.Mockito.`when` as whenever
@RunWithLooper
class NotificationShelfTest : SysuiTestCase() {
- private val shelf = NotificationShelf(context, /* attrs */ null)
+ private val shelf = NotificationShelf(
+ context,
+ /* attrs */ null,
+ /* showNotificationShelf */true
+ )
private val shelfState = shelf.viewState as NotificationShelf.ShelfState
private val ambientState = mock(AmbientState::class.java)
+ private val hostLayoutController: NotificationStackScrollLayoutController = mock()
@Before
fun setUp() {
- shelf.bind(ambientState, /* hostLayoutController */ null)
+ shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController)
shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
}
@@ -37,7 +49,7 @@ class NotificationShelfTest : SysuiTestCase() {
setFractionToShade(0f)
setOnLockscreen(true)
- shelf.updateActualWidth(/* fractionToShade */ 0f, /* shortestWidth */ 10f);
+ shelf.updateActualWidth(/* fractionToShade */ 0f, /* shortestWidth */ 10f)
assertTrue(shelf.actualWidth == 10)
shelf.updateActualWidth(/* fractionToShade */ 0.5f, /* shortestWidth */ 10f)
@@ -155,7 +167,7 @@ class NotificationShelfTest : SysuiTestCase() {
whenever(expandableView.actualHeight).thenReturn(20)
whenever(expandableView.minHeight).thenReturn(20)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(true)
whenever(ambientState.isOnKeyguard).thenReturn(true)
@@ -182,7 +194,7 @@ class NotificationShelfTest : SysuiTestCase() {
whenever(expandableView.actualHeight).thenReturn(20)
whenever(expandableView.minHeight).thenReturn(20)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(true)
whenever(ambientState.isOnKeyguard).thenReturn(true)
@@ -209,7 +221,7 @@ class NotificationShelfTest : SysuiTestCase() {
whenever(expandableView.actualHeight).thenReturn(25)
whenever(expandableView.minHeight).thenReturn(25)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(true)
whenever(ambientState.isOnKeyguard).thenReturn(true)
@@ -236,7 +248,7 @@ class NotificationShelfTest : SysuiTestCase() {
whenever(expandableView.actualHeight).thenReturn(10)
whenever(expandableView.minHeight).thenReturn(10)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(false)
whenever(ambientState.isExpansionChanging).thenReturn(false)
@@ -251,6 +263,42 @@ class NotificationShelfTest : SysuiTestCase() {
assertEquals(0f, amountInShelf)
}
+ @Test
+ fun updateState_expansionChanging_shelfTransparent() {
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.25f,
+ expectedAlpha = 0.0f
+ )
+ }
+
+ @Test
+ fun updateState_expansionChangingWhileBouncerInTransit_shelfTransparent() {
+ whenever(ambientState.isBouncerInTransit).thenReturn(true)
+
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.85f,
+ expectedAlpha = 0.0f
+ )
+ }
+
+ @Test
+ fun updateState_expansionChanging_shelfAlphaUpdated() {
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.6f,
+ expectedAlpha = ShadeInterpolation.getContentAlpha(0.6f)
+ )
+ }
+
+ @Test
+ fun updateState_expansionChangingWhileBouncerInTransit_shelfAlphaUpdated() {
+ whenever(ambientState.isBouncerInTransit).thenReturn(true)
+
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.95f,
+ expectedAlpha = aboutToShowBouncerProgress(0.95f)
+ )
+ }
+
private fun setFractionToShade(fraction: Float) {
whenever(ambientState.fractionToShade).thenReturn(fraction)
}
@@ -258,4 +306,19 @@ class NotificationShelfTest : SysuiTestCase() {
private fun setOnLockscreen(isOnLockscreen: Boolean) {
whenever(ambientState.isOnKeyguard).thenReturn(isOnLockscreen)
}
-} \ No newline at end of file
+
+ private fun updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction: Float,
+ expectedAlpha: Float
+ ) {
+ whenever(ambientState.lastVisibleBackgroundChild)
+ .thenReturn(ExpandableNotificationRow(mContext, null))
+ whenever(ambientState.isExpansionChanging).thenReturn(true)
+ whenever(ambientState.expansionFraction).thenReturn(expansionFraction)
+ whenever(hostLayoutController.speedBumpIndex).thenReturn(0)
+
+ shelf.updateState(StackScrollAlgorithmState(), ambientState)
+
+ assertEquals(expectedAlpha, shelf.viewState.alpha)
+ }
+}
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 3c22edc0fcf2..6ae021b48f66 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
@@ -250,7 +250,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
// Validate that when the animation ends the stackEndHeight is recalculated immediately
clearInvocations(mAmbientState);
mStackScroller.setPanelFlinging(false);
- verify(mAmbientState).setIsFlinging(eq(false));
+ verify(mAmbientState).setFlinging(eq(false));
verify(mAmbientState).setStackEndHeight(anyFloat());
verify(mAmbientState).setStackHeight(anyFloat());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 35d2363b1c2a..40aec82f5a85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -20,7 +20,10 @@ import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@SmallTest
@@ -35,7 +38,6 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
}
-
private val ambientState = AmbientState(
context,
dumpManager,
@@ -115,29 +117,54 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
}
@Test
- fun resetViewStates_isExpansionChanging_viewBecomesTransparent() {
+ fun resetViewStates_expansionChanging_notificationBecomesTransparent() {
whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
- ambientState.isExpansionChanging = true
- ambientState.expansionFraction = 0.25f
- stackScrollAlgorithm.initView(context)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.25f,
+ expectedAlpha = 0.0f
+ )
+ }
- stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+ @Test
+ fun resetViewStates_expansionChangingWhileBouncerInTransit_viewBecomesTransparent() {
+ whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.85f,
+ expectedAlpha = 0.0f
+ )
+ }
- val expected = getContentAlpha(0.25f)
- assertThat(notificationRow.viewState.alpha).isEqualTo(expected)
+ @Test
+ fun resetViewStates_expansionChanging_notificationAlphaUpdated() {
+ whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.6f,
+ expectedAlpha = getContentAlpha(0.6f)
+ )
}
@Test
- fun resetViewStates_isExpansionChangingWhileBouncerInTransit_viewBecomesTransparent() {
+ fun resetViewStates_expansionChangingWhileBouncerInTransit_notificationAlphaUpdated() {
whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.95f,
+ expectedAlpha = aboutToShowBouncerProgress(0.95f)
+ )
+ }
+
+ @Test
+ fun resetViewStates_expansionChanging_shelfUpdated() {
+ ambientState.shelf = notificationShelf
ambientState.isExpansionChanging = true
- ambientState.expansionFraction = 0.25f
+ ambientState.expansionFraction = 0.6f
stackScrollAlgorithm.initView(context)
stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
- val expected = aboutToShowBouncerProgress(0.25f)
- assertThat(notificationRow.viewState.alpha).isEqualTo(expected)
+ verify(notificationShelf).updateState(
+ /* algorithmState= */any(),
+ /* ambientState= */eq(ambientState)
+ )
}
@Test
@@ -179,7 +206,28 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
}
@Test
- fun resetViewStates_hiddenShelf_viewAlphaDoesNotChange() {
+ fun resetViewStates_hiddenShelf_allRowsBecomesTransparent() {
+ hostView.removeAllViews()
+ val row1 = mockExpandableNotificationRow()
+ hostView.addView(row1)
+ val row2 = mockExpandableNotificationRow()
+ hostView.addView(row2)
+
+ ambientState.setStatusBarState(StatusBarState.KEYGUARD)
+ ambientState.hideAmount = 0.25f
+ notificationShelf.viewState.hidden = true
+ ambientState.shelf = notificationShelf
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ val expected = 1f - ambientState.hideAmount
+ assertThat(row1.viewState.alpha).isEqualTo(expected)
+ assertThat(row2.viewState.alpha).isEqualTo(expected)
+ }
+
+ @Test
+ fun resetViewStates_hiddenShelf_shelfAlphaDoesNotChange() {
val expected = notificationShelf.viewState.alpha
notificationShelf.viewState.hidden = true
ambientState.shelf = notificationShelf
@@ -458,4 +506,23 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
/* originalCornerRoundness= */ 1f)
assertEquals(1f, currentRoundness)
}
+
+ private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction: Float,
+ expectedAlpha: Float
+ ) {
+ ambientState.isExpansionChanging = true
+ ambientState.expansionFraction = expansionFraction
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(notificationRow.viewState.alpha).isEqualTo(expectedAlpha)
+ }
+}
+
+private fun mockExpandableNotificationRow(): ExpandableNotificationRow {
+ return mock(ExpandableNotificationRow::class.java).apply {
+ whenever(viewState).thenReturn(ExpandableViewState())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 5c9871a01536..9de9db1d39e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
import android.os.PowerManager;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -61,10 +62,10 @@ import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
public class DozeServiceHostTest extends SysuiTestCase {
private DozeServiceHost mDozeServiceHost;
@@ -92,6 +93,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
@Mock private View mAmbientIndicationContainer;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private AuthController mAuthController;
+ @Mock private DozeHost.Callback mCallback;
@Before
public void setup() {
@@ -100,7 +102,7 @@ public class DozeServiceHostTest extends SysuiTestCase {
mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager,
mBatteryController, mScrimController, () -> mBiometricUnlockController,
mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
- mKeyguardUpdateMonitor, mPulseExpansionHandler, Optional.empty(),
+ mKeyguardUpdateMonitor, mPulseExpansionHandler,
mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
mAuthController, mNotificationIconAreaController);
@@ -114,16 +116,19 @@ public class DozeServiceHostTest extends SysuiTestCase {
@Test
public void testStartStopDozing() {
+ mDozeServiceHost.addCallback(mCallback);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
assertFalse(mDozeServiceHost.getDozingRequested());
mDozeServiceHost.startDozing();
+ verify(mCallback).onDozingChanged(eq(true));
verify(mStatusBarStateController).setIsDozing(eq(true));
verify(mCentralSurfaces).updateIsKeyguard();
mDozeServiceHost.stopDozing();
+ verify(mCallback).onDozingChanged(eq(false));
verify(mStatusBarStateController).setIsDozing(eq(false));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
deleted file mode 100644
index 3440fa5ac9b1..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.android.systemui.statusbar.phone
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.LayoutInflater
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.assist.AssistManager
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.policy.AccessibilityController
-import com.android.systemui.statusbar.policy.FlashlightController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.tuner.TunerService
-import java.util.concurrent.Executor
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class KeyguardBottomAreaTest : SysuiTestCase() {
-
- @Mock
- private lateinit var mCentralSurfaces: CentralSurfaces
- private lateinit var mKeyguardBottomArea: KeyguardBottomAreaView
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- // Mocked dependencies
- mDependency.injectMockDependency(AccessibilityController::class.java)
- mDependency.injectMockDependency(ActivityStarter::class.java)
- mDependency.injectMockDependency(AssistManager::class.java)
- mDependency.injectTestDependency(Executor::class.java, Executor { it.run() })
- mDependency.injectMockDependency(FlashlightController::class.java)
- mDependency.injectMockDependency(KeyguardStateController::class.java)
- mDependency.injectMockDependency(TunerService::class.java)
-
- mKeyguardBottomArea = LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
- }
-
- @Test
- fun initFrom_doesntCrash() {
- val other = LayoutInflater.from(mContext).inflate(R.layout.keyguard_bottom_area,
- null, false) as KeyguardBottomAreaView
-
- other.initFrom(mKeyguardBottomArea)
- }
-}
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 2dcdcfce56eb..e790d85763d0 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
@@ -51,6 +51,7 @@ import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -277,6 +278,17 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
+ public void onPanelExpansionChanged_neverTranslatesBouncerWhenShadeLocked() {
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ expansionEvent(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false));
+ verify(mBouncer, never()).setExpansion(anyFloat());
+ }
+
+ @Test
public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() {
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
verify(mCentralSurfaces).animateKeyguardUnoccluding();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
new file mode 100644
index 000000000000..6dbee2f26ff9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.shared.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.HIDDEN_ICONS_TUNABLE_KEY
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+class ConnectivityRepositoryImplTest : SysuiTestCase() {
+
+ private lateinit var underTest: ConnectivityRepositoryImpl
+
+ @Mock private lateinit var connectivitySlots: ConnectivitySlots
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var logger: ConnectivityPipelineLogger
+ private lateinit var scope: CoroutineScope
+ @Mock private lateinit var tunerService: TunerService
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ scope = CoroutineScope(IMMEDIATE)
+
+ underTest = ConnectivityRepositoryImpl(
+ connectivitySlots,
+ context,
+ dumpManager,
+ logger,
+ scope,
+ tunerService,
+ )
+ }
+
+ @After
+ fun tearDown() {
+ scope.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+ context.getOrCreateTestableResources().addOverride(
+ DEFAULT_HIDDEN_ICONS_RESOURCE,
+ arrayOf(SLOT_WIFI, SLOT_ETHERNET)
+ )
+ // Re-create our [ConnectivityRepositoryImpl], since it fetches
+ // config_statusBarIconsToExclude when it's first constructed
+ underTest = ConnectivityRepositoryImpl(
+ connectivitySlots,
+ context,
+ dumpManager,
+ logger,
+ scope,
+ tunerService,
+ )
+
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_slotNamesAdded_flowHasSlots() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+
+ assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_wrongKey_doesNotUpdate() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+
+ // WHEN onTuningChanged with the wrong key
+ getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
+ yield()
+
+ // THEN we didn't update our value and still have the old one
+ assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_slotNamesAddedThenNull_flowHasDefault() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+ context.getOrCreateTestableResources().addOverride(
+ DEFAULT_HIDDEN_ICONS_RESOURCE,
+ arrayOf(SLOT_WIFI, SLOT_ETHERNET)
+ )
+ // Re-create our [ConnectivityRepositoryImpl], since it fetches
+ // config_statusBarIconsToExclude when it's first constructed
+ underTest = ConnectivityRepositoryImpl(
+ connectivitySlots,
+ context,
+ dumpManager,
+ logger,
+ scope,
+ tunerService,
+ )
+
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ // First, update the slots
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
+ assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
+
+ // WHEN we update to a null value
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
+ yield()
+
+ // THEN we go back to our default value
+ assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() = runBlocking(IMMEDIATE) {
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ whenever(connectivitySlots.getSlotFromName(SLOT_WIFI))
+ .thenReturn(ConnectivitySlot.WIFI)
+ whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
+
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
+
+ assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_someEmptySlotNames_flowHasValidSlotsOnly() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ // WHEN there's empty and blank slot names
+ getTunable().onTuningChanged(
+ HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE, ,,$SLOT_WIFI"
+ )
+
+ // THEN we skip that slot but still process the other ones
+ assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() = runBlocking(IMMEDIATE) {
+ var latest: Set<ConnectivitySlot>? = null
+ val job = underTest
+ .forceHiddenSlots
+ .onEach { latest = it }
+ .launchIn(this)
+
+ whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
+ whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
+ whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
+
+ getTunable().onTuningChanged(
+ HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,,$SLOT_WIFI,$SLOT_ETHERNET,,,"
+ )
+
+ assertThat(latest).isEmpty()
+
+ job.cancel()
+ }
+
+ @Test
+ fun forceHiddenSlots_newSubscriberGetsCurrentValue() = runBlocking(IMMEDIATE) {
+ setUpEthernetWifiMobileSlotNames()
+
+ var latest1: Set<ConnectivitySlot>? = null
+ val job1 = underTest
+ .forceHiddenSlots
+ .onEach { latest1 = it }
+ .launchIn(this)
+
+ getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
+
+ assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
+
+ // WHEN we add a second subscriber after having already emitted a value
+ var latest2: Set<ConnectivitySlot>? = null
+ val job2 = underTest
+ .forceHiddenSlots
+ .onEach { latest2 = it }
+ .launchIn(this)
+
+ // THEN the second subscribe receives the already-emitted value
+ assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
+
+ job1.cancel()
+ job2.cancel()
+ }
+
+ private fun getTunable(): TunerService.Tunable {
+ val callbackCaptor = argumentCaptor<TunerService.Tunable>()
+ Mockito.verify(tunerService).addTunable(callbackCaptor.capture(), any())
+ return callbackCaptor.value!!
+ }
+
+ private fun setUpEthernetWifiMobileSlotNames() {
+ whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET))
+ .thenReturn(ConnectivitySlot.ETHERNET)
+ whenever(connectivitySlots.getSlotFromName(SLOT_WIFI))
+ .thenReturn(ConnectivitySlot.WIFI)
+ whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE))
+ .thenReturn(ConnectivitySlot.MOBILE)
+ }
+
+ companion object {
+ private const val SLOT_ETHERNET = "ethernet"
+ private const val SLOT_WIFI = "wifi"
+ private const val SLOT_MOBILE = "mobile"
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
new file mode 100644
index 000000000000..bd70034b13de
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.data.repository
+
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/** Fake implementation of [ConnectivityRepository] exposing set methods for all the flows. */
+class FakeConnectivityRepository : ConnectivityRepository {
+ private val _forceHiddenIcons: MutableStateFlow<Set<ConnectivitySlot>> =
+ MutableStateFlow(emptySet())
+ override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> = _forceHiddenIcons
+
+ fun setForceHiddenIcons(hiddenIcons: Set<ConnectivitySlot>) {
+ _forceHiddenIcons.value = hiddenIcons
+ }
+}
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/WifiInteractorTest.kt
index 9d8b4bcabc8a..e896749d9a94 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/WifiInteractorTest.kt
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
@@ -37,18 +39,22 @@ class WifiInteractorTest : SysuiTestCase() {
private lateinit var underTest: WifiInteractor
- private lateinit var repository: FakeWifiRepository
+ private lateinit var connectivityRepository: FakeConnectivityRepository
+ private lateinit var wifiRepository: FakeWifiRepository
@Before
fun setUp() {
- repository = FakeWifiRepository()
- underTest = WifiInteractor(repository)
+ connectivityRepository = FakeConnectivityRepository()
+ wifiRepository = FakeWifiRepository()
+ underTest = WifiInteractor(connectivityRepository, wifiRepository)
}
@Test
fun hasActivityIn_noInOrOut_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = false))
+ wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
+ )
var latest: Boolean? = null
val job = underTest
@@ -63,8 +69,10 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_onlyOut_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = true))
+ wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
+ )
var latest: Boolean? = null
val job = underTest
@@ -79,8 +87,10 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_onlyIn_outputsTrue() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
+ wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
var latest: Boolean? = null
val job = underTest
@@ -95,8 +105,10 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_inAndOut_outputsTrue() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+ wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
var latest: Boolean? = null
val job = underTest
@@ -111,8 +123,10 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_ssidNull_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = null))
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = null))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
var latest: Boolean? = null
val job = underTest
@@ -127,8 +141,10 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_inactiveNetwork_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.Inactive)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
var latest: Boolean? = null
val job = underTest
@@ -143,8 +159,10 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_carrierMergedNetwork_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
var latest: Boolean? = null
val job = underTest
@@ -159,7 +177,7 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_multipleChanges_multipleOutputChanges() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
+ wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
var latest: Boolean? = null
val job = underTest
@@ -168,23 +186,33 @@ class WifiInteractorTest : SysuiTestCase() {
.launchIn(this)
// Conduct a series of changes and verify we catch each of them in succession
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
yield()
assertThat(latest).isTrue()
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = true))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
+ )
yield()
assertThat(latest).isFalse()
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
yield()
assertThat(latest).isTrue()
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
yield()
assertThat(latest).isTrue()
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = false))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
+ )
yield()
assertThat(latest).isFalse()
@@ -200,7 +228,7 @@ class WifiInteractorTest : SysuiTestCase() {
ssid = "AB",
passpointProviderFriendlyName = "friendly"
)
- repository.setWifiNetwork(wifiNetwork)
+ wifiRepository.setWifiNetwork(wifiNetwork)
var latest: WifiNetworkModel? = null
val job = underTest
@@ -213,6 +241,36 @@ class WifiInteractorTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun isForceHidden_repoHasWifiHidden_outputsTrue() = runBlocking(IMMEDIATE) {
+ connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+
+ var latest: Boolean? = null
+ val job = underTest
+ .isForceHidden
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun isForceHidden_repoDoesNotHaveWifiHidden_outputsFalse() = runBlocking(IMMEDIATE) {
+ connectivityRepository.setForceHiddenIcons(setOf())
+
+ var latest: Boolean? = null
+ val job = underTest
+ .isForceHidden
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
companion object {
val VALID_WIFI_NETWORK_MODEL = WifiNetworkModel.Active(networkId = 1, ssid = "AB")
}
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 f0a775b52297..43103a065e68 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
@@ -17,17 +17,24 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import androidx.test.filters.SmallTest
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
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.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -50,60 +57,101 @@ class WifiViewModelTest : SysuiTestCase() {
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var logger: ConnectivityPipelineLogger
@Mock private lateinit var constants: WifiConstants
- private lateinit var repository: FakeWifiRepository
+ private lateinit var connectivityRepository: FakeConnectivityRepository
+ private lateinit var wifiRepository: FakeWifiRepository
private lateinit var interactor: WifiInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- repository = FakeWifiRepository()
- interactor = WifiInteractor(repository)
+ connectivityRepository = FakeConnectivityRepository()
+ wifiRepository = FakeWifiRepository()
+ interactor = WifiInteractor(connectivityRepository, wifiRepository)
underTest = WifiViewModel(
statusBarPipelineFlags,
constants,
+ context,
logger,
interactor
)
}
@Test
- fun wifiIconResId_inactiveNetwork_outputsNoNetworkIcon() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.Inactive)
+ fun wifiIcon_forceHidden_outputsNull() = runBlocking(IMMEDIATE) {
+ connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2))
- var latest: Int? = null
+ var latest: Icon? = null
val job = underTest
- .wifiIconResId
- .onEach { latest = it }
- .launchIn(this)
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
- assertThat(latest).isEqualTo(WIFI_NO_NETWORK)
+ assertThat(latest).isNull()
job.cancel()
}
@Test
- fun wifiIconResId_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
+ fun wifiIcon_notForceHidden_outputsVisible() = runBlocking(IMMEDIATE) {
+ connectivityRepository.setForceHiddenIcons(setOf())
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2))
- var latest: Int? = null
+ var latest: Icon? = null
val job = underTest
- .wifiIconResId
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).isInstanceOf(Icon.Resource::class.java)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiIcon_inactiveNetwork_outputsNoNetworkIcon() = runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+
+ var latest: Icon? = null
+ val job = underTest
+ .wifiIcon
.onEach { latest = it }
.launchIn(this)
+ assertThat(latest).isInstanceOf(Icon.Resource::class.java)
+ val icon = latest as Icon.Resource
+ assertThat(icon.res).isEqualTo(WIFI_NO_NETWORK)
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(WIFI_NO_CONNECTION))
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(NO_INTERNET))
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiIcon_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
+
+ var latest: Icon? = null
+ val job = underTest
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
+
assertThat(latest).isNull()
job.cancel()
}
@Test
- fun wifiIconResId_isActiveNullLevel_outputsNull() = runBlocking(IMMEDIATE) {
- repository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = null))
+ fun wifiIcon_isActiveNullLevel_outputsNull() = runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = null))
- var latest: Int? = null
+ var latest: Icon? = null
val job = underTest
- .wifiIconResId
+ .wifiIcon
.onEach { latest = it }
.launchIn(this)
@@ -113,10 +161,10 @@ class WifiViewModelTest : SysuiTestCase() {
}
@Test
- fun wifiIconResId_isActiveAndValidated_level1_outputsFull1Icon() = runBlocking(IMMEDIATE) {
+ fun wifiIcon_isActiveAndValidated_level1_outputsFull1Icon() = runBlocking(IMMEDIATE) {
val level = 1
- repository.setWifiNetwork(
+ wifiRepository.setWifiNetwork(
WifiNetworkModel.Active(
NETWORK_ID,
isValidated = true,
@@ -124,22 +172,28 @@ class WifiViewModelTest : SysuiTestCase() {
)
)
- var latest: Int? = null
+ var latest: Icon? = null
val job = underTest
- .wifiIconResId
- .onEach { latest = it }
- .launchIn(this)
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
- assertThat(latest).isEqualTo(WIFI_FULL_ICONS[level])
+ assertThat(latest).isInstanceOf(Icon.Resource::class.java)
+ val icon = latest as Icon.Resource
+ assertThat(icon.res).isEqualTo(WIFI_FULL_ICONS[level])
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(WIFI_CONNECTION_STRENGTH[level]))
+ assertThat(icon.contentDescription?.getAsString())
+ .doesNotContain(context.getString(NO_INTERNET))
job.cancel()
}
@Test
- fun wifiIconResId_isActiveAndNotValidated_level4_outputsEmpty4Icon() = runBlocking(IMMEDIATE) {
+ fun wifiIcon_isActiveAndNotValidated_level4_outputsEmpty4Icon() = runBlocking(IMMEDIATE) {
val level = 4
- repository.setWifiNetwork(
+ wifiRepository.setWifiNetwork(
WifiNetworkModel.Active(
NETWORK_ID,
isValidated = false,
@@ -147,13 +201,19 @@ class WifiViewModelTest : SysuiTestCase() {
)
)
- var latest: Int? = null
+ var latest: Icon? = null
val job = underTest
- .wifiIconResId
- .onEach { latest = it }
- .launchIn(this)
+ .wifiIcon
+ .onEach { latest = it }
+ .launchIn(this)
- assertThat(latest).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
+ assertThat(latest).isInstanceOf(Icon.Resource::class.java)
+ val icon = latest as Icon.Resource
+ assertThat(icon.res).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(WIFI_CONNECTION_STRENGTH[level]))
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(NO_INTERNET))
job.cancel()
}
@@ -161,7 +221,7 @@ class WifiViewModelTest : SysuiTestCase() {
@Test
fun activityInVisible_showActivityConfigFalse_outputsFalse() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(false)
- repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
@@ -178,7 +238,7 @@ class WifiViewModelTest : SysuiTestCase() {
@Test
fun activityInVisible_showActivityConfigFalse_noUpdatesReceived() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(false)
- repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
@@ -187,7 +247,9 @@ class WifiViewModelTest : SysuiTestCase() {
.launchIn(this)
// Update the repo to have activityIn
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
yield()
// Verify that we didn't update to activityIn=true (because our config is false)
@@ -199,7 +261,7 @@ class WifiViewModelTest : SysuiTestCase() {
@Test
fun activityInVisible_showActivityConfigTrue_outputsUpdate() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true)
- repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null
val job = underTest
@@ -208,7 +270,9 @@ class WifiViewModelTest : SysuiTestCase() {
.launchIn(this)
// Update the repo to have activityIn
- repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
+ wifiRepository.setWifiActivity(
+ WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
yield()
// Verify that we updated to activityIn=true
@@ -217,6 +281,13 @@ class WifiViewModelTest : SysuiTestCase() {
job.cancel()
}
+ private fun ContentDescription.getAsString(): String? {
+ return when (this) {
+ is ContentDescription.Loaded -> this.description
+ is ContentDescription.Resource -> context.getString(this.res)
+ }
+ }
+
companion object {
private const val NETWORK_ID = 2
private val ACTIVE_VALID_WIFI_NETWORK = WifiNetworkModel.Active(NETWORK_ID, ssid = "AB")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index 4a8170fc2955..8f363efd9f51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -57,6 +58,8 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
private DumpManager mDumpManager;
@Mock
private Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
+ @Mock
+ private KeyguardUpdateMonitorLogger mLogger;
@Before
public void setup() {
@@ -66,6 +69,7 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
mKeyguardUpdateMonitor,
mLockPatternUtils,
mKeyguardUnlockAnimationControllerLazy,
+ mLogger,
mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
index c3805ad36533..8290dab19bdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
@@ -57,8 +57,6 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
@Mock
private lateinit var inflatedUserDetailItemView: KeyguardUserDetailItemView
@Mock
- private lateinit var userInfo: UserInfo
- @Mock
private lateinit var layoutInflater: LayoutInflater
@Mock
private lateinit var keyguardUserSwitcherController: KeyguardUserSwitcherController
@@ -188,7 +186,7 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
private fun createUserRecord(isCurrentUser: Boolean, isGuestUser: Boolean) =
UserRecord(
- userInfo,
+ UserInfo(0 /* id */, "name", 0 /* flags */),
picture,
isGuestUser,
isCurrentUser,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index f1330682b0e1..e616c26377d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.temporarydisplay
import android.content.Context
import android.content.pm.ApplicationInfo
@@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -38,7 +39,6 @@ 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.time.FakeSystemClock
-import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -52,8 +52,8 @@ import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
-class MediaTttChipControllerCommonTest : SysuiTestCase() {
- private lateinit var controllerCommon: TestControllerCommon
+class TemporaryViewDisplayControllerTest : SysuiTestCase() {
+ private lateinit var underTest: TestController
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
@@ -72,8 +72,6 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
@Mock
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
private lateinit var powerManager: PowerManager
@Before
@@ -97,11 +95,10 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
- controllerCommon = TestControllerCommon(
+ underTest = TestController(
context,
logger,
windowManager,
- viewUtil,
fakeExecutor,
accessibilityManager,
configurationController,
@@ -110,43 +107,43 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
}
@Test
- fun displayChip_chipAdded() {
- controllerCommon.displayChip(getState())
+ fun displayView_viewAdded() {
+ underTest.displayView(getState())
verify(windowManager).addView(any(), any())
}
@Test
- fun displayChip_screenOff_screenWakes() {
+ fun displayView_screenOff_screenWakes() {
whenever(powerManager.isScreenOn).thenReturn(false)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
verify(powerManager).wakeUp(any(), any(), any())
}
@Test
- fun displayChip_screenAlreadyOn_screenNotWoken() {
+ fun displayView_screenAlreadyOn_screenNotWoken() {
whenever(powerManager.isScreenOn).thenReturn(true)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
verify(powerManager, never()).wakeUp(any(), any(), any())
}
@Test
- fun displayChip_twice_chipNotAddedTwice() {
- controllerCommon.displayChip(getState())
+ fun displayView_twice_viewNotAddedTwice() {
+ underTest.displayView(getState())
reset(windowManager)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
verify(windowManager, never()).addView(any(), any())
}
@Test
- fun displayChip_chipDoesNotDisappearsBeforeTimeout() {
+ fun displayView_viewDoesNotDisappearsBeforeTimeout() {
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
reset(windowManager)
fakeClock.advanceTime(TIMEOUT_MS - 1)
@@ -155,9 +152,9 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
}
@Test
- fun displayChip_chipDisappearsAfterTimeout() {
+ fun displayView_viewDisappearsAfterTimeout() {
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
reset(windowManager)
fakeClock.advanceTime(TIMEOUT_MS + 1)
@@ -166,176 +163,176 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
}
@Test
- fun displayChip_calledAgainBeforeTimeout_timeoutReset() {
- // First, display the chip
+ fun displayView_calledAgainBeforeTimeout_timeoutReset() {
+ // First, display the view
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
- // After some time, re-display the chip
+ // After some time, re-display the view
val waitTime = 1000L
fakeClock.advanceTime(waitTime)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
// Wait until the timeout for the first display would've happened
fakeClock.advanceTime(TIMEOUT_MS - waitTime + 1)
- // Verify we didn't hide the chip
+ // Verify we didn't hide the view
verify(windowManager, never()).removeView(any())
}
@Test
- fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() {
- // First, display the chip
+ fun displayView_calledAgainBeforeTimeout_eventuallyTimesOut() {
+ // First, display the view
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
- // After some time, re-display the chip
+ // After some time, re-display the view
fakeClock.advanceTime(1000L)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
- // Ensure we still hide the chip eventually
+ // Ensure we still hide the view eventually
fakeClock.advanceTime(TIMEOUT_MS + 1)
verify(windowManager).removeView(any())
}
@Test
- fun displayScaleChange_chipReinflatedWithMostRecentState() {
- controllerCommon.displayChip(getState(name = "First name"))
- controllerCommon.displayChip(getState(name = "Second name"))
+ fun displayScaleChange_viewReinflatedWithMostRecentState() {
+ underTest.displayView(getState(name = "First name"))
+ underTest.displayView(getState(name = "Second name"))
reset(windowManager)
getConfigurationListener().onDensityOrFontScaleChanged()
verify(windowManager).removeView(any())
verify(windowManager).addView(any(), any())
- assertThat(controllerCommon.mostRecentChipInfo?.name).isEqualTo("Second name")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
}
@Test
- fun removeChip_chipRemovedAndRemovalLogged() {
- // First, add the chip
- controllerCommon.displayChip(getState())
+ fun removeView_viewRemovedAndRemovalLogged() {
+ // First, add the view
+ underTest.displayView(getState())
// Then, remove it
val reason = "test reason"
- controllerCommon.removeChip(reason)
+ underTest.removeView(reason)
verify(windowManager).removeView(any())
verify(logger).logChipRemoval(reason)
}
@Test
- fun removeChip_noAdd_viewNotRemoved() {
- controllerCommon.removeChip("reason")
+ fun removeView_noAdd_viewNotRemoved() {
+ underTest.removeView("reason")
verify(windowManager, never()).removeView(any())
}
@Test
fun setIcon_nullAppIconDrawableAndNullPackageName_stillHasIcon() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, appPackageName = null, appIconDrawableOverride = null)
+ underTest.setIcon(view, appPackageName = null, appIconDrawableOverride = null)
- assertThat(chipView.getAppIconView().drawable).isNotNull()
+ assertThat(view.getAppIconView().drawable).isNotNull()
}
@Test
fun setIcon_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(
- chipView, appPackageName = "fakePackageName", appIconDrawableOverride = null
+ underTest.setIcon(
+ view, appPackageName = "fakePackageName", appIconDrawableOverride = null
)
- assertThat(chipView.getAppIconView().drawable).isNotNull()
+ assertThat(view.getAppIconView().drawable).isNotNull()
}
@Test
fun setIcon_nullAppIconDrawable_iconIsFromPackageName() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, PACKAGE_NAME, appIconDrawableOverride = null, null)
+ underTest.setIcon(view, PACKAGE_NAME, appIconDrawableOverride = null, null)
- assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
+ assertThat(view.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
}
@Test
fun setIcon_hasAppIconDrawable_iconIsDrawable() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
val drawable = context.getDrawable(R.drawable.ic_alarm)!!
- controllerCommon.setIcon(chipView, PACKAGE_NAME, drawable, null)
+ underTest.setIcon(view, PACKAGE_NAME, drawable, null)
- assertThat(chipView.getAppIconView().drawable).isEqualTo(drawable)
+ assertThat(view.getAppIconView().drawable).isEqualTo(drawable)
}
@Test
fun setIcon_nullAppNameAndNullPackageName_stillHasContentDescription() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, appPackageName = null, appNameOverride = null)
+ underTest.setIcon(view, appPackageName = null, appNameOverride = null)
- assertThat(chipView.getAppIconView().contentDescription.toString()).isNotEmpty()
+ assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty()
}
@Test
fun setIcon_nullAppNameAndInvalidPackageName_stillHasContentDescription() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(
- chipView, appPackageName = "fakePackageName", appNameOverride = null
+ underTest.setIcon(
+ view, appPackageName = "fakePackageName", appNameOverride = null
)
- assertThat(chipView.getAppIconView().contentDescription.toString()).isNotEmpty()
+ assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty()
}
@Test
fun setIcon_nullAppName_iconContentDescriptionIsFromPackageName() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appNameOverride = null)
+ underTest.setIcon(view, PACKAGE_NAME, null, appNameOverride = null)
- assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
+ assertThat(view.getAppIconView().contentDescription).isEqualTo(APP_NAME)
}
@Test
fun setIcon_hasAppName_iconContentDescriptionIsAppNameOverride() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
val appName = "Override App Name"
- controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appName)
+ underTest.setIcon(view, PACKAGE_NAME, null, appName)
- assertThat(chipView.getAppIconView().contentDescription).isEqualTo(appName)
+ assertThat(view.getAppIconView().contentDescription).isEqualTo(appName)
}
@Test
fun setIcon_iconSizeMatchesGetIconSize() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, PACKAGE_NAME)
- chipView.measure(
+ underTest.setIcon(view, PACKAGE_NAME)
+ view.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
- assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE)
- assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE)
+ assertThat(view.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE)
+ assertThat(view.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE)
}
- private fun getState(name: String = "name") = ChipInfo(name)
+ private fun getState(name: String = "name") = ViewInfo(name)
- private fun getChipView(): ViewGroup {
+ private fun getView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any())
return viewCaptor.value as ViewGroup
@@ -349,37 +346,35 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
return callbackCaptor.value
}
- inner class TestControllerCommon(
+ inner class TestController(
context: Context,
logger: MediaTttLogger,
windowManager: WindowManager,
- viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
- ) : MediaTttChipControllerCommon<ChipInfo>(
+ ) : TemporaryViewDisplayController<ViewInfo>(
context,
logger,
windowManager,
- viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
powerManager,
R.layout.media_ttt_chip,
) {
- var mostRecentChipInfo: ChipInfo? = null
+ var mostRecentViewInfo: ViewInfo? = null
override val windowLayoutParams = commonWindowLayoutParams
- override fun updateChipView(newChipInfo: ChipInfo, currentChipView: ViewGroup) {
- super.updateChipView(newChipInfo, currentChipView)
- mostRecentChipInfo = newChipInfo
+ override fun updateView(newInfo: ViewInfo, currentView: ViewGroup) {
+ super.updateView(newInfo, currentView)
+ mostRecentViewInfo = newInfo
}
override fun getIconSize(isAppIcon: Boolean): Int = ICON_SIZE
}
- inner class ChipInfo(val name: String) : ChipInfoCommon {
+ inner class ViewInfo(val name: String) : TemporaryViewInfo {
override fun getTimeoutMs() = 1L
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index f51f78315dbd..8645298682d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -18,24 +18,28 @@ package com.android.systemui.unfold
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
-import android.os.Handler
import android.os.PowerManager
import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.testing.TestableLooper.RunWithLooper
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.unfold.util.FoldableDeviceStates
import com.android.systemui.unfold.util.FoldableTestUtils
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,7 +53,6 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@SmallTest
-@RunWithLooper
class FoldAodAnimationControllerTest : SysuiTestCase() {
@Mock lateinit var deviceStateManager: DeviceStateManager
@@ -74,26 +77,15 @@ class FoldAodAnimationControllerTest : SysuiTestCase() {
private lateinit var deviceStates: FoldableDeviceStates
- private lateinit var testableLooper: TestableLooper
+ lateinit var keyguardRepository: FakeKeyguardRepository
- lateinit var foldAodAnimationController: FoldAodAnimationController
+ lateinit var underTest: FoldAodAnimationController
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- testableLooper = TestableLooper.get(this)
-
- foldAodAnimationController =
- FoldAodAnimationController(
- Handler(testableLooper.looper),
- context.mainExecutor,
- context,
- deviceStateManager,
- wakefulnessLifecycle,
- globalSettings,
- latencyTracker,
- )
- .apply { initialize(centralSurfaces, lightRevealScrim) }
+
deviceStates = FoldableTestUtils.findDeviceStates(context)
whenever(notificationPanelViewController.view).thenReturn(viewGroup)
@@ -107,60 +99,102 @@ class FoldAodAnimationControllerTest : SysuiTestCase() {
val onActionStarted = it.arguments[0] as Runnable
onActionStarted.run()
}
- verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
- foldAodAnimationController.setIsDozing(dozing = true)
- setAodEnabled(enabled = true)
- sendFoldEvent(folded = false)
+ keyguardRepository = FakeKeyguardRepository()
+ val keyguardInteractor = KeyguardInteractor(repository = keyguardRepository)
+
+ // Needs to be run on the main thread
+ runBlocking(IMMEDIATE) {
+ underTest =
+ FoldAodAnimationController(
+ fakeExecutor,
+ context,
+ deviceStateManager,
+ wakefulnessLifecycle,
+ globalSettings,
+ latencyTracker,
+ { keyguardInteractor },
+ )
+ .apply { initialize(centralSurfaces, lightRevealScrim) }
+
+ verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
+
+ setAodEnabled(enabled = true)
+ sendFoldEvent(folded = false)
+ }
}
@Test
- fun onFolded_aodDisabled_doesNotLogLatency() {
- setAodEnabled(enabled = false)
+ fun onFolded_aodDisabled_doesNotLogLatency() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozing(this)
+ keyguardRepository.setDozing(true)
+ setAodEnabled(enabled = false)
- fold()
- simulateScreenTurningOn()
+ yield()
- verifyNoMoreInteractions(latencyTracker)
- }
+ fold()
+ simulateScreenTurningOn()
+
+ verifyNoMoreInteractions(latencyTracker)
+
+ job.cancel()
+ }
@Test
- fun onFolded_aodEnabled_logsLatency() {
- setAodEnabled(enabled = true)
+ fun onFolded_aodEnabled_logsLatency() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozing(this)
+ keyguardRepository.setDozing(true)
+ setAodEnabled(enabled = true)
- fold()
- simulateScreenTurningOn()
+ yield()
- verify(latencyTracker).onActionStart(any())
- verify(latencyTracker).onActionEnd(any())
- }
+ fold()
+ simulateScreenTurningOn()
+
+ verify(latencyTracker).onActionStart(any())
+ verify(latencyTracker).onActionEnd(any())
+
+ job.cancel()
+ }
@Test
- fun onFolded_animationCancelled_doesNotLogLatency() {
- setAodEnabled(enabled = true)
+ fun onFolded_animationCancelled_doesNotLogLatency() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozing(this)
+ keyguardRepository.setDozing(true)
+ setAodEnabled(enabled = true)
- fold()
- foldAodAnimationController.onScreenTurningOn({})
- foldAodAnimationController.onStartedWakingUp()
- testableLooper.processAllMessages()
+ yield()
- verify(latencyTracker).onActionStart(any())
- verify(latencyTracker).onActionCancel(any())
- }
+ fold()
+ underTest.onScreenTurningOn({})
+ underTest.onStartedWakingUp()
+ fakeExecutor.runAllReady()
+
+ verify(latencyTracker).onActionStart(any())
+ verify(latencyTracker).onActionCancel(any())
+
+ job.cancel()
+ }
private fun simulateScreenTurningOn() {
- foldAodAnimationController.onScreenTurningOn({})
- foldAodAnimationController.onScreenTurnedOn()
- testableLooper.processAllMessages()
+ underTest.onScreenTurningOn({})
+ underTest.onScreenTurnedOn()
+ fakeExecutor.runAllReady()
}
private fun fold() = sendFoldEvent(folded = true)
- private fun setAodEnabled(enabled: Boolean) =
- foldAodAnimationController.onAlwaysOnChanged(alwaysOn = enabled)
+ private fun setAodEnabled(enabled: Boolean) = underTest.onAlwaysOnChanged(alwaysOn = enabled)
private fun sendFoldEvent(folded: Boolean) {
val state = if (folded) deviceStates.folded else deviceStates.unfolded
foldStateListenerCaptor.value.onStateChanged(state)
}
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
index 66367ecfc95c..439beaab6c7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
@@ -24,9 +24,11 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -54,6 +56,10 @@ class UserSwitcherActivityTest : SysuiTestCase() {
private lateinit var userManager: UserManager
@Mock
private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var flags: FeatureFlags
+ @Mock
+ private lateinit var viewModelFactoryLazy: dagger.Lazy<UserSwitcherViewModel.Factory>
@Before
fun setUp() {
@@ -61,11 +67,12 @@ class UserSwitcherActivityTest : SysuiTestCase() {
activity = UserSwitcherActivity(
userSwitcherController,
broadcastDispatcher,
- layoutInflater,
falsingCollector,
falsingManager,
userManager,
- userTracker
+ userTracker,
+ flags,
+ viewModelFactoryLazy,
)
}
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
new file mode 100644
index 000000000000..6b466e1ac2d8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -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.systemui.user.data.repository
+
+import android.content.pm.UserInfo
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class UserRepositoryImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var manager: UserManager
+ @Mock private lateinit var controller: UserSwitcherController
+ @Captor
+ private lateinit var userSwitchCallbackCaptor:
+ ArgumentCaptor<UserSwitcherController.UserSwitchCallback>
+
+ private lateinit var underTest: UserRepositoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(controller.addUsersFromLockScreen).thenReturn(MutableStateFlow(false))
+ whenever(controller.isGuestUserAutoCreated).thenReturn(false)
+ whenever(controller.isGuestUserResetting).thenReturn(false)
+
+ underTest =
+ UserRepositoryImpl(
+ appContext = context,
+ manager = manager,
+ controller = controller,
+ )
+ }
+
+ @Test
+ fun `users - registers for updates`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.users.onEach {}.launchIn(this)
+
+ verify(controller).addUserSwitchCallback(any())
+
+ job.cancel()
+ }
+
+ @Test
+ fun `users - unregisters from updates`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.users.onEach {}.launchIn(this)
+ verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
+
+ job.cancel()
+
+ verify(controller).removeUserSwitchCallback(userSwitchCallbackCaptor.value)
+ }
+
+ @Test
+ fun `users - does not include actions`() =
+ runBlocking(IMMEDIATE) {
+ whenever(controller.users)
+ .thenReturn(
+ arrayListOf(
+ createUserRecord(0, isSelected = true),
+ createActionRecord(UserActionModel.ADD_USER),
+ createUserRecord(1),
+ createUserRecord(2),
+ createActionRecord(UserActionModel.ADD_SUPERVISED_USER),
+ createActionRecord(UserActionModel.ENTER_GUEST_MODE),
+ )
+ )
+ var models: List<UserModel>? = null
+ val job = underTest.users.onEach { models = it }.launchIn(this)
+
+ assertThat(models).hasSize(3)
+ assertThat(models?.get(0)?.id).isEqualTo(0)
+ assertThat(models?.get(0)?.isSelected).isTrue()
+ assertThat(models?.get(1)?.id).isEqualTo(1)
+ assertThat(models?.get(1)?.isSelected).isFalse()
+ assertThat(models?.get(2)?.id).isEqualTo(2)
+ assertThat(models?.get(2)?.isSelected).isFalse()
+ job.cancel()
+ }
+
+ @Test
+ fun selectedUser() =
+ runBlocking(IMMEDIATE) {
+ whenever(controller.users)
+ .thenReturn(
+ arrayListOf(
+ createUserRecord(0, isSelected = true),
+ createUserRecord(1),
+ createUserRecord(2),
+ )
+ )
+ var id: Int? = null
+ val job = underTest.selectedUser.map { it.id }.onEach { id = it }.launchIn(this)
+
+ assertThat(id).isEqualTo(0)
+
+ whenever(controller.users)
+ .thenReturn(
+ arrayListOf(
+ createUserRecord(0),
+ createUserRecord(1),
+ createUserRecord(2, isSelected = true),
+ )
+ )
+ verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
+ userSwitchCallbackCaptor.value.onUserSwitched()
+ assertThat(id).isEqualTo(2)
+
+ job.cancel()
+ }
+
+ @Test
+ fun `actions - unregisters from updates`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.actions.onEach {}.launchIn(this)
+ verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
+
+ job.cancel()
+
+ verify(controller).removeUserSwitchCallback(userSwitchCallbackCaptor.value)
+ }
+
+ @Test
+ fun `actions - registers for updates`() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.actions.onEach {}.launchIn(this)
+
+ verify(controller).addUserSwitchCallback(any())
+
+ job.cancel()
+ }
+
+ @Test
+ fun `actopms - does not include users`() =
+ runBlocking(IMMEDIATE) {
+ whenever(controller.users)
+ .thenReturn(
+ arrayListOf(
+ createUserRecord(0, isSelected = true),
+ createActionRecord(UserActionModel.ADD_USER),
+ createUserRecord(1),
+ createUserRecord(2),
+ createActionRecord(UserActionModel.ADD_SUPERVISED_USER),
+ createActionRecord(UserActionModel.ENTER_GUEST_MODE),
+ )
+ )
+ var models: List<UserActionModel>? = null
+ val job = underTest.actions.onEach { models = it }.launchIn(this)
+
+ assertThat(models).hasSize(3)
+ assertThat(models?.get(0)).isEqualTo(UserActionModel.ADD_USER)
+ assertThat(models?.get(1)).isEqualTo(UserActionModel.ADD_SUPERVISED_USER)
+ assertThat(models?.get(2)).isEqualTo(UserActionModel.ENTER_GUEST_MODE)
+ job.cancel()
+ }
+
+ private fun createUserRecord(id: Int, isSelected: Boolean = false): UserRecord {
+ return UserRecord(
+ info = UserInfo(id, "name$id", 0),
+ isCurrent = isSelected,
+ )
+ }
+
+ private fun createActionRecord(action: UserActionModel): UserRecord {
+ return UserRecord(
+ isAddUser = action == UserActionModel.ADD_USER,
+ isAddSupervisedUser = action == UserActionModel.ADD_SUPERVISED_USER,
+ isGuest = action == UserActionModel.ENTER_GUEST_MODE,
+ )
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
+}
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
new file mode 100644
index 000000000000..e914e2e0a1da
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.nullable
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class UserInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var controller: UserSwitcherController
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ private lateinit var underTest: UserInteractor
+
+ private lateinit var userRepository: FakeUserRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ userRepository = FakeUserRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ underTest =
+ UserInteractor(
+ repository = userRepository,
+ controller = controller,
+ activityStarter = activityStarter,
+ keyguardInteractor =
+ KeyguardInteractor(
+ repository = keyguardRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun `actions - not actionable when locked and locked - no actions`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(UserActionModel.values().toList())
+ userRepository.setActionableWhenLocked(false)
+ keyguardRepository.setKeyguardShowing(true)
+
+ var actions: List<UserActionModel>? = null
+ val job = underTest.actions.onEach { actions = it }.launchIn(this)
+
+ assertThat(actions).isEmpty()
+ job.cancel()
+ }
+
+ @Test
+ fun `actions - not actionable when locked and not locked`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ )
+ )
+ userRepository.setActionableWhenLocked(false)
+ keyguardRepository.setKeyguardShowing(false)
+
+ var actions: List<UserActionModel>? = null
+ val job = underTest.actions.onEach { actions = it }.launchIn(this)
+
+ assertThat(actions)
+ .isEqualTo(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
+ )
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `actions - actionable when locked and not locked`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ )
+ )
+ userRepository.setActionableWhenLocked(true)
+ keyguardRepository.setKeyguardShowing(false)
+
+ var actions: List<UserActionModel>? = null
+ val job = underTest.actions.onEach { actions = it }.launchIn(this)
+
+ assertThat(actions)
+ .isEqualTo(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
+ )
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `actions - actionable when locked and locked`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ )
+ )
+ userRepository.setActionableWhenLocked(true)
+ keyguardRepository.setKeyguardShowing(true)
+
+ var actions: List<UserActionModel>? = null
+ val job = underTest.actions.onEach { actions = it }.launchIn(this)
+
+ assertThat(actions)
+ .isEqualTo(
+ listOf(
+ UserActionModel.ENTER_GUEST_MODE,
+ UserActionModel.ADD_USER,
+ UserActionModel.ADD_SUPERVISED_USER,
+ UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
+ )
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun selectUser() {
+ val userId = 3
+
+ underTest.selectUser(userId)
+
+ verify(controller).onUserSelected(eq(userId), nullable())
+ }
+
+ @Test
+ fun `executeAction - guest`() {
+ underTest.executeAction(UserActionModel.ENTER_GUEST_MODE)
+
+ verify(controller).createAndSwitchToGuestUser(nullable())
+ }
+
+ @Test
+ fun `executeAction - add user`() {
+ underTest.executeAction(UserActionModel.ADD_USER)
+
+ verify(controller).showAddUserDialog(nullable())
+ }
+
+ @Test
+ fun `executeAction - add supervised user`() {
+ underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)
+
+ verify(controller).startSupervisedUserActivity()
+ }
+
+ @Test
+ fun `executeAction - manage users`() {
+ underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
+
+ verify(activityStarter).startActivity(any(), anyBoolean())
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
+}
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
new file mode 100644
index 000000000000..ef4500df3600
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.ui.viewmodel
+
+import android.graphics.drawable.Drawable
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+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
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class UserSwitcherViewModelTest : SysuiTestCase() {
+
+ @Mock private lateinit var controller: UserSwitcherController
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ private lateinit var underTest: UserSwitcherViewModel
+
+ private lateinit var userRepository: FakeUserRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var powerRepository: FakePowerRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ userRepository = FakeUserRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ powerRepository = FakePowerRepository()
+ underTest =
+ UserSwitcherViewModel.Factory(
+ userInteractor =
+ UserInteractor(
+ repository = userRepository,
+ controller = controller,
+ activityStarter = activityStarter,
+ keyguardInteractor =
+ KeyguardInteractor(
+ repository = keyguardRepository,
+ )
+ ),
+ powerInteractor =
+ PowerInteractor(
+ repository = powerRepository,
+ ),
+ )
+ .create(UserSwitcherViewModel::class.java)
+ }
+
+ @Test
+ fun users() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setUsers(
+ listOf(
+ UserModel(
+ id = 0,
+ name = Text.Loaded("zero"),
+ image = USER_IMAGE,
+ isSelected = true,
+ isSelectable = true,
+ ),
+ UserModel(
+ id = 1,
+ name = Text.Loaded("one"),
+ image = USER_IMAGE,
+ isSelected = false,
+ isSelectable = true,
+ ),
+ UserModel(
+ id = 2,
+ name = Text.Loaded("two"),
+ image = USER_IMAGE,
+ isSelected = false,
+ isSelectable = false,
+ ),
+ )
+ )
+
+ var userViewModels: List<UserViewModel>? = null
+ val job = underTest.users.onEach { userViewModels = it }.launchIn(this)
+
+ assertThat(userViewModels).hasSize(3)
+ assertUserViewModel(
+ viewModel = userViewModels?.get(0),
+ viewKey = 0,
+ name = "zero",
+ isSelectionMarkerVisible = true,
+ alpha = LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA,
+ isClickable = true,
+ )
+ assertUserViewModel(
+ viewModel = userViewModels?.get(1),
+ viewKey = 1,
+ name = "one",
+ isSelectionMarkerVisible = false,
+ alpha = LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA,
+ isClickable = true,
+ )
+ assertUserViewModel(
+ viewModel = userViewModels?.get(2),
+ viewKey = 2,
+ name = "two",
+ isSelectionMarkerVisible = false,
+ alpha = LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA,
+ isClickable = false,
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `maximumUserColumns - few users`() =
+ runBlocking(IMMEDIATE) {
+ setUsers(count = 2)
+ var value: Int? = null
+ val job = underTest.maximumUserColumns.onEach { value = it }.launchIn(this)
+
+ assertThat(value).isEqualTo(4)
+ job.cancel()
+ }
+
+ @Test
+ fun `maximumUserColumns - many users`() =
+ runBlocking(IMMEDIATE) {
+ setUsers(count = 5)
+ var value: Int? = null
+ val job = underTest.maximumUserColumns.onEach { value = it }.launchIn(this)
+
+ assertThat(value).isEqualTo(3)
+ job.cancel()
+ }
+
+ @Test
+ fun `isOpenMenuButtonVisible - has actions - true`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(UserActionModel.values().toList())
+
+ var isVisible: Boolean? = null
+ val job = underTest.isOpenMenuButtonVisible.onEach { isVisible = it }.launchIn(this)
+
+ assertThat(isVisible).isTrue()
+ job.cancel()
+ }
+
+ @Test
+ fun `isOpenMenuButtonVisible - no actions - false`() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(emptyList())
+
+ var isVisible: Boolean? = null
+ val job = underTest.isOpenMenuButtonVisible.onEach { isVisible = it }.launchIn(this)
+
+ assertThat(isVisible).isFalse()
+ job.cancel()
+ }
+
+ @Test
+ fun menu() =
+ runBlocking(IMMEDIATE) {
+ userRepository.setActions(UserActionModel.values().toList())
+ var isMenuVisible: Boolean? = null
+ val job = underTest.isMenuVisible.onEach { isMenuVisible = it }.launchIn(this)
+ assertThat(isMenuVisible).isFalse()
+
+ underTest.onOpenMenuButtonClicked()
+ assertThat(isMenuVisible).isTrue()
+
+ underTest.onMenuClosed()
+ assertThat(isMenuVisible).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `isFinishRequested - finishes when user is switched`() =
+ runBlocking(IMMEDIATE) {
+ setUsers(count = 2)
+ var isFinishRequested: Boolean? = null
+ val job = underTest.isFinishRequested.onEach { isFinishRequested = it }.launchIn(this)
+ assertThat(isFinishRequested).isFalse()
+
+ userRepository.setSelectedUser(1)
+ yield()
+ assertThat(isFinishRequested).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `isFinishRequested - finishes when the screen turns off`() =
+ runBlocking(IMMEDIATE) {
+ setUsers(count = 2)
+ powerRepository.setInteractive(true)
+ var isFinishRequested: Boolean? = null
+ val job = underTest.isFinishRequested.onEach { isFinishRequested = it }.launchIn(this)
+ assertThat(isFinishRequested).isFalse()
+
+ powerRepository.setInteractive(false)
+ yield()
+ assertThat(isFinishRequested).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `isFinishRequested - finishes when cancel button is clicked`() =
+ runBlocking(IMMEDIATE) {
+ setUsers(count = 2)
+ powerRepository.setInteractive(true)
+ var isFinishRequested: Boolean? = null
+ val job = underTest.isFinishRequested.onEach { isFinishRequested = it }.launchIn(this)
+ assertThat(isFinishRequested).isFalse()
+
+ underTest.onCancelButtonClicked()
+ yield()
+ assertThat(isFinishRequested).isTrue()
+
+ underTest.onFinished()
+ yield()
+ assertThat(isFinishRequested).isFalse()
+
+ job.cancel()
+ }
+
+ private fun setUsers(count: Int) {
+ userRepository.setUsers(
+ (0 until count).map { index ->
+ UserModel(
+ id = index,
+ name = Text.Loaded("$index"),
+ image = USER_IMAGE,
+ isSelected = index == 0,
+ isSelectable = true,
+ )
+ }
+ )
+ }
+
+ private fun assertUserViewModel(
+ viewModel: UserViewModel?,
+ viewKey: Int,
+ name: String,
+ isSelectionMarkerVisible: Boolean,
+ alpha: Float,
+ isClickable: Boolean,
+ ) {
+ checkNotNull(viewModel)
+ assertThat(viewModel.viewKey).isEqualTo(viewKey)
+ assertThat(viewModel.name).isEqualTo(Text.Loaded(name))
+ assertThat(viewModel.isSelectionMarkerVisible).isEqualTo(isSelectionMarkerVisible)
+ assertThat(viewModel.alpha).isEqualTo(alpha)
+ assertThat(viewModel.onClicked != null).isEqualTo(isClickable)
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ private val USER_IMAGE = mock<Drawable>()
+ }
+}
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 092e82c526e3..7df707789290 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
@@ -28,12 +28,14 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
import org.junit.Test
import org.junit.runner.RunWith
@@ -127,6 +129,53 @@ class SetChangesFlowTest : SysuiTestCase() {
)
)
}
+
+ @Test
+ fun dontEmitFirstEvent() = runBlocking {
+ assertThatFlow(flowOf(setOf(1, 2), setOf(2, 3)).setChanges(emitFirstEvent = false))
+ .emitsExactly(
+ SetChanges(
+ removed = setOf(1),
+ added = setOf(3),
+ )
+ )
+ }
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class SampleFlowTest : SysuiTestCase() {
+ @Test
+ fun simple() = runBlocking {
+ 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()
+ }
+
+ @Test
+ fun multipleSamples() = runBlocking {
+ val samplee = MutableSharedFlow<Int>()
+ val sampler = flow {
+ emit(1)
+ samplee.emit(1)
+ emit(2)
+ samplee.emit(2)
+ samplee.emit(3)
+ emit(3)
+ emit(4)
+ }
+ assertThatFlow(sampler.sample(samplee) { a, b -> a to b })
+ .emitsExactly(
+ 2 to 1,
+ 3 to 3,
+ 4 to 3,
+ )
+ }
}
private fun <T> assertThatFlow(flow: Flow<T>) = object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
new file mode 100644
index 000000000000..6848b836a348
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.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.util.kotlin
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class RaceSuspendTest : SysuiTestCase() {
+ @Test
+ fun raceSimple() = runBlocking {
+ val winner = CompletableDeferred<Int>()
+ val result = async {
+ race(
+ { winner.await() },
+ { awaitCancellation() },
+ )
+ }
+ winner.complete(1)
+ assertThat(result.await()).isEqualTo(1)
+ }
+
+ @Test
+ fun raceImmediate() = runBlocking {
+ assertThat(
+ race<Int>(
+ { 1 },
+ { 2 },
+ )
+ ).isEqualTo(1)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 11eb4e3de354..42b434a9deaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -12,6 +12,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF 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
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
new file mode 100644
index 000000000000..15465f4d40fe
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.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.power.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakePowerRepository(
+ initialInteractive: Boolean = true,
+) : PowerRepository {
+
+ private val _isInteractive = MutableStateFlow(initialInteractive)
+ override val isInteractive: Flow<Boolean> = _isInteractive.asStateFlow()
+
+ fun setInteractive(value: Boolean) {
+ _isInteractive.value = value
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
new file mode 100644
index 000000000000..20f1e367944f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.user.data.repository
+
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
+
+class FakeUserRepository : UserRepository {
+
+ private val _users = MutableStateFlow<List<UserModel>>(emptyList())
+ override val users: Flow<List<UserModel>> = _users.asStateFlow()
+ override val selectedUser: Flow<UserModel> =
+ users.map { models -> models.first { model -> model.isSelected } }
+
+ private val _actions = MutableStateFlow<List<UserActionModel>>(emptyList())
+ override val actions: Flow<List<UserActionModel>> = _actions.asStateFlow()
+
+ private val _isActionableWhenLocked = MutableStateFlow(false)
+ override val isActionableWhenLocked: Flow<Boolean> = _isActionableWhenLocked.asStateFlow()
+
+ private var _isGuestUserAutoCreated: Boolean = false
+ override val isGuestUserAutoCreated: Boolean
+ get() = _isGuestUserAutoCreated
+ private var _isGuestUserResetting: Boolean = false
+ override val isGuestUserResetting: Boolean
+ get() = _isGuestUserResetting
+
+ fun setUsers(models: List<UserModel>) {
+ _users.value = models
+ }
+
+ fun setSelectedUser(userId: Int) {
+ check(_users.value.find { it.id == userId } != null) {
+ "Cannot select a user with ID $userId - no user with that ID found!"
+ }
+
+ setUsers(
+ _users.value.map { model ->
+ when {
+ model.isSelected && model.id != userId -> model.copy(isSelected = false)
+ !model.isSelected && model.id == userId -> model.copy(isSelected = true)
+ else -> model
+ }
+ }
+ )
+ }
+
+ fun setActions(models: List<UserActionModel>) {
+ _actions.value = models
+ }
+
+ fun setActionableWhenLocked(value: Boolean) {
+ _isActionableWhenLocked.value = value
+ }
+
+ fun setGuestUserAutoCreated(value: Boolean) {
+ _isGuestUserAutoCreated = value
+ }
+
+ fun setGuestUserResetting(value: Boolean) {
+ _isGuestUserResetting = value
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index f539dbdf76a0..8d171be87cb6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -26,6 +26,7 @@ package com.android.systemui.util.mockito
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatcher
import org.mockito.Mockito
+import org.mockito.stubbing.OngoingStubbing
/**
* Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when
@@ -77,8 +78,18 @@ inline fun <reified T : Any> argumentCaptor(): ArgumentCaptor<T> =
* Helper function for creating new mocks, without the need to pass in a [Class] instance.
*
* Generic T is nullable because implicitly bounded by Any?.
+ *
+ * @param apply builder function to simplify stub configuration by improving type inference.
*/
-inline fun <reified T : Any> mock(): T = Mockito.mock(T::class.java)
+inline fun <reified T : Any> mock(apply: T.() -> Unit = {}): T = Mockito.mock(T::class.java)
+ .apply(apply)
+
+/**
+ * Helper function for stubbing methods without the need to use backticks.
+ *
+ * @see Mockito.when
+ */
+fun <T> whenever(methodCall: T): OngoingStubbing<T> = Mockito.`when`(methodCall)
/**
* A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when
@@ -118,3 +129,17 @@ inline fun <reified T : Any> kotlinArgumentCaptor(): KotlinArgumentCaptor<T> =
*/
inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
kotlinArgumentCaptor<T>().apply { block() }.value
+
+/**
+ * Variant of [withArgCaptor] for capturing multiple arguments.
+ *
+ * val captor = argumentCaptor<Foo>()
+ * verify(...).someMethod(captor.capture())
+ * val captured: List<Foo> = captor.allValues
+ *
+ * becomes:
+ *
+ * val capturedList = captureMany<Foo> { verify(...).someMethod(capture()) }
+ */
+inline fun <reified T : Any> captureMany(block: KotlinArgumentCaptor<T>.() -> Unit): List<T> =
+ kotlinArgumentCaptor<T>().apply{ block() }.allValues
diff --git a/services/Android.bp b/services/Android.bp
index 4f80a988806e..decfa77e002a 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -57,6 +57,7 @@ system_optimized_java_defaults {
// retracing infra.
optimize: false,
shrink: true,
+ ignore_warnings: false,
proguard_flags_files: ["proguard.flags"],
},
// Note: Optimizations are disabled by default if unspecified in
@@ -175,6 +176,8 @@ java_library {
"android.hidl.manager-V1.0-java",
"framework-tethering.stubs.module_lib",
"service-art.stubs.system_server",
+ "service-permission.stubs.system_server",
+ "service-sdksandbox.stubs.system_server",
],
// Uncomment to enable output of certain warnings (deprecated, unchecked)
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 990e96397bae..f55cfb1ab210 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -957,10 +957,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// current state of the windows as the window manager may be delaying
// the computation for performance reasons.
boolean shouldComputeWindows = false;
- int displayId = Display.INVALID_DISPLAY;
+ int displayId = event.getDisplayId();
synchronized (mLock) {
final int windowId = event.getWindowId();
- if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
+ && displayId == Display.INVALID_DISPLAY) {
displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
resolvedUserId, windowId);
event.setDisplayId(displayId);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index d33e7b2e4d87..7c68c8a5bdb1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -19,7 +19,6 @@ package com.android.server.accessibility;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -275,22 +274,23 @@ public class AccessibilityWindowManager {
* Sets the active flag of the window according to given windowId, others set to inactive.
*
* @param windowId The windowId
+ * @return {@code true} if the window is in this display, {@code false} otherwise.
*/
- void setActiveWindowLocked(int windowId) {
+ boolean setActiveWindowLocked(int windowId) {
+ boolean foundWindow = false;
if (mWindows != null) {
final int windowCount = mWindows.size();
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = mWindows.get(i);
if (window.getId() == windowId) {
window.setActive(true);
- mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
- AccessibilityEvent.obtainWindowsChangedEvent(windowId,
- AccessibilityEvent.WINDOWS_CHANGE_ACTIVE));
+ foundWindow = true;
} else {
window.setActive(false);
}
}
}
+ return foundWindow;
}
/**
@@ -298,24 +298,23 @@ public class AccessibilityWindowManager {
* unfocused.
*
* @param windowId The windowId
+ * @return {@code true} if the window is in this display, {@code false} otherwise.
*/
- void setAccessibilityFocusedWindowLocked(int windowId) {
+ boolean setAccessibilityFocusedWindowLocked(int windowId) {
+ boolean foundWindow = false;
if (mWindows != null) {
final int windowCount = mWindows.size();
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = mWindows.get(i);
if (window.getId() == windowId) {
- mAccessibilityFocusedDisplayId = mDisplayId;
window.setAccessibilityFocused(true);
- mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
- AccessibilityEvent.obtainWindowsChangedEvent(
- windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
-
+ foundWindow = true;
} else {
window.setAccessibilityFocused(false);
}
}
}
+ return foundWindow;
}
/**
@@ -704,7 +703,7 @@ public class AccessibilityWindowManager {
final AccessibilityWindowInfo window = oldWindows.get(i);
if (mA11yWindowInfoById.get(window.getId()) == null) {
events.add(AccessibilityEvent.obtainWindowsChangedEvent(
- window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED));
+ mDisplayId, window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED));
}
}
@@ -714,13 +713,13 @@ public class AccessibilityWindowManager {
final AccessibilityWindowInfo newWindow = mWindows.get(i);
final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId());
if (oldWindow == null) {
- events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(mDisplayId,
newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED));
} else {
int changes = newWindow.differenceFrom(oldWindow);
if (changes != 0) {
events.add(AccessibilityEvent.obtainWindowsChangedEvent(
- newWindow.getId(), changes));
+ mDisplayId, newWindow.getId(), changes));
}
}
}
@@ -1522,38 +1521,59 @@ public class AccessibilityWindowManager {
private void setActiveWindowLocked(int windowId) {
if (mActiveWindowId != windowId) {
- mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
- AccessibilityEvent.obtainWindowsChangedEvent(
+ List<AccessibilityEvent> events = new ArrayList<>(2);
+ if (mActiveWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ final DisplayWindowsObserver observer =
+ getDisplayWindowObserverByWindowIdLocked(mActiveWindowId);
+ if (observer != null) {
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId,
mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE));
+ }
+ }
mActiveWindowId = windowId;
// Goes through all windows for each display.
final int count = mDisplayWindowsObservers.size();
for (int i = 0; i < count; i++) {
final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
- if (observer != null) {
- observer.setActiveWindowLocked(windowId);
+ if (observer != null && observer.setActiveWindowLocked(windowId)) {
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId,
+ windowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE));
}
}
+
+ for (final AccessibilityEvent event : events) {
+ mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(event);
+ }
}
}
private void setAccessibilityFocusedWindowLocked(int windowId) {
if (mAccessibilityFocusedWindowId != windowId) {
- mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
- AccessibilityEvent.obtainWindowsChangedEvent(
- mAccessibilityFocusedWindowId,
- WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
+ List<AccessibilityEvent> events = new ArrayList<>(2);
+ if (mAccessibilityFocusedDisplayId != Display.INVALID_DISPLAY
+ && mAccessibilityFocusedWindowId
+ != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+ mAccessibilityFocusedDisplayId, mAccessibilityFocusedWindowId,
+ AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
+ }
mAccessibilityFocusedWindowId = windowId;
// Goes through all windows for each display.
final int count = mDisplayWindowsObservers.size();
for (int i = 0; i < count; i++) {
final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
- if (observer != null) {
- observer.setAccessibilityFocusedWindowLocked(windowId);
+ if (observer != null && observer.setAccessibilityFocusedWindowLocked(windowId)) {
+ mAccessibilityFocusedDisplayId = observer.mDisplayId;
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId,
+ windowId, AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
}
}
+
+ for (final AccessibilityEvent event : events) {
+ mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(event);
+ }
}
}
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index b3314ede8e50..dc9144a68e4c 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -107,7 +107,7 @@ import java.util.Set;
*/
@SuppressLint("LongLogTag")
class AssociationRequestsProcessor {
- private static final String TAG = "CompanionDevice_AssociationRequestsProcessor";
+ private static final String TAG = "CDM_AssociationRequestsProcessor";
private static final ComponentName ASSOCIATION_REQUEST_APPROVAL_ACTIVITY =
createRelative(COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, ".CompanionDeviceActivity");
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index d5991d3930a8..8c6ad3bad857 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -58,7 +58,7 @@ import java.util.StringJoiner;
@SuppressLint("LongLogTag")
class AssociationStoreImpl implements AssociationStore {
private static final boolean DEBUG = false;
- private static final String TAG = "CompanionDevice_AssociationStore";
+ private static final String TAG = "CDM_AssociationStore";
private final Object mLock = new Object();
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index 4f15691e7e3e..3814591dfce6 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -66,7 +66,7 @@ import java.util.Map;
@SuppressLint("LongLogTag")
public class CompanionApplicationController {
static final boolean DEBUG = false;
- private static final String TAG = "CompanionDevice_ApplicationController";
+ private static final String TAG = "CDM_CompanionApplicationController";
private static final long REBIND_TIMEOUT = 10 * 1000; // 10 sec
@@ -256,6 +256,9 @@ public class CompanionApplicationController {
return;
}
+ Log.i(TAG, "Calling onDeviceAppeared to userId=[" + userId + "] package=["
+ + packageName + "] associationId=[" + association.getId() + "]");
+
primaryServiceConnector.postOnDeviceAppeared(association);
}
@@ -278,6 +281,9 @@ public class CompanionApplicationController {
return;
}
+ Log.i(TAG, "Calling onDeviceDisappeared to userId=[" + userId + "] package=["
+ + packageName + "] associationId=[" + association.getId() + "]");
+
primaryServiceConnector.postOnDeviceDisappeared(association);
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 1af2ca1eff2e..85d314042496 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -124,7 +124,7 @@ import java.util.Set;
@SuppressLint("LongLogTag")
public class CompanionDeviceManagerService extends SystemService {
- static final String TAG = "CompanionDeviceManagerService";
+ static final String TAG = "CDM_CompanionDeviceManagerService";
static final boolean DEBUG = false;
/** Range of Association IDs allocated for a user.*/
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
index fa1d107735a2..93cc611c803f 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
@@ -43,7 +43,7 @@ import com.android.server.ServiceThread;
*/
@SuppressLint("LongLogTag")
class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDeviceService> {
- private static final String TAG = "CompanionDevice_ServiceConnector";
+ private static final String TAG = "CDM_CompanionServiceConnector";
private static final boolean DEBUG = false;
/** Listener for changes to the state of the {@link CompanionDeviceServiceConnector} */
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 322ad46e697a..d24f9e3df382 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -26,7 +26,7 @@ import java.io.PrintWriter;
import java.util.List;
class CompanionDeviceShellCommand extends ShellCommand {
- private static final String TAG = "CompanionDevice_ShellCommand";
+ private static final String TAG = "CDM_CompanionDeviceShellCommand";
private final CompanionDeviceManagerService mService;
private final AssociationStore mAssociationStore;
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java
index 73e68ec0bf97..c1825296ca69 100644
--- a/services/companion/java/com/android/server/companion/DataStoreUtils.java
+++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java
@@ -37,7 +37,7 @@ import java.io.FileOutputStream;
* Util class for CDM data stores
*/
public final class DataStoreUtils {
- private static final String TAG = "CompanionDevice_DataStoreUtils";
+ private static final String TAG = "CDM_DataStoreUtils";
/**
* Check if the parser pointer is at the start of the tag
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index be81b67a5c74..0bba3c9cba6b 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -63,7 +63,7 @@ import java.util.concurrent.TimeUnit;
*/
public class SystemDataTransferProcessor {
- private static final String LOG_TAG = SystemDataTransferProcessor.class.getSimpleName();
+ private static final String LOG_TAG = "CDM_SystemDataTransferProcessor";
// Values from UI to SystemDataTransferProcessor via ResultReceiver
private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED = 0;
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index ab0a06260cfc..dbff62850b00 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -75,7 +75,7 @@ import java.util.concurrent.TimeoutException;
*/
public class SystemDataTransferRequestStore {
- private static final String LOG_TAG = SystemDataTransferRequestStore.class.getSimpleName();
+ private static final String LOG_TAG = "CDM_SystemDataTransferRequestStore";
private static final String FILE_NAME = "companion_device_system_data_transfer_requests.xml";
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index ad09f7cd3dde..0aaa523164f0 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -71,7 +71,7 @@ import java.util.Set;
@SuppressLint("LongLogTag")
class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
- private static final String TAG = "CompanionDevice_PresenceMonitor_BLE";
+ private static final String TAG = "CDM_BleCompanionDeviceScanner";
/**
* When using {@link ScanSettings#SCAN_MODE_LOW_POWER}, it usually takes from 20 seconds to
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index 823743da7da6..f6b99b551ecb 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -40,7 +40,7 @@ import java.util.Map;
class BluetoothCompanionDeviceConnectionListener
extends BluetoothAdapter.BluetoothConnectionCallback
implements AssociationStore.OnChangeListener {
- private static final String TAG = "CompanionDevice_PresenceMonitor_BT";
+ private static final String TAG = "CDM_BluetoothCompanionDeviceConnectionListener";
interface Callback {
void onBluetoothCompanionDeviceConnected(int associationId);
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..fb8c5b1a9397 100644
--- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -57,7 +57,7 @@ import java.util.Set;
public class CompanionDevicePresenceMonitor implements AssociationStore.OnChangeListener,
BluetoothCompanionDeviceConnectionListener.Callback, BleCompanionDeviceScanner.Callback {
static final boolean DEBUG = false;
- private static final String TAG = "CompanionDevice_PresenceMonitor";
+ private static final String TAG = "CDM_CompanionDevicePresenceMonitor";
/** Callback for notifying about changes to status of companion devices. */
public interface Callback {
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index 77d51ea9a5ac..6db99a0d0b73 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -49,7 +49,7 @@ import java.util.concurrent.atomic.AtomicInteger;
@SuppressLint("LongLogTag")
public class CompanionTransportManager {
- private static final String TAG = "CompanionTransportManager";
+ private static final String TAG = "CDM_CompanionTransportManager";
// TODO: flip to false
private static final boolean DEBUG = true;
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 593a63c2f0c9..fc628cfdced2 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -250,7 +250,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
// The callback is fired only when windowFlags are changed. To let VirtualDevice owner
// aware that the virtual display has a secure window on top.
if ((windowFlags & FLAG_SECURE) != 0) {
- mSecureWindowCallback.onSecureWindowShown(mDisplayId, activityInfo.applicationInfo.uid);
+ // Post callback on the main thread, so it doesn't block activity launching.
+ mHandler.post(() -> mSecureWindowCallback.onSecureWindowShown(mDisplayId,
+ activityInfo.applicationInfo.uid));
}
if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) {
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index b33d84c0f275..c713a4144cec 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -314,6 +314,14 @@ public class BootReceiver extends BroadcastReceiver {
private static final DropboxRateLimiter sDropboxRateLimiter = new DropboxRateLimiter();
/**
+ * Reset the dropbox rate limiter.
+ */
+ @VisibleForTesting
+ public static void resetDropboxRateLimiter() {
+ sDropboxRateLimiter.reset();
+ }
+
+ /**
* Add a tombstone to the DropBox.
*
* @param ctx Context
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index c375c73124da..a1adc6f4aff3 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -35,7 +35,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FastPrintWriter;
import com.android.server.pm.Computer;
-import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.snapshot.PackageDataSnapshot;
import java.io.PrintWriter;
@@ -566,7 +565,7 @@ public abstract class IntentResolver<F, R extends Object> {
* "stopped", that is whether it should not be included in the result
* if the intent requests to excluded stopped objects.
*/
- protected boolean isFilterStopped(PackageStateInternal packageState, @UserIdInt int userId) {
+ protected boolean isFilterStopped(@NonNull Computer computer, F filter, @UserIdInt int userId) {
return false;
}
@@ -805,8 +804,7 @@ public abstract class IntentResolver<F, R extends Object> {
int match;
if (debug) Slog.v(TAG, "Matching against filter " + filter);
- if (excludingStopped && isFilterStopped(computer.getPackageStateInternal(packageName),
- userId)) {
+ if (excludingStopped && isFilterStopped(computer, filter, userId)) {
if (debug) {
Slog.v(TAG, " Filter's target is stopped; skipping");
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 5393b2a93125..a6e5aa4b9ec7 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -314,10 +314,8 @@ public class Watchdog implements Dumpable {
} else {
prefix = "Blocked in monitor " + mCurrentMonitor.getClass().getName();
}
- Thread thread = getThread();
- String threadIdentifier = thread.getName() + ", tid=" + thread.getId();
long latencySeconds = (SystemClock.uptimeMillis() - mStartTimeMillis) / 1000;
- return prefix + " on " + mName + " (" + threadIdentifier + ")"
+ return prefix + " on " + mName + " (" + getThread().getName() + ")"
+ " for " + latencySeconds + "s";
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ccf782a310c1..26899f196126 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -377,6 +377,7 @@ import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.AlarmManagerInternal;
+import com.android.server.BootReceiver;
import com.android.server.DeviceIdleInternal;
import com.android.server.DisplayThread;
import com.android.server.IntentResolver;
@@ -17812,6 +17813,10 @@ public class ActivityManagerService extends IActivityManager.Stub
for (BroadcastQueue queue : mBroadcastQueues) {
queue.waitForIdle(pw);
}
+ if (pw != null) {
+ pw.println("All broadcast queues are idle!");
+ pw.flush();
+ }
}
public void waitForBroadcastBarrier(@Nullable PrintWriter pw) {
@@ -17878,10 +17883,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
- * Reset the dropbox rate limiter
+ * Reset the dropbox rate limiter here and in BootReceiver
*/
void resetDropboxRateLimiter() {
mDropboxRateLimiter.reset();
+ BootReceiver.resetDropboxRateLimiter();
}
/**
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index cc2b693d36f7..a41a311bd643 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -44,6 +44,7 @@ import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.os.BatteryConsumer;
import android.os.BatteryManagerInternal;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
@@ -2292,6 +2293,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
pw.println(" --settings: dump the settings key/values related to batterystats");
pw.println(" --cpu: dump cpu stats for debugging purpose");
pw.println(" --power-profile: dump the power profile constants");
+ pw.println(" --usage: write battery usage stats. Optional arguments:");
+ pw.println(" --proto: output as a binary protobuffer");
+ pw.println(" --model power-profile: use the power profile model"
+ + " even if measured energy is available");
pw.println(" <package.name>: optional name of package to filter output by.");
pw.println(" -h: print this help text.");
pw.println("Battery stats (batterystats) commands:");
@@ -2335,6 +2340,31 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
}
+ private void dumpUsageStatsToProto(FileDescriptor fd, PrintWriter pw, int model,
+ boolean proto) {
+ awaitCompletion();
+ syncStats("dump", BatteryExternalStatsWorker.UPDATE_ALL);
+
+ BatteryUsageStatsQuery.Builder builder = new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includeProcessStateData()
+ .includePowerModels();
+ if (model == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
+ builder.powerProfileModeledOnly();
+ }
+ BatteryUsageStatsQuery query = builder.build();
+ synchronized (mStats) {
+ mStats.prepareForDumpLocked();
+ BatteryUsageStats batteryUsageStats =
+ mBatteryUsageStatsProvider.getBatteryUsageStats(query);
+ if (proto) {
+ batteryUsageStats.dumpToProto(fd);
+ } else {
+ batteryUsageStats.dump(pw, "");
+ }
+ }
+ }
+
private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {
i++;
if (i >= args.length) {
@@ -2488,6 +2518,35 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} else if ("--power-profile".equals(arg)) {
dumpPowerProfile(pw);
return;
+ } else if ("--usage".equals(arg)) {
+ int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
+ boolean proto = false;
+ for (int j = i + 1; j < args.length; j++) {
+ switch (args[j]) {
+ case "--proto":
+ proto = true;
+ break;
+ case "--model": {
+ if (j + 1 < args.length) {
+ j++;
+ if ("power-profile".equals(args[j])) {
+ model = BatteryConsumer.POWER_MODEL_POWER_PROFILE;
+ } else {
+ pw.println("Unknown power model: " + args[j]);
+ dumpHelp(pw);
+ return;
+ }
+ } else {
+ pw.println("--model without a value");
+ dumpHelp(pw);
+ return;
+ }
+ break;
+ }
+ }
+ }
+ dumpUsageStatsToProto(fd, pw, model, proto);
+ return;
} else if ("-a".equals(arg)) {
flags |= BatteryStats.DUMP_VERBOSE;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 6087f76687bf..e5975c3ed73d 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -108,7 +108,7 @@ public class DropboxRateLimiter {
}
/** Resets the rate limiter memory. */
- void reset() {
+ public void reset() {
synchronized (mErrorClusterRecords) {
mErrorClusterRecords.clear();
}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 15887f0df406..da209f095897 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -37,3 +37,7 @@ per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.c
per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
per-file ContentProviderHelper.java = varunshah@google.com, omakoto@google.com, jsharkey@google.com, yamasani@google.com
+
+# Multiuser
+per-file User* = file:/MULTIUSER_OWNERS
+
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 80dd2669a9a9..dbe80c8aee35 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2534,7 +2534,7 @@ public class OomAdjuster {
capability |= capabilityFromFGS;
}
- capability |= getDefaultCapability(psr, procState);
+ capability |= getDefaultCapability(app, procState);
// Do final modification to adj. Everything we do between here and applying
// the final setAdj must be done in this function, because we will also use
@@ -2556,7 +2556,7 @@ public class OomAdjuster {
|| state.getCurCapability() != prevCapability;
}
- private int getDefaultCapability(ProcessServiceRecord psr, int procState) {
+ private int getDefaultCapability(ProcessRecord app, int procState) {
switch (procState) {
case PROCESS_STATE_PERSISTENT:
case PROCESS_STATE_PERSISTENT_UI:
@@ -2565,15 +2565,13 @@ public class OomAdjuster {
case PROCESS_STATE_BOUND_TOP:
return PROCESS_CAPABILITY_NETWORK;
case PROCESS_STATE_FOREGROUND_SERVICE:
- if (psr.hasForegroundServices()) {
- // Capability from FGS are conditional depending on foreground service type in
- // manifest file and the mAllowWhileInUsePermissionInFgs flag.
- return PROCESS_CAPABILITY_NETWORK;
+ if (app.getActiveInstrumentation() != null) {
+ return PROCESS_CAPABILITY_ALL_IMPLICIT | PROCESS_CAPABILITY_NETWORK ;
} else {
- // process has no FGS, the PROCESS_STATE_FOREGROUND_SERVICE is from client.
- // the implicit capability could be removed in the future, client should use
- // BIND_INCLUDE_CAPABILITY flag.
- return PROCESS_CAPABILITY_ALL_IMPLICIT | PROCESS_CAPABILITY_NETWORK;
+ // Capability from foreground service is conditional depending on
+ // foregroundServiceType in the manifest file and the
+ // mAllowWhileInUsePermissionInFgs flag.
+ return PROCESS_CAPABILITY_NETWORK;
}
case PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
return PROCESS_CAPABILITY_NETWORK;
diff --git a/services/core/java/com/android/server/app/TEST_MAPPING b/services/core/java/com/android/server/app/TEST_MAPPING
new file mode 100644
index 000000000000..0ba4d8c35523
--- /dev/null
+++ b/services/core/java/com/android/server/app/TEST_MAPPING
@@ -0,0 +1,23 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsGameManagerTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.app"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ec8bf55f5576..31f86599b865 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3360,8 +3360,7 @@ public class AudioService extends IAudioService.Stub
dispatchAbsoluteVolumeChanged(streamType, info, newIndex);
}
- if ((device == AudioSystem.DEVICE_OUT_BLE_HEADSET
- || device == AudioSystem.DEVICE_OUT_BLE_BROADCAST)
+ if (AudioSystem.isLeAudioDeviceType(device)
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
@@ -4117,8 +4116,7 @@ public class AudioService extends IAudioService.Stub
dispatchAbsoluteVolumeChanged(streamType, info, index);
}
- if ((device == AudioSystem.DEVICE_OUT_BLE_HEADSET
- || device == AudioSystem.DEVICE_OUT_BLE_BROADCAST)
+ if (AudioSystem.isLeAudioDeviceType(device)
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
@@ -6957,7 +6955,8 @@ public class AudioService extends IAudioService.Stub
return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE;
}
if (isAbsoluteVolumeDevice(audioSystemDeviceOut)
- || isA2dpAbsoluteVolumeDevice(audioSystemDeviceOut)) {
+ || isA2dpAbsoluteVolumeDevice(audioSystemDeviceOut)
+ || AudioSystem.isLeAudioDeviceType(audioSystemDeviceOut)) {
return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE;
}
return AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE;
@@ -7724,7 +7723,9 @@ public class AudioService extends IAudioService.Stub
int index;
if (isFullyMuted()) {
index = 0;
- } else if (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device)) {
+ } else if (isAbsoluteVolumeDevice(device)
+ || isA2dpAbsoluteVolumeDevice(device)
+ || AudioSystem.isLeAudioDeviceType(device)) {
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if (isFullVolumeDevice(device)) {
index = (mIndexMax + 5)/10;
@@ -7746,7 +7747,8 @@ public class AudioService extends IAudioService.Stub
if (isFullyMuted()) {
index = 0;
} else if (isAbsoluteVolumeDevice(device)
- || isA2dpAbsoluteVolumeDevice(device)) {
+ || isA2dpAbsoluteVolumeDevice(device)
+ || AudioSystem.isLeAudioDeviceType(device)) {
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if (isFullVolumeDevice(device)) {
index = (mIndexMax + 5)/10;
@@ -8167,7 +8169,8 @@ public class AudioService extends IAudioService.Stub
int streamDevice = getDeviceForStream(streamType);
if ((device != streamDevice)
&& (isAbsoluteVolumeDevice(device)
- || isA2dpAbsoluteVolumeDevice(device))) {
+ || isA2dpAbsoluteVolumeDevice(device)
+ || AudioSystem.isLeAudioDeviceType(device))) {
mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
}
mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index b5835ce0c847..30a9e0a70e96 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -433,7 +433,7 @@ public class AudioServiceEvents {
case VOL_SET_LE_AUDIO_VOL:
return new StringBuilder("setLeAudioVolume:")
.append(" index:").append(mVal1)
- .append(" gain dB:").append(mVal2)
+ .append(" maxIndex:").append(mVal2)
.toString();
case VOL_SET_AVRCP_VOL:
return new StringBuilder("setAvrcpVolume:")
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 4adbfaa79c69..6e85361e9e91 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -410,9 +410,8 @@ public class BtHelper {
}
return;
}
- /* leaudio expect volume value in range 0 to 255
- */
- int volume = (index * (BT_LE_AUDIO_MAX_VOL - BT_LE_AUDIO_MIN_VOL)) / maxIndex ;
+ /* leaudio expect volume value in range 0 to 255 */
+ int volume = (int) Math.round((double) index * BT_LE_AUDIO_MAX_VOL / maxIndex);
if (AudioService.DEBUG_VOL) {
Log.i(TAG, "setLeAudioVolume: calling mLeAudio.setVolume idx="
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 4559c56b6de0..454894e2f26e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -64,6 +64,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
/**
@@ -119,11 +120,7 @@ public final class PlaybackActivityMonitor
private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
- private final ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>();
- // a public client is one that needs an anonymized version of the playback configurations, we
- // keep track of whether there is at least one to know when we need to create the list of
- // playback configurations that do not contain uid/pid/package name information.
- private boolean mHasPublicClients = false;
+ private final ConcurrentLinkedQueue<PlayMonitorClient> mClients = new ConcurrentLinkedQueue<>();
private final Object mPlayerLock = new Object();
@GuardedBy("mPlayerLock")
@@ -550,11 +547,9 @@ public final class PlaybackActivityMonitor
+ DateFormat.getTimeInstance().format(new Date()));
synchronized(mPlayerLock) {
pw.println("\n playback listeners:");
- synchronized(mClients) {
- for (PlayMonitorClient pmc : mClients) {
- pw.print(" " + (pmc.mIsPrivileged ? "(S)" : "(P)")
- + pmc.toString());
- }
+ for (PlayMonitorClient pmc : mClients) {
+ pw.print(" " + (pmc.isPrivileged() ? "(S)" : "(P)")
+ + pmc.toString());
}
pw.println("\n");
// all players
@@ -635,48 +630,33 @@ public final class PlaybackActivityMonitor
* @param iplayerReleased indicates if the change was due to a player being released
*/
private void dispatchPlaybackChange(boolean iplayerReleased) {
- synchronized (mClients) {
- // typical use case, nobody is listening, don't do any work
- if (mClients.isEmpty()) {
- return;
- }
- }
if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); }
final List<AudioPlaybackConfiguration> configsSystem;
- // list of playback configurations for "public consumption". It is only computed if there
+ // list of playback configurations for "public consumption". It is computed lazy if there
// are non-system playback activity listeners.
- final List<AudioPlaybackConfiguration> configsPublic;
+ List<AudioPlaybackConfiguration> configsPublic = null;
synchronized (mPlayerLock) {
if (mPlayers.isEmpty()) {
return;
}
- configsSystem = new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
+ configsSystem = new ArrayList<>(mPlayers.values());
}
- synchronized (mClients) {
- // was done at beginning of method, but could have changed
- if (mClients.isEmpty()) {
- return;
- }
- configsPublic = mHasPublicClients ? anonymizeForPublicConsumption(configsSystem) : null;
- final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
- while (clientIterator.hasNext()) {
- final PlayMonitorClient pmc = clientIterator.next();
- try {
- // do not spam the logs if there are problems communicating with this client
- if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) {
- if (pmc.mIsPrivileged) {
- pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem,
- iplayerReleased);
- } else {
- // non-system clients don't have the control interface IPlayer, so
- // they don't need to flush commands when a player was released
- pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic, false);
- }
+
+ final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
+ while (clientIterator.hasNext()) {
+ final PlayMonitorClient pmc = clientIterator.next();
+ // do not spam the logs if there are problems communicating with this client
+ if (!pmc.reachedMaxErrorCount()) {
+ if (pmc.isPrivileged()) {
+ pmc.dispatchPlaybackConfigChange(configsSystem,
+ iplayerReleased);
+ } else {
+ if (configsPublic == null) {
+ configsPublic = anonymizeForPublicConsumption(configsSystem);
}
- } catch (RemoteException e) {
- pmc.mErrorCount++;
- Log.e(TAG, "Error (" + pmc.mErrorCount +
- ") trying to dispatch playback config change to " + pmc, e);
+ // non-system clients don't have the control interface IPlayer, so
+ // they don't need to flush commands when a player was released
+ pmc.dispatchPlaybackConfigChange(configsPublic, false);
}
}
}
@@ -899,14 +879,9 @@ public final class PlaybackActivityMonitor
if (pcdb == null) {
return;
}
- synchronized(mClients) {
- final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
- if (pmc.init()) {
- if (!isPrivileged) {
- mHasPublicClients = true;
- }
- mClients.add(pmc);
- }
+ final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
+ if (pmc.init()) {
+ mClients.add(pmc);
}
}
@@ -914,23 +889,14 @@ public final class PlaybackActivityMonitor
if (pcdb == null) {
return;
}
- synchronized(mClients) {
- final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
- boolean hasPublicClients = false;
- // iterate over the clients to remove the dispatcher to remove, and reevaluate at
- // the same time if we still have a public client.
- while (clientIterator.hasNext()) {
- PlayMonitorClient pmc = clientIterator.next();
- if (pcdb.asBinder().equals(pmc.mDispatcherCb.asBinder())) {
- pmc.release();
- clientIterator.remove();
- } else {
- if (!pmc.mIsPrivileged) {
- hasPublicClients = true;
- }
- }
+ final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
+ // iterate over the clients to remove the dispatcher
+ while (clientIterator.hasNext()) {
+ PlayMonitorClient pmc = clientIterator.next();
+ if (pmc.equalsDispatcher(pcdb)) {
+ pmc.release();
+ clientIterator.remove();
}
- mHasPublicClients = hasPublicClients;
}
}
@@ -958,24 +924,34 @@ public final class PlaybackActivityMonitor
// can afford to be static because only one PlaybackActivityMonitor ever instantiated
static PlaybackActivityMonitor sListenerDeathMonitor;
- final IPlaybackConfigDispatcher mDispatcherCb;
- final boolean mIsPrivileged;
-
- int mErrorCount = 0;
// number of errors after which we don't update this client anymore to not spam the logs
- static final int MAX_ERRORS = 5;
+ private static final int MAX_ERRORS = 5;
+
+ private final IPlaybackConfigDispatcher mDispatcherCb;
+
+ @GuardedBy("this")
+ private final boolean mIsPrivileged;
+ @GuardedBy("this")
+ private boolean mIsReleased = false;
+ @GuardedBy("this")
+ private int mErrorCount = 0;
PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
mDispatcherCb = pcdb;
mIsPrivileged = isPrivileged;
}
+ @Override
public void binderDied() {
Log.w(TAG, "client died");
sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb);
}
- boolean init() {
+ synchronized boolean init() {
+ if (mIsReleased) {
+ // Do not init after release
+ return false;
+ }
try {
mDispatcherCb.asBinder().linkToDeath(this, 0);
return true;
@@ -985,8 +961,43 @@ public final class PlaybackActivityMonitor
}
}
- void release() {
+ synchronized void release() {
mDispatcherCb.asBinder().unlinkToDeath(this, 0);
+ mIsReleased = true;
+ }
+
+ void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
+ boolean flush) {
+ synchronized (this) {
+ if (mIsReleased) {
+ // Do not dispatch anything after release
+ return;
+ }
+ }
+ try {
+ mDispatcherCb.dispatchPlaybackConfigChange(configs, flush);
+ } catch (RemoteException e) {
+ synchronized (this) {
+ mErrorCount++;
+ Log.e(TAG, "Error (" + mErrorCount
+ + ") trying to dispatch playback config change to " + this, e);
+ }
+ }
+ }
+
+ synchronized boolean isPrivileged() {
+ return mIsPrivileged;
+ }
+
+ synchronized boolean reachedMaxErrorCount() {
+ return mErrorCount >= MAX_ERRORS;
+ }
+
+ synchronized boolean equalsDispatcher(IPlaybackConfigDispatcher pcdb) {
+ if (pcdb == null) {
+ return false;
+ }
+ return pcdb.asBinder().equals(mDispatcherCb.asBinder());
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
index 4779f6f931ee..6a2731d3982e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
@@ -216,19 +216,31 @@ public abstract class BiometricServiceRegistry<T extends BiometricServiceProvide
return null;
}
- if (mAllProps.size() > 1) {
- Slog.e(TAG, "getSingleProvider() called but multiple sensors present: "
- + mAllProps.size());
- }
+ // TODO(b/242837110): remove the try-catch once the bug is fixed.
+ try {
+ if (mAllProps.size() > 1) {
+ Slog.e(TAG, "getSingleProvider() called but multiple sensors present: "
+ + mAllProps.size());
+ }
- final int sensorId = mAllProps.get(0).sensorId;
- final T provider = getProviderForSensor(sensorId);
- if (provider != null) {
- return new Pair<>(sensorId, provider);
- }
+ final int sensorId = mAllProps.get(0).sensorId;
+ final T provider = getProviderForSensor(sensorId);
+ if (provider != null) {
+ return new Pair<>(sensorId, provider);
+ }
- Slog.e(TAG, "Single sensor: " + sensorId + ", but provider not found");
- return null;
+ Slog.e(TAG, "Single sensor: " + sensorId + ", but provider not found");
+ return null;
+ } catch (NullPointerException e) {
+ final String extra;
+ if (mAllProps == null) {
+ extra = "mAllProps: null";
+ } else {
+ extra = "mAllProps.size(): " + mAllProps.size();
+ }
+ Slog.e(TAG, "This shouldn't happen. " + extra, e);
+ throw e;
+ }
}
/**
diff --git a/services/core/java/com/android/server/input/BatteryController.java b/services/core/java/com/android/server/input/BatteryController.java
new file mode 100644
index 000000000000..c57d7e79c12a
--- /dev/null
+++ b/services/core/java/com/android/server/input/BatteryController.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input;
+
+import android.annotation.BinderThread;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.input.IInputDeviceBatteryListener;
+import android.hardware.input.InputManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.view.InputDevice;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A thread-safe component of {@link InputManagerService} responsible for managing the battery state
+ * of input devices.
+ */
+final class BatteryController {
+ private static final String TAG = BatteryController.class.getSimpleName();
+
+ // To enable these logs, run:
+ // 'adb shell setprop log.tag.BatteryController DEBUG' (requires restart)
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final NativeInputManagerService mNative;
+
+ // Maps a pid to the registered listener record for that process. There can only be one battery
+ // listener per process.
+ @GuardedBy("mLock")
+ private final ArrayMap<Integer, ListenerRecord> mListenerRecords = new ArrayMap<>();
+
+ BatteryController(Context context, NativeInputManagerService nativeService) {
+ mContext = context;
+ mNative = nativeService;
+ }
+
+ /**
+ * Register the battery listener for the given input device and start monitoring its battery
+ * state.
+ */
+ @BinderThread
+ void registerBatteryListener(int deviceId, @NonNull IInputDeviceBatteryListener listener,
+ int pid) {
+ synchronized (mLock) {
+ ListenerRecord listenerRecord = mListenerRecords.get(pid);
+
+ if (listenerRecord == null) {
+ listenerRecord = new ListenerRecord(pid, listener);
+ try {
+ listener.asBinder().linkToDeath(listenerRecord.mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Client died before battery listener could be registered.");
+ return;
+ }
+ mListenerRecords.put(pid, listenerRecord);
+ if (DEBUG) Slog.d(TAG, "Battery listener added for pid " + pid);
+ }
+
+ if (listenerRecord.mListener.asBinder() != listener.asBinder()) {
+ throw new SecurityException(
+ "Cannot register a new battery listener when there is already another "
+ + "registered listener for pid "
+ + pid);
+ }
+ if (!listenerRecord.mMonitoredDevices.add(deviceId)) {
+ throw new IllegalArgumentException(
+ "The battery listener for pid " + pid
+ + " is already monitoring deviceId " + deviceId);
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Battery listener for pid " + pid
+ + " is monitoring deviceId " + deviceId);
+ }
+
+ notifyBatteryListener(deviceId, listenerRecord);
+ }
+ }
+
+ private void notifyBatteryListener(int deviceId, ListenerRecord record) {
+ final long eventTime = SystemClock.uptimeMillis();
+ try {
+ record.mListener.onBatteryStateChanged(
+ deviceId,
+ hasBattery(deviceId),
+ mNative.getBatteryStatus(deviceId),
+ mNative.getBatteryCapacity(deviceId) / 100.f,
+ eventTime);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify listener", e);
+ }
+ }
+
+ private boolean hasBattery(int deviceId) {
+ final InputDevice device =
+ Objects.requireNonNull(mContext.getSystemService(InputManager.class))
+ .getInputDevice(deviceId);
+ return device != null && device.hasBattery();
+ }
+
+ /**
+ * Unregister the battery listener for the given input device and stop monitoring its battery
+ * state. If there are no other input devices that this listener is monitoring, the listener is
+ * removed.
+ */
+ @BinderThread
+ void unregisterBatteryListener(int deviceId, @NonNull IInputDeviceBatteryListener listener,
+ int pid) {
+ synchronized (mLock) {
+ final ListenerRecord listenerRecord = mListenerRecords.get(pid);
+ if (listenerRecord == null) {
+ throw new IllegalArgumentException(
+ "Cannot unregister battery callback: No listener registered for pid "
+ + pid);
+ }
+
+ if (listenerRecord.mListener.asBinder() != listener.asBinder()) {
+ throw new IllegalArgumentException(
+ "Cannot unregister battery callback: The listener is not the one that "
+ + "is registered for pid "
+ + pid);
+ }
+
+ if (!listenerRecord.mMonitoredDevices.contains(deviceId)) {
+ throw new IllegalArgumentException(
+ "Cannot unregister battery callback: The device is not being "
+ + "monitored for deviceId " + deviceId);
+ }
+
+ unregisterRecordLocked(listenerRecord, deviceId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void unregisterRecordLocked(ListenerRecord listenerRecord, int deviceId) {
+ final int pid = listenerRecord.mPid;
+
+ if (!listenerRecord.mMonitoredDevices.remove(deviceId)) {
+ throw new IllegalStateException("Cannot unregister battery callback: The deviceId "
+ + deviceId
+ + " is not being monitored by pid "
+ + pid);
+ }
+
+ if (listenerRecord.mMonitoredDevices.isEmpty()) {
+ // There are no more devices being monitored by this listener.
+ listenerRecord.mListener.asBinder().unlinkToDeath(listenerRecord.mDeathRecipient, 0);
+ mListenerRecords.remove(pid);
+ if (DEBUG) Slog.d(TAG, "Battery listener removed for pid " + pid);
+ }
+ }
+
+ private void handleListeningProcessDied(int pid) {
+ synchronized (mLock) {
+ final ListenerRecord listenerRecord = mListenerRecords.get(pid);
+ if (listenerRecord == null) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Removing battery listener for pid " + pid + " because the process died");
+ }
+ for (final int deviceId : listenerRecord.mMonitoredDevices) {
+ unregisterRecordLocked(listenerRecord, deviceId);
+ }
+ }
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ synchronized (mLock) {
+ pw.println(prefix + TAG + ": " + mListenerRecords.size()
+ + " battery listeners");
+ for (int i = 0; i < mListenerRecords.size(); i++) {
+ pw.println(prefix + " " + i + ": " + mListenerRecords.valueAt(i));
+ }
+ }
+ }
+
+ @SuppressWarnings("all")
+ void monitor() {
+ synchronized (mLock) {
+ return;
+ }
+ }
+
+ // A record of a registered battery listener from one process.
+ private class ListenerRecord {
+ final int mPid;
+ final IInputDeviceBatteryListener mListener;
+ final IBinder.DeathRecipient mDeathRecipient;
+ // The set of deviceIds that are currently being monitored by this listener.
+ final Set<Integer> mMonitoredDevices;
+
+ ListenerRecord(int pid, IInputDeviceBatteryListener listener) {
+ mPid = pid;
+ mListener = listener;
+ mMonitoredDevices = new ArraySet<>();
+ mDeathRecipient = () -> handleListeningProcessDied(pid);
+ }
+
+ @Override
+ public String toString() {
+ return "pid=" + mPid
+ + ", monitored devices=" + Arrays.toString(mMonitoredDevices.toArray());
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index fe9e68d285cd..3def714f0c30 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -48,6 +48,7 @@ import android.hardware.SensorPrivacyManager.Sensors;
import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayViewport;
+import android.hardware.input.IInputDeviceBatteryListener;
import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.IInputManager;
import android.hardware.input.IInputSensorEventListener;
@@ -308,6 +309,9 @@ public class InputManagerService extends IInputManager.Stub
@GuardedBy("mInputMonitors")
final Map<IBinder, GestureMonitorSpyWindow> mInputMonitors = new HashMap<>();
+ // Manages battery state for input devices.
+ private final BatteryController mBatteryController;
+
// Maximum number of milliseconds to wait for input event injection.
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
@@ -417,6 +421,7 @@ public class InputManagerService extends IInputManager.Stub
mContext = injector.getContext();
mHandler = new InputManagerHandler(injector.getLooper());
mNative = injector.getNativeService(this);
+ mBatteryController = new BatteryController(mContext, mNative);
mUseDevInputEventForAudioJack =
mContext.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
@@ -2653,6 +2658,18 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
+ public void registerBatteryListener(int deviceId, IInputDeviceBatteryListener listener) {
+ Objects.requireNonNull(listener);
+ mBatteryController.registerBatteryListener(deviceId, listener, Binder.getCallingPid());
+ }
+
+ @Override
+ public void unregisterBatteryListener(int deviceId, IInputDeviceBatteryListener listener) {
+ Objects.requireNonNull(listener);
+ mBatteryController.unregisterBatteryListener(deviceId, listener, Binder.getCallingPid());
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -2665,7 +2682,8 @@ public class InputManagerService extends IInputManager.Stub
pw.println("Input Manager Service (Java) State:");
dumpAssociations(pw, " " /*prefix*/);
dumpSpyWindowGestureMonitors(pw, " " /*prefix*/);
- dumpDisplayInputPropertiesValues(pw, " " /* prefix */);
+ dumpDisplayInputPropertiesValues(pw, " " /*prefix*/);
+ mBatteryController.dump(pw, " " /*prefix*/);
}
private void dumpAssociations(PrintWriter pw, String prefix) {
@@ -2776,6 +2794,7 @@ public class InputManagerService extends IInputManager.Stub
synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
synchronized (mInputMonitors) { /* Test if blocked by input monitor lock. */ }
synchronized (mAdditionalDisplayInputPropertiesLock) { /* Test if blocked by props lock */ }
+ mBatteryController.monitor();
mNative.monitor();
}
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 325c3089f89a..6b568b74c405 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.input;
+import android.annotation.Nullable;
import android.hardware.display.DisplayViewport;
import android.hardware.input.InputSensorInfo;
import android.hardware.lights.Light;
@@ -141,6 +142,13 @@ public interface NativeInputManagerService {
int getBatteryStatus(int deviceId);
+ /**
+ * Get the device path of the battery for an input device.
+ * @return the path for the input device battery, or null if there is none.
+ */
+ @Nullable
+ String getBatteryDevicePath(int deviceId);
+
List<Light> getLights(int deviceId);
int getLightPlayerId(int deviceId, int lightId);
@@ -327,6 +335,9 @@ public interface NativeInputManagerService {
public native int getBatteryStatus(int deviceId);
@Override
+ public native String getBatteryDevicePath(int deviceId);
+
+ @Override
public native List<Light> getLights(int deviceId);
@Override
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
index 071917ca3e4c..15b209396a88 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
@@ -19,6 +19,8 @@ package com.android.server.location.contexthub;
import android.hardware.location.NanoAppMessage;
import android.util.Log;
+import java.util.Collection;
+
/**
* A class to log events and useful metrics within the Context Hub service.
*
@@ -217,6 +219,18 @@ public class ContextHubEventLogger {
}
/**
+ * Clears all queues of events.
+ */
+ public synchronized void clear() {
+ for (Collection<?> deque:
+ new Collection<?>[] {mNanoappLoadEventQueue, mNanoappUnloadEventQueue,
+ mMessageFromNanoappQueue, mMessageToNanoappQueue,
+ mContextHubRestartEventQueue}) {
+ deque.clear();
+ }
+ }
+
+ /**
* Logs a nanoapp load event
*
* @param contextHubId the ID of the context hub
diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
index 98bae3dc247b..811e96ce6d82 100644
--- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
+++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
@@ -96,6 +96,7 @@ public class LogAccessDialogActivity extends Activity implements
// show Alert
mAlert = mAlertDialog.create();
+ mAlert.getWindow().setHideOverlayWindows(true);
mAlert.show();
// set Alert Timeout
diff --git a/services/core/java/com/android/server/logcat/OWNERS b/services/core/java/com/android/server/logcat/OWNERS
index 87d30f3dd537..4ce93aa7df83 100644
--- a/services/core/java/com/android/server/logcat/OWNERS
+++ b/services/core/java/com/android/server/logcat/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 1218649
+
cbrubaker@google.com
eunjeongshin@google.com
georgechan@google.com
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 477b8da61e0f..d8aa469bcd81 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -106,7 +106,7 @@ public class PreferencesHelper implements RankingConfig {
private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
@VisibleForTesting
- static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 50000;
+ static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
@VisibleForTesting
static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 50000;
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index bda7b823adc3..7193587d1119 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -673,6 +673,18 @@ final class DeletePackageHelper {
final String packageName = versionedPackage.getPackageName();
final long versionCode = versionedPackage.getLongVersionCode();
+ if (mPm.mProtectedPackages.isPackageDataProtected(userId, packageName)) {
+ mPm.mHandler.post(() -> {
+ try {
+ Slog.w(TAG, "Attempted to delete protected package: " + packageName);
+ observer.onPackageDeleted(packageName,
+ PackageManager.DELETE_FAILED_INTERNAL_ERROR, null);
+ } catch (RemoteException re) {
+ }
+ });
+ return;
+ }
+
try {
if (mPm.mInjector.getLocalService(ActivityTaskManagerInternal.class)
.isBaseOfLockedTask(packageName)) {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 9db9837ffc45..8024d41db90d 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -23,7 +23,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GR
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
@@ -1420,9 +1419,7 @@ final class InstallPackageHelper {
}
if (!isApex) {
- if (!doRenameLI(args, res.mReturnCode, parsedPackage)) {
- throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
- }
+ doRenameLI(args, res.mReturnCode, res.mReturnMsg, parsedPackage);
try {
setUpFsVerityIfPossible(parsedPackage);
@@ -1689,19 +1686,20 @@ final class InstallPackageHelper {
* scanned package should be updated to reflect the rename.
*/
@GuardedBy("mPm.mInstallLock")
- private boolean doRenameLI(InstallArgs args, int status, ParsedPackage parsedPackage) {
+ private void doRenameLI(InstallArgs args, int status, String statusMsg,
+ ParsedPackage parsedPackage) throws PrepareFailure {
if (args.mMoveInfo != null) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
mRemovePackageHelper.cleanUpForMoveInstall(args.mMoveInfo.mToUuid,
args.mMoveInfo.mPackageName, args.mMoveInfo.mFromCodePath);
- return false;
+ throw new PrepareFailure(status, statusMsg);
}
- return true;
+ return;
}
// For file installations
if (status != PackageManager.INSTALL_SUCCEEDED) {
mRemovePackageHelper.removeCodePath(args.mCodeFile);
- return false;
+ throw new PrepareFailure(status, statusMsg);
}
final File targetDir = resolveTargetDir(args);
@@ -1723,12 +1721,14 @@ final class InstallPackageHelper {
}
} catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to rename", e);
- return false;
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE,
+ "Failed to rename");
}
if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) {
Slog.w(TAG, "Failed to restorecon");
- return false;
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failed to restorecon");
}
// Reflect the rename internally
@@ -1739,14 +1739,13 @@ final class InstallPackageHelper {
parsedPackage.setPath(afterCodeFile.getCanonicalPath());
} catch (IOException e) {
Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);
- return false;
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failed to get path: " + afterCodeFile);
}
parsedPackage.setBaseApkPath(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, parsedPackage.getBaseApkPath()));
parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, parsedPackage.getSplitCodePaths()));
-
- return true;
}
// TODO(b/168126411): Once staged install flow starts using the same folder as non-staged
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index a1d505453483..b9a119504d1f 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -318,6 +318,10 @@ public abstract class UserManagerInternal {
*
* <p>On most devices this call will be a no-op, but it will be used on devices that support
* multiple users on multiple displays (like automotives with passenger displays).
+ *
+ * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} when a user is
+ * started and it doesn't validate if the display exists.
+ *
*/
public abstract void assignUserToDisplay(@UserIdInt int userId, int displayId);
@@ -326,6 +330,9 @@ public abstract class UserManagerInternal {
*
* <p>On most devices this call will be a no-op, but it will be used on devices that support
* multiple users on multiple displays (like automotives with passenger displays).
+ *
+ * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} when a user is
+ * stopped.
*/
public abstract void unassignUserFromDisplay(@UserIdInt int userId);
@@ -345,10 +352,22 @@ public abstract class UserManagerInternal {
* Returns the display id assigned to the user, or {@code Display.INVALID_DISPLAY} if the
* user is not assigned to any display.
*
- * <p>The current foreground user is associated with the main display, while other users would
- * only assigned to a display if they were started with
+ * <p>The current foreground user is associated with the
+ * {@link android.view.Display#DEFAULT_DISPLAY default display}, while other users would only be
+ * assigned to a display if they were started with
* {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}. If the user is a profile
* and is running, it's assigned to its parent display.
*/
public abstract int getDisplayAssignedToUser(@UserIdInt int userId);
+
+ /**
+ * Returns the main user (i.e., not a profile) that is assigned to the display, or the
+ * {@link android.app.ActivityManager#getCurrentUser() current foreground user} if no user is
+ * associated with the display.
+ *
+ * <p>The {@link android.view.Display#DEFAULT_DISPLAY default display} is always assigned to
+ * the current foreground user, while other displays would be associated with the user that was
+ * started with {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}.
+ */
+ public abstract @UserIdInt int getUserAssignedToDisplay(int displayId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4bcda2c0265e..6749cebc3652 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -630,7 +630,7 @@ public class UserManagerService extends IUserManager.Stub {
/**
* Set on on devices that support background users (key) running on secondary displays (value).
*/
- // TODO(b/239982558): move such logic to a different class (like UserDisplayAssigner)
+ // TODO(b/244644281): move such logic to a different class (like UserDisplayAssigner)
@Nullable
@GuardedBy("mUsersOnSecondaryDisplays")
private final SparseIntArray mUsersOnSecondaryDisplays;
@@ -710,7 +710,8 @@ public class UserManagerService extends IUserManager.Stub {
@VisibleForTesting
UserManagerService(Context context) {
this(context, /* pm= */ null, /* userDataPreparer= */ null,
- /* packagesLock= */ new Object(), context.getCacheDir(), /* users= */ null);
+ /* packagesLock= */ new Object(), context.getCacheDir(), /* users= */ null,
+ /* usersOnSecondaryDisplays= */ null);
}
/**
@@ -721,13 +722,13 @@ public class UserManagerService extends IUserManager.Stub {
UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer,
Object packagesLock) {
this(context, pm, userDataPreparer, packagesLock, Environment.getDataDirectory(),
- /* users= */ null);
+ /* users= */ null, /* usersOnSecondaryDisplays= */ null);
}
@VisibleForTesting
UserManagerService(Context context, PackageManagerService pm,
UserDataPreparer userDataPreparer, Object packagesLock, File dataDir,
- SparseArray<UserData> users) {
+ SparseArray<UserData> users, @Nullable SparseIntArray usersOnSecondaryDisplays) {
mContext = context;
mPm = pm;
mPackagesLock = packagesLock;
@@ -758,7 +759,13 @@ public class UserManagerService extends IUserManager.Stub {
mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null;
emulateSystemUserModeIfNeeded();
mUsersOnSecondaryDisplaysEnabled = UserManager.isUsersOnSecondaryDisplaysEnabled();
- mUsersOnSecondaryDisplays = mUsersOnSecondaryDisplaysEnabled ? new SparseIntArray() : null;
+ if (mUsersOnSecondaryDisplaysEnabled) {
+ mUsersOnSecondaryDisplays = usersOnSecondaryDisplays == null
+ ? new SparseIntArray() // default behavior
+ : usersOnSecondaryDisplays; // passed by unit test
+ } else {
+ mUsersOnSecondaryDisplays = null;
+ }
}
void systemReady() {
@@ -1380,15 +1387,20 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public void setUserEnabled(@UserIdInt int userId) {
checkManageUsersPermission("enable user");
+ UserInfo info;
+ boolean wasUserDisabled = false;
synchronized (mPackagesLock) {
- UserInfo info;
synchronized (mUsersLock) {
info = getUserInfoLU(userId);
+ if (info != null && !info.isEnabled()) {
+ wasUserDisabled = true;
+ info.flags ^= UserInfo.FLAG_DISABLED;
+ writeUserLP(getUserDataLU(info.id));
+ }
}
- if (info != null && !info.isEnabled()) {
- info.flags ^= UserInfo.FLAG_DISABLED;
- writeUserLP(getUserDataLU(info.id));
- }
+ }
+ if (wasUserDisabled && info != null && info.isProfile()) {
+ sendProfileAddedBroadcast(info.profileGroupId, info.id);
}
}
@@ -1720,6 +1732,11 @@ public class UserManagerService extends IUserManager.Stub {
return userId == getCurrentUserId();
}
+ @VisibleForTesting
+ boolean isUsersOnSecondaryDisplaysEnabled() {
+ return mUsersOnSecondaryDisplaysEnabled;
+ }
+
@Override
public boolean isUserVisible(@UserIdInt int userId) {
int callingUserId = UserHandle.getCallingUserId();
@@ -1733,7 +1750,8 @@ public class UserManagerService extends IUserManager.Stub {
return isUserVisibleUnchecked(userId);
}
- private boolean isUserVisibleUnchecked(@UserIdInt int userId) {
+ @VisibleForTesting
+ boolean isUserVisibleUnchecked(@UserIdInt int userId) {
// First check current foreground user and their profiles (on main display)
if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
return true;
@@ -1750,8 +1768,8 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- // TODO(b/239982558): add unit test
- private int getDisplayAssignedToUser(@UserIdInt int userId) {
+ @VisibleForTesting
+ int getDisplayAssignedToUser(@UserIdInt int userId) {
if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
return Display.DEFAULT_DISPLAY;
}
@@ -1765,6 +1783,43 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ @VisibleForTesting
+ int getUserAssignedToDisplay(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ return getCurrentUserId();
+ }
+
+ if (!mUsersOnSecondaryDisplaysEnabled) {
+ int currentUserId = getCurrentUserId();
+ Slogf.w(LOG_TAG, "getUsersAssignedToDisplay(%d) called with non-DEFAULT_DISPLAY on "
+ + "system that doesn't support that; returning current user (%d)", displayId,
+ currentUserId);
+ return currentUserId;
+ }
+
+ synchronized (mUsersOnSecondaryDisplays) {
+ for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
+ if (mUsersOnSecondaryDisplays.valueAt(i) != displayId) {
+ continue;
+ }
+ int userId = mUsersOnSecondaryDisplays.keyAt(i);
+ if (!isProfileUnchecked(userId)) {
+ return userId;
+ } else if (DBG_MUMD) {
+ Slogf.d(LOG_TAG, "getUserAssignedToDisplay(%d): skipping user %d because it's "
+ + "a profile", displayId, userId);
+ }
+ }
+ }
+
+ int currentUserId = getCurrentUserId();
+ if (DBG_MUMD) {
+ Slogf.d(LOG_TAG, "getUserAssignedToDisplay(%d): no user assigned to display, returning "
+ + "current user (%d) instead", displayId, currentUserId);
+ }
+ return currentUserId;
+ }
+
/**
* Gets the current user id, calling {@link ActivityManagerInternal} directly (and without
* performing any permission check).
@@ -4817,7 +4872,9 @@ public class UserManagerService extends IUserManager.Stub {
MetricsLogger.count(mContext, userInfo.isGuest() ? TRON_GUEST_CREATED
: (userInfo.isDemo() ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
- if (!userInfo.isProfile()) {
+ if (userInfo.isProfile()) {
+ sendProfileAddedBroadcast(userInfo.profileGroupId, userInfo.id);
+ } else {
// If the user switch hasn't been explicitly toggled on or off by the user, turn it on.
if (android.provider.Settings.Global.getString(mContext.getContentResolver(),
android.provider.Settings.Global.USER_SWITCHER_ENABLED) == null) {
@@ -5417,6 +5474,17 @@ public class UserManagerService extends IUserManager.Stub {
}
/**
+ * Send {@link Intent#ACTION_PROFILE_ADDED} broadcast when a user of type
+ * {@link UserInfo#isProfile()} is added. This broadcast is sent only to dynamic receivers
+ * created with {@link Context#registerReceiver}.
+ */
+ private void sendProfileAddedBroadcast(int parentUserId, int addedUserId) {
+ sendProfileBroadcast(
+ new Intent(Intent.ACTION_PROFILE_ADDED),
+ parentUserId, addedUserId);
+ }
+
+ /**
* Send {@link Intent#ACTION_PROFILE_REMOVED} broadcast when a user of type
* {@link UserInfo#isProfile()} is removed. Additionally sends
* {@link Intent#ACTION_MANAGED_PROFILE_REMOVED} broadcast if the profile is of type
@@ -5434,12 +5502,12 @@ public class UserManagerService extends IUserManager.Stub {
if (Objects.equals(userType, UserManager.USER_TYPE_PROFILE_MANAGED)) {
sendManagedProfileRemovedBroadcast(parentUserId, removedUserId);
}
- sendProfileBroadcastToRegisteredReceivers(
+ sendProfileBroadcast(
new Intent(Intent.ACTION_PROFILE_REMOVED),
parentUserId, removedUserId);
}
- private void sendProfileBroadcastToRegisteredReceivers(Intent intent,
+ private void sendProfileBroadcast(Intent intent,
int parentUserId, int userId) {
final UserHandle parentHandle = UserHandle.of(parentUserId);
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
@@ -5450,7 +5518,7 @@ public class UserManagerService extends IUserManager.Stub {
private void sendManagedProfileRemovedBroadcast(int parentUserId, int removedUserId) {
Intent managedProfileIntent = new Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(removedUserId));
+ managedProfileIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(removedUserId));
managedProfileIntent.putExtra(Intent.EXTRA_USER_HANDLE, removedUserId);
final UserHandle parentHandle = UserHandle.of(parentUserId);
getDevicePolicyManagerInternal().broadcastIntentToManifestReceivers(
@@ -6719,10 +6787,24 @@ public class UserManagerService extends IUserManager.Stub {
+ "users on multiple displays");
}
- Preconditions.checkArgument(userId != UserHandle.USER_SYSTEM, "Cannot start system user"
- + " on secondary display (%d)", displayId);
- // TODO(b/239982558): call DisplayManagerInternal to check if display is valid instead
- Preconditions.checkArgument(displayId > 0, "Invalid display id (%d)", displayId);
+ Preconditions.checkArgument(userId != UserHandle.USER_SYSTEM, "Cannot assign system "
+ + "user to secondary display (%d)", displayId);
+ Preconditions.checkArgument(displayId != Display.INVALID_DISPLAY,
+ "Cannot assign to INVALID_DISPLAY (%d)", displayId);
+
+ int currentUserId = getCurrentUserId();
+ Preconditions.checkArgument(userId != currentUserId,
+ "Cannot assign current user to other displays");
+
+ boolean isProfile = isProfileUnchecked(userId);
+
+ Preconditions.checkArgument(userId != currentUserId,
+ "Cannot assign current user to other displays");
+
+ Preconditions.checkArgument(
+ !isProfile || getProfileParentIdUnchecked(userId) != currentUserId,
+ "Cannot assign profile user %d to display %d when its parent is the current "
+ + "user (%d)", userId, displayId, currentUserId);
synchronized (mUsersOnSecondaryDisplays) {
if (DBG_MUMD) {
@@ -6730,7 +6812,7 @@ public class UserManagerService extends IUserManager.Stub {
userId, displayId);
}
- if (isProfileUnchecked(userId)) {
+ if (isProfile) {
// Profile can only start in the same display as parent
int parentUserId = getProfileParentId(userId);
int parentDisplayId = mUsersOnSecondaryDisplays.get(parentUserId);
@@ -6749,7 +6831,7 @@ public class UserManagerService extends IUserManager.Stub {
// is refactored, it should be atomic.
if (mUsersOnSecondaryDisplays.valueAt(i) == displayId) {
throw new IllegalStateException("Cannot assign " + userId + " to "
- + "display " + displayId + " as it's already assigned to "
+ + "display " + displayId + " as it's already assigned to "
+ "user " + mUsersOnSecondaryDisplays.keyAt(i));
}
// TODO(b/239982558) also check that user is not already assigned to other
@@ -6800,6 +6882,11 @@ public class UserManagerService extends IUserManager.Stub {
public int getDisplayAssignedToUser(int userId) {
return UserManagerService.this.getDisplayAssignedToUser(userId);
}
+
+ @Override
+ public int getUserAssignedToDisplay(int displayId) {
+ return UserManagerService.this.getUserAssignedToDisplay(displayId);
+ }
} // class LocalService
/**
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
index 88b4a94f7027..23872d4fe634 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
@@ -46,7 +46,7 @@ import com.android.server.pm.UserManagerService;
* Legacy permission manager service.
*/
public class LegacyPermissionManagerService extends ILegacyPermissionManager.Stub {
- private static final String TAG = "PackageManager";
+ private static final String TAG = "PermissionManager";
/** Injector that can be used to facilitate testing. */
private final Injector mInjector;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index c05d5cea0bed..4c095fb6db46 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -168,7 +168,7 @@ import java.util.concurrent.TimeoutException;
*/
public class PermissionManagerServiceImpl implements PermissionManagerServiceInterface {
- private static final String TAG = "PackageManager";
+ private static final String TAG = "PermissionManager";
private static final String LOG_TAG = PermissionManagerServiceImpl.class.getSimpleName();
private static final String SKIP_KILL_APP_REASON_NOTIFICATION_TEST = "skip permission revoke "
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index 7baec6217e2e..1d95e8744c47 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -930,12 +930,14 @@ public class ComponentResolver extends ComponentResolverLocked implements
}
@Override
- protected boolean isFilterStopped(@Nullable PackageStateInternal packageState,
+ protected boolean isFilterStopped(@NonNull Computer computer, F filter,
@UserIdInt int userId) {
if (!mUserManager.exists(userId)) {
return true;
}
+ final PackageStateInternal packageState = computer.getPackageStateInternal(
+ filter.first.getPackageName());
if (packageState == null || packageState.getPkg() == null) {
return false;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1bb476f6e4a1..e332ac765633 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2930,6 +2930,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
UserHandle.CURRENT_OR_SELF);
}
return key_consumed;
+ case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
+ case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
+ case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
+ // TODO: Add logic to handle keyboard backlight controls (go/pk_backlight_control)
+ return key_consumed;
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_MUTE:
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index e00e02999b0c..e21feae65c97 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1211,7 +1211,7 @@ public class TrustManagerService extends SystemService {
if (info.userId == userId
&& info.agent.isTrusted()
&& info.agent.shouldDisplayTrustGrantedMessage()
- && !TextUtils.isEmpty(info.agent.getMessage())) {
+ && info.agent.getMessage() != null) {
trustGrantedMessages.add(info.agent.getMessage().toString());
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index f25929c36060..d0b058b4c8f2 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2713,6 +2713,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
checkPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT);
synchronized (mLock) {
WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+ if (data == null) {
+ data = mWallpaperMap.get(UserHandle.USER_SYSTEM);
+ if (data == null) {
+ Slog.e(TAG, "getWallpaperDimAmount: wallpaperData is null");
+ return 0.0f;
+ }
+ }
return data.mWallpaperDimAmount;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index be801182980d..16b5ee54a5ba 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2120,7 +2120,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord);
- updateEnterpriseThumbnailDrawable(mAtmService.mUiContext);
+ updateEnterpriseThumbnailDrawable(mAtmService.getUiContext());
}
/**
@@ -7424,7 +7424,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final Rect frame = win.getRelativeFrame();
final Drawable thumbnailDrawable = task.mUserId == mWmService.mCurrentUserId
- ? mAtmService.mUiContext.getDrawable(R.drawable.ic_account_circle)
+ ? mAtmService.getUiContext().getDrawable(R.drawable.ic_account_circle)
: mEnterpriseThumbnailDrawable;
final HardwareBuffer thumbnail = getDisplayContent().mAppTransition
.createCrossProfileAppsThumbnail(thumbnailDrawable, frame);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 037cd8edfa39..d131457cb9a2 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -387,6 +387,14 @@ public class ActivityStartController {
callingUid, realCallingUid, UserHandle.USER_NULL);
final SparseArray<String> startingUidPkgs = new SparseArray<>();
final long origId = Binder.clearCallingIdentity();
+
+ SafeActivityOptions bottomOptions = null;
+ if (options != null) {
+ // To ensure the first N-1 activities (N == total # of activities) are also launched
+ // into the correct display, use a copy of the passed-in options (keeping only
+ // display-related info) for these activities.
+ bottomOptions = options.selectiveCloneDisplayOptions();
+ }
try {
intents = ArrayUtils.filterNotNull(intents, Intent[]::new);
final ActivityStarter[] starters = new ActivityStarter[intents.length];
@@ -435,7 +443,7 @@ public class ActivityStartController {
final boolean top = i == intents.length - 1;
final SafeActivityOptions checkedOptions = top
? options
- : null;
+ : bottomOptions;
starters[i] = obtainStarter(intent, reason)
.setIntentGrants(intentGrants)
.setCaller(caller)
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bdbe787388be..8f5d838488e2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -345,7 +345,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
* This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
* change at runtime. Use mContext for non-UI purposes.
*/
- final Context mUiContext;
+ private final Context mUiContext;
final ActivityThread mSystemThread;
H mH;
UiHandler mUiHandler;
@@ -1040,6 +1040,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ Context getUiContext() {
+ return mUiContext;
+ }
+
UserManagerService getUserManager() {
if (mUserManager == null) {
IBinder b = ServiceManager.getService(Context.USER_SERVICE);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c940a658015d..d2c098b73e71 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -39,6 +39,7 @@ import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -746,7 +747,8 @@ public class AppTransition implements Dump {
if (isKeyguardGoingAwayTransitOld(transit) && enter) {
a = mTransitionAnimation.loadKeyguardExitAnimation(mNextAppTransitionFlags,
transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
- } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
+ } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
+ || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM) {
a = null;
} else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE && !enter) {
a = mTransitionAnimation.loadKeyguardUnoccludeAnimation();
@@ -1158,6 +1160,9 @@ public class AppTransition implements Dump {
case TRANSIT_OLD_KEYGUARD_OCCLUDE: {
return "TRANSIT_OLD_KEYGUARD_OCCLUDE";
}
+ case TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM: {
+ return "TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM";
+ }
case TRANSIT_OLD_KEYGUARD_UNOCCLUDE: {
return "TRANSIT_OLD_KEYGUARD_UNOCCLUDE";
}
@@ -1413,6 +1418,7 @@ public class AppTransition implements Dump {
static boolean isKeyguardOccludeTransitOld(@TransitionOldType int transit) {
return transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
+ || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM
|| transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 4b0005d77e40..7e62c61572d6 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -34,6 +34,7 @@ import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -357,8 +358,14 @@ public class AppTransitionController {
// When there is a closing app, the keyguard has already been occluded by an
// activity, and another activity has started on top of that activity, so normal
// app transition animation should be used.
- return closingApps.isEmpty() ? TRANSIT_OLD_KEYGUARD_OCCLUDE
- : TRANSIT_OLD_ACTIVITY_OPEN;
+ if (!closingApps.isEmpty()) {
+ return TRANSIT_OLD_ACTIVITY_OPEN;
+ }
+ if (!openingApps.isEmpty() && openingApps.valueAt(0).getActivityType()
+ == ACTIVITY_TYPE_DREAM) {
+ return TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
+ }
+ return TRANSIT_OLD_KEYGUARD_OCCLUDE;
case TRANSIT_KEYGUARD_UNOCCLUDE:
return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b10e4203f183..7471993545a7 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1618,24 +1618,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
config = new Configuration();
computeScreenConfiguration(config);
- } else if (!(mTransitionController.isCollecting(this)
- // If waiting for a remote display change, don't prematurely update configuration.
- || mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange())) {
- // No obvious action we need to take, but if our current state mismatches the
- // activity manager's, update it, disregarding font scale, which should remain set
- // to the value of the previous configuration.
- // Here we're calling Configuration#unset() instead of setToDefaults() because we
- // need to keep override configs clear of non-empty values (e.g. fontSize).
- final Configuration currentConfig = getRequestedOverrideConfiguration();
- mTmpConfiguration.unset();
- mTmpConfiguration.updateFrom(currentConfig);
- computeScreenConfiguration(mTmpConfiguration);
- if (currentConfig.diff(mTmpConfiguration) != 0) {
- mWaitingForConfig = true;
- setLayoutNeeded();
- mDisplayRotation.prepareNormalRotationAnimation();
- config = new Configuration(mTmpConfiguration);
- }
}
return config;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index cb5996035c4a..e7806060c14f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -421,7 +421,7 @@ public class DisplayPolicy {
mService = service;
mContext = displayContent.isDefaultDisplay ? service.mContext
: service.mContext.createDisplayContext(displayContent.getDisplay());
- mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.mUiContext
+ mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.getUiContext()
: service.mAtmService.mSystemThread
.getSystemUiContext(displayContent.getDisplayId());
mDisplayContent = displayContent;
@@ -2183,9 +2183,7 @@ public class DisplayPolicy {
}
mDecorInsets.invalidate();
mDecorInsets.mInfoForRotation[rotation].set(newInfo);
- // If the device is booting, let the boot procedure trigger the new configuration.
- // Otherwise the display configuration needs to be recomputed now.
- return mService.mDisplayEnabled;
+ return true;
}
DecorInsets.Info getDecorInsetsInfo(int rotation, int w, int h) {
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 3108d4e0fcc0..258351436188 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -101,7 +101,8 @@ class DragDropController {
float thumbCenterX, float thumbCenterY, ClipData data) {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" +
- Integer.toHexString(flags) + " data=" + data);
+ Integer.toHexString(flags) + " data=" + data + " touch(" + touchX + ","
+ + touchY + ") thumb center(" + thumbCenterX + "," + thumbCenterY + ")");
}
final IBinder dragToken = new Binder();
@@ -156,6 +157,7 @@ class DragDropController {
mDragState.mPid = callerPid;
mDragState.mUid = callerUid;
mDragState.mOriginalAlpha = alpha;
+ mDragState.mAnimatedScale = callingWin.mGlobalScale;
mDragState.mToken = dragToken;
mDragState.mDisplayContent = displayContent;
mDragState.mData = data;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 2242bfde7ced..25ff023a664b 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -112,6 +112,7 @@ class DragState {
int mTouchSource;
boolean mDragResult;
boolean mRelinquishDragSurfaceToDropTarget;
+ float mAnimatedScale = 1.0f;
float mOriginalAlpha;
float mOriginalX, mOriginalY;
float mCurrentX, mCurrentY;
@@ -650,7 +651,8 @@ class DragState {
PropertyValuesHolder.ofFloat(
ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY,
mOriginalY - mThumbOffsetY),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, 1, 1),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale,
+ mAnimatedScale),
PropertyValuesHolder.ofFloat(
ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, mOriginalAlpha / 2));
@@ -678,7 +680,7 @@ class DragState {
ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX, mCurrentX),
PropertyValuesHolder.ofFloat(
ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY, mCurrentY),
- PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, 1, 0),
+ PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, mAnimatedScale, 0),
PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0));
final AnimationListener listener = new AnimationListener();
animator.setDuration(MIN_ANIMATION_DURATION_MS);
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index d92a1f495917..a638784390b6 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -118,6 +118,34 @@ public class SafeActivityOptions {
}
/**
+ * To ensure that two activities, one using this object, and the other using the
+ * SafeActivityOptions returned from this function, are launched into the same display through
+ * ActivityStartController#startActivities, all display-related information, i.e.
+ * displayAreaToken, launchDisplayId and callerDisplayId, are cloned.
+ */
+ @Nullable SafeActivityOptions selectiveCloneDisplayOptions() {
+ final ActivityOptions options = cloneLaunchingDisplayOptions(mOriginalOptions);
+ final ActivityOptions callerOptions = cloneLaunchingDisplayOptions(mCallerOptions);
+ if (options == null && callerOptions == null) {
+ return null;
+ }
+
+ final SafeActivityOptions safeOptions = new SafeActivityOptions(options,
+ mOriginalCallingPid, mOriginalCallingUid);
+ safeOptions.mCallerOptions = callerOptions;
+ safeOptions.mRealCallingPid = mRealCallingPid;
+ safeOptions.mRealCallingUid = mRealCallingUid;
+ return safeOptions;
+ }
+
+ private ActivityOptions cloneLaunchingDisplayOptions(ActivityOptions options) {
+ return options == null ? null : ActivityOptions.makeBasic()
+ .setLaunchTaskDisplayArea(options.getLaunchTaskDisplayArea())
+ .setLaunchDisplayId(options.getLaunchDisplayId())
+ .setCallerDisplayId((options.getCallerDisplayId()));
+ }
+
+ /**
* Overrides options with options from a caller and records {@link Binder#getCallingPid}/
* {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
* method.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a9d5c69f1e02..728102ecc0d6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4459,6 +4459,14 @@ class Task extends TaskFragment {
// transferring the transform on the leash to the task, reset this state once we're
// moving out of pip
setCanAffectSystemUiFlags(true);
+ // Turn on userLeaveHint so other app can enter PiP mode.
+ mTaskSupervisor.mUserLeaving = true;
+ // Allow entering PiP from current top most activity when we are leaving PiP.
+ final Task topFocused = mRootWindowContainer.getTopDisplayFocusedRootTask();
+ if (topFocused != null) {
+ final ActivityRecord ar = topFocused.getTopResumedActivity();
+ enableEnterPipOnTaskSwitch(ar, null /* toFrontTask */, ar, null /* opts */);
+ }
mRootWindowContainer.notifyActivityPipModeChanged(this, null);
}
if (likelyResolvedMode == WINDOWING_MODE_PINNED) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 16fe4da68100..75d6647982bd 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -569,17 +569,16 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
t.setLayer(targetLeash, target.getLastLayer());
target.getRelativePosition(tmpPos);
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
- final Rect clipRect;
// No need to clip the display in case seeing the clipped content when during the
// display rotation. No need to clip activities because they rely on clipping on
// task layers.
if (target.asDisplayContent() != null || target.asActivityRecord() != null) {
- clipRect = null;
+ t.setCrop(targetLeash, null /* crop */);
} else {
- clipRect = target.getRequestedOverrideBounds();
- clipRect.offset(-tmpPos.x, -tmpPos.y);
+ // Crop to the requested bounds.
+ final Rect clipRect = target.getRequestedOverrideBounds();
+ t.setWindowCrop(targetLeash, clipRect.width(), clipRect.height());
}
- t.setCrop(targetLeash, clipRect);
t.setCornerRadius(targetLeash, 0);
t.setShadowRadius(targetLeash, 0);
t.setMatrix(targetLeash, 1, 0, 0, 1);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 891bc0f06f2c..78b4ce222dd3 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -2090,6 +2090,14 @@ static jint nativeGetBatteryStatus(JNIEnv* env, jobject nativeImplObj, jint devi
return static_cast<jint>(ret.value_or(BATTERY_STATUS_UNKNOWN));
}
+static jstring nativeGetBatteryDevicePath(JNIEnv* env, jobject nativeImplObj, jint deviceId) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+
+ const std::optional<std::string> batteryPath =
+ im->getInputManager()->getReader().getBatteryDevicePath(deviceId);
+ return batteryPath ? env->NewStringUTF(batteryPath->c_str()) : nullptr;
+}
+
static void nativeReloadKeyboardLayouts(JNIEnv* env, jobject nativeImplObj) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -2371,6 +2379,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"setLightColor", "(III)V", (void*)nativeSetLightColor},
{"getBatteryCapacity", "(I)I", (void*)nativeGetBatteryCapacity},
{"getBatteryStatus", "(I)I", (void*)nativeGetBatteryStatus},
+ {"getBatteryDevicePath", "(I)Ljava/lang/String;", (void*)nativeGetBatteryDevicePath},
{"reloadKeyboardLayouts", "()V", (void*)nativeReloadKeyboardLayouts},
{"reloadDeviceAliases", "()V", (void*)nativeReloadDeviceAliases},
{"dump", "()Ljava/lang/String;", (void*)nativeDump},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 333895ea5571..89a280930c60 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8336,9 +8336,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ PackageManager.FEATURE_DEVICE_ADMIN + " feature.");
}
- // TODO(b/240562946): Remove owner name from API parameters.
@Override
- public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId,
+ public boolean setDeviceOwner(ComponentName admin, int userId,
boolean setProfileOwnerOnCurrentUserIfNecessary) {
if (!mHasFeature) {
logMissingFeatureAction("Cannot set " + ComponentName.flattenToShortString(admin)
@@ -8794,9 +8793,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
}
- // TODO(b/240562946): Remove owner name from API parameters.
@Override
- public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) {
+ public boolean setProfileOwner(ComponentName who, int userHandle) {
if (!mHasFeature) {
logMissingFeatureAction("Cannot set " + ComponentName.flattenToShortString(who)
+ " as profile owner for user " + userHandle);
@@ -10222,6 +10220,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ApplicationInfo applicationInfo = mIPackageManager.getApplicationInfo(
enabledPackage, PackageManager.MATCH_UNINSTALLED_PACKAGES,
userIdToCheck);
+
+ if (applicationInfo == null) {
+ return false;
+ }
+
systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
} catch (RemoteException e) {
Slogf.i(LOG_TAG, "Can't talk to package managed", e);
@@ -10817,9 +10820,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Set admin.
setActiveAdmin(profileOwner, /* refreshing= */ true, userId);
- final String ownerName = getProfileOwnerNameUnchecked(
- Process.myUserHandle().getIdentifier());
- setProfileOwner(profileOwner, ownerName, userId);
+ setProfileOwner(profileOwner, userId);
synchronized (getLockObject()) {
DevicePolicyData policyData = getUserData(userId);
@@ -15412,24 +15413,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- final String[] feature_allow =
- { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED };
- final String[] feature_disallow =
- { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED };
-
- boolean compatible = true;
- for (Account account : accounts) {
- if (hasAccountFeatures(am, account, feature_disallow)) {
- Slogf.e(LOG_TAG, "%s has %s", account, feature_disallow[0]);
- compatible = false;
- break;
- }
- if (!hasAccountFeatures(am, account, feature_allow)) {
- Slogf.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]);
- compatible = false;
- break;
- }
- }
+ boolean compatible = !hasIncompatibleAccounts(am, accounts);
if (compatible) {
Slogf.w(LOG_TAG, "All accounts are compatible");
} else {
@@ -15439,6 +15423,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
}
+ private boolean hasIncompatibleAccounts(AccountManager am, Account[] accounts) {
+ // TODO(b/244284408): Add test
+ final String[] feature_allow =
+ { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED };
+ final String[] feature_disallow =
+ { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED };
+
+ for (Account account : accounts) {
+ if (hasAccountFeatures(am, account, feature_disallow)) {
+ Slogf.e(LOG_TAG, "%s has %s", account, feature_disallow[0]);
+ return true;
+ }
+ if (!hasAccountFeatures(am, account, feature_allow)) {
+ Slogf.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private boolean hasAccountFeatures(AccountManager am, Account account, String[] features) {
try {
return am.hasFeatures(account, features, null, null).getResult();
@@ -17658,8 +17663,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
maybeInstallDevicePolicyManagementRoleHolderInUser(userInfo.id);
installExistingAdminPackage(userInfo.id, admin.getPackageName());
- if (!enableAdminAndSetProfileOwner(
- userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) {
+ if (!enableAdminAndSetProfileOwner(userInfo.id, caller.getUserId(), admin)) {
throw new ServiceSpecificException(
ERROR_SETTING_PROFILE_OWNER_FAILED,
"Error setting profile owner.");
@@ -17848,10 +17852,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private boolean enableAdminAndSetProfileOwner(
- @UserIdInt int userId, @UserIdInt int callingUserId, ComponentName adminComponent,
- String ownerName) {
+ @UserIdInt int userId, @UserIdInt int callingUserId, ComponentName adminComponent) {
enableAndSetActiveAdmin(userId, callingUserId, adminComponent);
- return setProfileOwner(adminComponent, ownerName, userId);
+ return setProfileOwner(adminComponent, userId);
}
private void enableAndSetActiveAdmin(
@@ -18048,11 +18051,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
- if (!setActiveAdminAndDeviceOwner(
- deviceOwnerUserId, deviceAdmin, provisioningParams.getOwnerName())) {
+ if (!setActiveAdminAndDeviceOwner(deviceOwnerUserId, deviceAdmin)) {
throw new ServiceSpecificException(
- ERROR_SET_DEVICE_OWNER_FAILED,
- "Failed to set device owner.");
+ ERROR_SET_DEVICE_OWNER_FAILED, "Failed to set device owner.");
}
disallowAddUser();
@@ -18179,12 +18180,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private boolean setActiveAdminAndDeviceOwner(
- @UserIdInt int userId, ComponentName adminComponent, String name) {
+ @UserIdInt int userId, ComponentName adminComponent) {
enableAndSetActiveAdmin(userId, userId, adminComponent);
// TODO(b/178187130): Directly set DO and remove the check once silent provisioning is no
// longer used.
if (getDeviceOwnerComponent(/* callingUserOnly= */ true) == null) {
- return setDeviceOwner(adminComponent, name, userId,
+ return setDeviceOwner(adminComponent, userId,
/* setProfileOwnerOnCurrentUserIfNecessary= */ true);
}
return true;
@@ -18706,7 +18707,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
AccountManager am = AccountManager.get(mContext);
Account[] accounts = am.getAccounts();
- return accounts.length == 0;
+ if (accounts.length == 0) {
+ return true;
+ }
+ return !hasIncompatibleAccounts(am, accounts);
}
private void setBypassDevicePolicyManagementRoleQualificationStateInternal(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 1fa2f53bea17..43432258045c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -50,6 +50,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
private final DevicePolicyManagerService mService;
private int mUserId = UserHandle.USER_SYSTEM;
+ //TODO(b/240562946): remove mName once it is not used by setDeviceOwner
private String mName = "";
private ComponentName mComponent;
private boolean mSetDoOnly;
@@ -256,7 +257,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
try {
- if (!mService.setDeviceOwner(mComponent, mName, mUserId,
+ if (!mService.setDeviceOwner(mComponent, mUserId,
/* setProfileOwnerOnCurrentUserIfNecessary= */ !mSetDoOnly)) {
throw new RuntimeException(
"Can't set package " + mComponent + " as device owner.");
@@ -287,7 +288,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
try {
- if (!mService.setProfileOwner(mComponent, mName, mUserId)) {
+ if (!mService.setProfileOwner(mComponent, mUserId)) {
throw new RuntimeException("Can't set component "
+ mComponent.flattenToShortString() + " as profile owner for user "
+ mUserId);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index dbed091f7da2..f921ccf62a3b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -929,6 +929,9 @@ public final class SystemServer implements Dumpable {
startCoreServices(t);
startOtherServices(t);
startApexServices(t);
+ // Only update the timeout after starting all the services so that we use
+ // the default timeout to start system server.
+ updateWatchdogTimeout(t);
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
@@ -1499,10 +1502,6 @@ public final class SystemServer implements Dumpable {
SQLiteCompatibilityWalFlags.reset();
t.traceEnd();
- t.traceBegin("UpdateWatchdogTimeout");
- Watchdog.getInstance().registerSettingsObserver(context);
- t.traceEnd();
-
// Records errors and logs, for example wtf()
// Currently this service indirectly depends on SettingsProvider so do this after
// InstallSystemProviders.
@@ -3055,6 +3054,12 @@ public final class SystemServer implements Dumpable {
t.traceEnd(); // startApexServices
}
+ private void updateWatchdogTimeout(@NonNull TimingsTraceAndSlog t) {
+ t.traceBegin("UpdateWatchdogTimeout");
+ Watchdog.getInstance().registerSettingsObserver(mSystemContext);
+ t.traceEnd();
+ }
+
private boolean deviceHasConfigString(@NonNull Context context, @StringRes int resId) {
String serviceName = context.getString(resId);
return !TextUtils.isEmpty(serviceName);
diff --git a/services/proguard.flags b/services/proguard.flags
index 606f360f2cc5..27fe5056b8b4 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -127,3 +127,7 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.usage.StorageStatsManagerLocal { *; }
-keep,allowoptimization,allowaccessmodification class com.android.internal.util.** { *; }
-keep,allowoptimization,allowaccessmodification class android.os.** { *; }
+
+# CoverageService guards optional jacoco class references with a runtime guard, so we can safely
+# suppress build-time warnings.
+-dontwarn org.jacoco.agent.rt.*
diff --git a/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java b/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java
index d68b517ca8cd..353341ed2985 100644
--- a/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java
+++ b/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java
@@ -42,6 +42,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.test.espresso.NoActivityResumedException;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.infra.AndroidFuture;
@@ -80,6 +81,7 @@ public class GameSessionTrampolineActivityTest {
}
@Test
+ @FlakyTest(bugId = 245615459)
public void launch_targetActivityFinishesSuccessfully_futureCompletedWithSameResults() {
AndroidFuture<GameSessionActivityResult> resultFuture =
startTestActivityViaGameSessionTrampolineActivity();
@@ -96,6 +98,7 @@ public class GameSessionTrampolineActivityTest {
}
@Test
+ @FlakyTest(bugId = 245616660)
public void launch_trampolineActivityProcessDeath_futureCompletedWithSameResults() {
setAlwaysFinishActivities(true);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java
new file mode 100644
index 000000000000..86a5c90596ff
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+
+import android.util.Log;
+
+import org.junit.Test;
+
+/**
+ * Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerInternalTest}
+ */
+public final class UserManagerInternalTest extends UserManagerServiceOrInternalTestCase {
+
+ private static final String TAG = UserManagerInternalTest.class.getSimpleName();
+
+ // NOTE: most the tests below only apply to MUMD configurations, so we're not adding _mumd_
+ // in the test names, but _nonMumd_ instead
+
+ @Test
+ public void testAssignUserToDisplay_nonMumd_defaultDisplayIgnored() {
+ mUmi.assignUserToDisplay(USER_ID, DEFAULT_DISPLAY);
+
+ assertNoUserAssignedToDisplay();
+ }
+
+ @Test
+ public void testAssignUserToDisplay_nonMumd_otherDisplay_currentUser() {
+ mockCurrentUser(USER_ID);
+
+ assertThrows(UnsupportedOperationException.class,
+ () -> mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID));
+ }
+
+ @Test
+ public void testAssignUserToDisplay_nonMumd_otherDisplay_startProfileOfcurrentUser() {
+ mockCurrentUser(PARENT_USER_ID);
+ addDefaultProfileAndParent();
+ startDefaultProfile();
+
+ assertThrows(UnsupportedOperationException.class,
+ () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+ }
+
+ @Test
+ public void testAssignUserToDisplay_nonMumd_otherDisplay_stoppedProfileOfcurrentUser() {
+ mockCurrentUser(PARENT_USER_ID);
+ addDefaultProfileAndParent();
+ stopDefaultProfile();
+
+ assertThrows(UnsupportedOperationException.class,
+ () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+ }
+
+ @Test
+ public void testAssignUserToDisplay_defaultDisplayIgnored() {
+ enableUsersOnSecondaryDisplays();
+
+ mUmi.assignUserToDisplay(USER_ID, DEFAULT_DISPLAY);
+
+ assertNoUserAssignedToDisplay();
+ }
+
+ @Test
+ public void testAssignUserToDisplay_systemUser() {
+ enableUsersOnSecondaryDisplays();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mUmi.assignUserToDisplay(USER_SYSTEM, SECONDARY_DISPLAY_ID));
+ }
+
+ @Test
+ public void testAssignUserToDisplay_invalidDisplay() {
+ enableUsersOnSecondaryDisplays();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mUmi.assignUserToDisplay(USER_ID, INVALID_DISPLAY));
+ }
+
+ @Test
+ public void testAssignUserToDisplay_currentUser() {
+ enableUsersOnSecondaryDisplays();
+ mockCurrentUser(USER_ID);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID));
+
+ assertNoUserAssignedToDisplay();
+ }
+
+ @Test
+ public void testAssignUserToDisplay_startedProfileOfCurrentUser() {
+ enableUsersOnSecondaryDisplays();
+ mockCurrentUser(PARENT_USER_ID);
+ addDefaultProfileAndParent();
+ startDefaultProfile();
+
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+
+ Log.v(TAG, "Exception: " + e);
+ assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
+ .matches("Cannot.*" + PROFILE_USER_ID + ".*" + SECONDARY_DISPLAY_ID
+ + ".*parent.*current.*" + PARENT_USER_ID + ".*");
+ assertNoUserAssignedToDisplay();
+ }
+
+ @Test
+ public void testAssignUserToDisplay_stoppedProfileOfCurrentUser() {
+ enableUsersOnSecondaryDisplays();
+ mockCurrentUser(PARENT_USER_ID);
+ addDefaultProfileAndParent();
+ stopDefaultProfile();
+
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+
+ Log.v(TAG, "Exception: " + e);
+ assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
+ .matches("Cannot.*" + PROFILE_USER_ID + ".*" + SECONDARY_DISPLAY_ID
+ + ".*parent.*current.*" + PARENT_USER_ID + ".*");
+
+ assertNoUserAssignedToDisplay();
+ }
+
+ @Test
+ public void testAssignUserToDisplay_displayAvailable() {
+ enableUsersOnSecondaryDisplays();
+
+ mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+ assertUserAssignedToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+ }
+
+ @Test
+ public void testAssignUserToDisplay_displayAlreadyAssigned() {
+ enableUsersOnSecondaryDisplays();
+
+ mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+ IllegalStateException e = assertThrows(IllegalStateException.class,
+ () -> mUmi.assignUserToDisplay(OTHER_USER_ID, SECONDARY_DISPLAY_ID));
+
+ Log.v(TAG, "Exception: " + e);
+ assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
+ .matches("Cannot.*" + OTHER_USER_ID + ".*" + SECONDARY_DISPLAY_ID + ".*already.*"
+ + USER_ID + ".*");
+ }
+
+ @Test
+ public void testAssignUserToDisplay_profileOnSameDisplayAsParent() {
+ enableUsersOnSecondaryDisplays();
+ addDefaultProfileAndParent();
+
+ mUmi.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+ mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
+
+ assertUsersAssignedToDisplays(PARENT_USER_ID, SECONDARY_DISPLAY_ID,
+ pair(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+ }
+
+ @Test
+ public void testAssignUserToDisplay_profileOnDifferentDisplayAsParent() {
+ enableUsersOnSecondaryDisplays();
+ addDefaultProfileAndParent();
+
+ mUmi.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+ IllegalStateException e = assertThrows(IllegalStateException.class,
+ () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID));
+
+ Log.v(TAG, "Exception: " + e);
+ assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
+ .matches("Cannot.*" + PROFILE_USER_ID + ".*" + OTHER_SECONDARY_DISPLAY_ID
+ + ".*parent.*" + PARENT_USER_ID + ".*" + SECONDARY_DISPLAY_ID + ".*");
+ assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+ }
+
+ @Test
+ public void testUnassignUserFromDisplay_nonMumd_ignored() {
+ mockCurrentUser(USER_ID);
+
+ mUmi.unassignUserFromDisplay(USER_SYSTEM);
+ mUmi.unassignUserFromDisplay(USER_ID);
+ mUmi.unassignUserFromDisplay(OTHER_USER_ID);
+
+ assertNoUserAssignedToDisplay();
+ }
+
+ @Test
+ public void testUnassignUserFromDisplay() {
+ testAssignUserToDisplay_displayAvailable();
+
+ mUmi.unassignUserFromDisplay(USER_ID);
+
+ assertNoUserAssignedToDisplay();
+ }
+
+ @Override
+ protected boolean isUserVisible(int userId) {
+ return mUmi.isUserVisible(userId);
+ }
+
+ @Override
+ protected boolean isUserVisibleOnDisplay(int userId, int displayId) {
+ return mUmi.isUserVisible(userId, displayId);
+ }
+
+ @Override
+ protected int getDisplayAssignedToUser(int userId) {
+ return mUmi.getDisplayAssignedToUser(userId);
+ }
+
+ @Override
+ protected int getUserAssignedToDisplay(int displayId) {
+ return mUmi.getUserAssignedToDisplay(displayId);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
new file mode 100644
index 000000000000..991053a120ef
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static android.os.UserHandle.USER_NULL;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import androidx.test.annotation.UiThreadTest;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.LocalServices;
+import com.android.server.am.UserState;
+import com.android.server.pm.UserManagerService.UserData;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Base class for {@link UserManagerInternalTest} and {@link UserManagerInternalTest}.
+ *
+ * <p>{@link UserManagerService} and its {@link UserManagerInternal} implementation have a
+ * "symbiotic relationship - some methods of the former simply call the latter and vice versa.
+ *
+ * <p>Ideally, only one of them should have the logic, but since that's not the case, this class
+ * provices the infra to make it easier to test both (which in turn would make it easier / safer to
+ * refactor their logic later).
+ */
+abstract class UserManagerServiceOrInternalTestCase extends ExtendedMockitoTestCase {
+
+ private static final String TAG = UserManagerServiceOrInternalTestCase.class.getSimpleName();
+
+ /**
+ * Id for a simple user (that doesn't have profiles).
+ */
+ protected static final int USER_ID = 600;
+
+ /**
+ * Id for another simple user.
+ */
+ protected static final int OTHER_USER_ID = 666;
+
+ /**
+ * Id for a user that has one profile (whose id is {@link #PROFILE_USER_ID}.
+ *
+ * <p>You can use {@link #addDefaultProfileAndParent()} to add both of this user to the service.
+ */
+ protected static final int PARENT_USER_ID = 642;
+
+ /**
+ * Id for a profile whose parent is {@link #PARENTUSER_ID}.
+ *
+ * <p>You can use {@link #addDefaultProfileAndParent()} to add both of this user to the service.
+ */
+ protected static final int PROFILE_USER_ID = 643;
+
+ /**
+ * Id of a secondary display (i.e, not {@link android.view.Display.DEFAULT_DISPLAY}).
+ */
+ protected static final int SECONDARY_DISPLAY_ID = 42;
+
+ /**
+ * Id of another secondary display (i.e, not {@link android.view.Display.DEFAULT_DISPLAY}).
+ */
+ protected static final int OTHER_SECONDARY_DISPLAY_ID = 108;
+
+ private final Object mPackagesLock = new Object();
+ private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation()
+ .getTargetContext();
+ private final SparseArray<UserData> mUsers = new SparseArray<>();
+
+ // TODO(b/244644281): manipulating mUsersOnSecondaryDisplays directly leaks implementation
+ // details into the unit test, but it's fine for now - in the long term, this logic should be
+ // refactored into a proper UserDisplayAssignment class.
+ private final SparseIntArray mUsersOnSecondaryDisplays = new SparseIntArray();
+
+ private Context mSpiedContext;
+ private UserManagerService mStandardUms;
+ private UserManagerService mMumdUms;
+ private UserManagerInternal mStandardUmi;
+ private UserManagerInternal mMumdUmi;
+
+ private @Mock PackageManagerService mMockPms;
+ private @Mock UserDataPreparer mMockUserDataPreparer;
+ private @Mock ActivityManagerInternal mActivityManagerInternal;
+
+ /**
+ * Reference to the {@link UserManagerService} being tested.
+ *
+ * <p>By default, such service doesn't support {@code MUMD} (Multiple Users on Multiple
+ * Displays), but that can be changed by calling {@link #enableUsersOnSecondaryDisplays()}.
+ */
+ protected UserManagerService mUms;
+
+ /**
+ * Reference to the {@link UserManagerInternal} being tested.
+ *
+ * <p>By default, such service doesn't support {@code MUMD} (Multiple Users on Multiple
+ * Displays), but that can be changed by calling {@link #enableUsersOnSecondaryDisplays()}.
+ */
+ protected UserManagerInternal mUmi;
+
+ @Override
+ protected void initializeSession(StaticMockitoSessionBuilder builder) {
+ builder
+ .spyStatic(UserManager.class)
+ .spyStatic(LocalServices.class);
+ }
+
+ @Before
+ @UiThreadTest // Needed to initialize main handler
+ public final void setFixtures() {
+ mSpiedContext = spy(mRealContext);
+
+ // Called when WatchedUserStates is constructed
+ doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache());
+
+ // Need to set both UserManagerService instances here, as they need to be run in the
+ // UiThread
+
+ // mMumdUms / mMumdUmi
+ mockIsUsersOnSecondaryDisplaysEnabled(/* usersOnSecondaryDisplaysEnabled= */ true);
+ mMumdUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
+ mPackagesLock, mRealContext.getDataDir(), mUsers, mUsersOnSecondaryDisplays);
+ assertWithMessage("UserManagerService.isUsersOnSecondaryDisplaysEnabled()")
+ .that(mMumdUms.isUsersOnSecondaryDisplaysEnabled())
+ .isTrue();
+ mMumdUmi = LocalServices.getService(UserManagerInternal.class);
+ assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mMumdUmi)
+ .isNotNull();
+ resetUserManagerInternal();
+
+ // mStandardUms / mStandardUmi
+ mockIsUsersOnSecondaryDisplaysEnabled(/* usersOnSecondaryDisplaysEnabled= */ false);
+ mStandardUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
+ mPackagesLock, mRealContext.getDataDir(), mUsers, mUsersOnSecondaryDisplays);
+ assertWithMessage("UserManagerService.isUsersOnSecondaryDisplaysEnabled()")
+ .that(mStandardUms.isUsersOnSecondaryDisplaysEnabled())
+ .isFalse();
+ mStandardUmi = LocalServices.getService(UserManagerInternal.class);
+ assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mStandardUmi)
+ .isNotNull();
+ setServiceFixtures(/*usersOnSecondaryDisplaysEnabled= */ false);
+ }
+
+ @After
+ public final void resetUserManagerInternal() {
+ // LocalServices follows the "Highlander rule" - There can be only one!
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // Methods whose UMS implementation calls UMI or vice-versa - they're tested in this class, //
+ // but the subclass must provide the proper implementation //
+ //////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected abstract boolean isUserVisible(int userId);
+ protected abstract boolean isUserVisibleOnDisplay(int userId, int displayId);
+ protected abstract int getDisplayAssignedToUser(int userId);
+ protected abstract int getUserAssignedToDisplay(int displayId);
+
+ /////////////////////////////////
+ // Tests for the above methods //
+ /////////////////////////////////
+
+ @Test
+ public void testIsUserVisible_invalidUser() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("isUserVisible(%s)", USER_NULL).that(isUserVisible(USER_NULL)).isFalse();
+ }
+
+ @Test
+ public void testIsUserVisible_currentUser() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isTrue();
+ }
+
+ @Test
+ public void testIsUserVisible_nonCurrentUser() {
+ mockCurrentUser(OTHER_USER_ID);
+
+ assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isFalse();
+ }
+
+ @Test
+ public void testIsUserVisible_startedProfileOfcurrentUser() {
+ addDefaultProfileAndParent();
+ mockCurrentUser(PARENT_USER_ID);
+ startDefaultProfile();
+ setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
+
+ assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID).that(isUserVisible(PROFILE_USER_ID))
+ .isTrue();
+ }
+
+ @Test
+ public void testIsUserVisible_stoppedProfileOfcurrentUser() {
+ addDefaultProfileAndParent();
+ mockCurrentUser(PARENT_USER_ID);
+ stopDefaultProfile();
+
+ assertWithMessage("isUserVisible(%s)", PROFILE_USER_ID).that(isUserVisible(PROFILE_USER_ID))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsUserVisible_bgUserOnSecondaryDisplay() {
+ enableUsersOnSecondaryDisplays();
+ mockCurrentUser(OTHER_USER_ID);
+ assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+ assertWithMessage("isUserVisible(%s)", USER_ID).that(isUserVisible(USER_ID)).isTrue();
+ }
+
+ // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
+ // isUserVisible() for bg users relies only on the user / display assignments
+
+ @Test
+ public void testIsUserVisibleOnDisplay_invalidUser() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_NULL, DEFAULT_DISPLAY)
+ .that(isUserVisibleOnDisplay(USER_NULL, DEFAULT_DISPLAY)).isFalse();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_currentUserInvalidDisplay() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, INVALID_DISPLAY)
+ .that(isUserVisibleOnDisplay(USER_ID, INVALID_DISPLAY)).isTrue();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_currentUserDefaultDisplay() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, DEFAULT_DISPLAY)
+ .that(isUserVisibleOnDisplay(USER_ID, DEFAULT_DISPLAY)).isTrue();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_currentUserSecondaryDisplay() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
+ .that(isUserVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_nonCurrentUserDefaultDisplay() {
+ mockCurrentUser(OTHER_USER_ID);
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, DEFAULT_DISPLAY)
+ .that(isUserVisibleOnDisplay(USER_ID, DEFAULT_DISPLAY)).isFalse();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserInvalidDisplay() {
+ addDefaultProfileAndParent();
+ mockCurrentUser(PARENT_USER_ID);
+ startDefaultProfile();
+ setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY)
+ .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserInvalidDisplay() {
+ addDefaultProfileAndParent();
+ mockCurrentUser(PARENT_USER_ID);
+ stopDefaultProfile();
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY)
+ .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isFalse();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserDefaultDisplay() {
+ addDefaultProfileAndParent();
+ mockCurrentUser(PARENT_USER_ID);
+ startDefaultProfile();
+ setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY)
+ .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserDefaultDisplay() {
+ addDefaultProfileAndParent();
+ mockCurrentUser(PARENT_USER_ID);
+ stopDefaultProfile();
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, DEFAULT_DISPLAY)
+ .that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isFalse();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_startedProfileOfcurrentUserSecondaryDisplay() {
+ addDefaultProfileAndParent();
+ mockCurrentUser(PARENT_USER_ID);
+ startDefaultProfile();
+ setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
+ .that(isUserVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_stoppedProfileOfcurrentUserSecondaryDisplay() {
+ addDefaultProfileAndParent();
+ mockCurrentUser(PARENT_USER_ID);
+ stopDefaultProfile();
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, SECONDARY_DISPLAY_ID)
+ .that(isUserVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID)).isFalse();
+ }
+
+ @Test
+ public void testIsUserVisibleOnDisplay_bgUserOnSecondaryDisplay() {
+ enableUsersOnSecondaryDisplays();
+ mockCurrentUser(OTHER_USER_ID);
+ assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+ assertWithMessage("isUserVisibleOnDisplay(%s, %s)", USER_ID, SECONDARY_DISPLAY_ID)
+ .that(isUserVisibleOnDisplay(USER_ID, SECONDARY_DISPLAY_ID)).isTrue();
+ }
+
+ // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
+ // isUserVisibleOnDisplay() for bg users relies only on the user / display assignments
+
+ @Test
+ public void testGetDisplayAssignedToUser_invalidUser() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("getDisplayAssignedToUser(%s)", USER_NULL)
+ .that(getDisplayAssignedToUser(USER_NULL)).isEqualTo(INVALID_DISPLAY);
+ }
+
+ @Test
+ public void testGetDisplayAssignedToUser_currentUser() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID)
+ .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testGetDisplayAssignedToUser_nonCurrentUser() {
+ mockCurrentUser(OTHER_USER_ID);
+
+ assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID)
+ .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(INVALID_DISPLAY);
+ }
+
+ @Test
+ public void testGetDisplayAssignedToUser_startedProfileOfcurrentUser() {
+ addDefaultProfileAndParent();
+ mockCurrentUser(PARENT_USER_ID);
+ startDefaultProfile();
+ setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
+
+ assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID)
+ .that(getDisplayAssignedToUser(PROFILE_USER_ID)).isEqualTo(DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testGetDisplayAssignedToUser_stoppedProfileOfcurrentUser() {
+ addDefaultProfileAndParent();
+ mockCurrentUser(PARENT_USER_ID);
+ stopDefaultProfile();
+
+ assertWithMessage("getDisplayAssignedToUser(%s)", PROFILE_USER_ID)
+ .that(getDisplayAssignedToUser(PROFILE_USER_ID)).isEqualTo(INVALID_DISPLAY);
+ }
+
+ @Test
+ public void testGetDisplayAssignedToUser_bgUserOnSecondaryDisplay() {
+ enableUsersOnSecondaryDisplays();
+ mockCurrentUser(OTHER_USER_ID);
+ assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+ assertWithMessage("getDisplayAssignedToUser(%s)", USER_ID)
+ .that(getDisplayAssignedToUser(USER_ID)).isEqualTo(SECONDARY_DISPLAY_ID);
+ }
+
+ // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
+ // getDisplayAssignedToUser() for bg users relies only on the user / display assignments
+
+ @Test
+ public void testGetUserAssignedToDisplay_invalidDisplay() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("getUserAssignedToDisplay(%s)", INVALID_DISPLAY)
+ .that(getUserAssignedToDisplay(INVALID_DISPLAY)).isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetUserAssignedToDisplay_defaultDisplay() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("getUserAssignedToDisplay(%s)", DEFAULT_DISPLAY)
+ .that(getUserAssignedToDisplay(DEFAULT_DISPLAY)).isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetUserAssignedToDisplay_secondaryDisplay() {
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
+ .that(getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetUserAssignedToDisplay_mumd_bgUserOnSecondaryDisplay() {
+ enableUsersOnSecondaryDisplays();
+ mockCurrentUser(OTHER_USER_ID);
+ assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+ assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
+ .that(getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetUserAssignedToDisplay_mumd_noUserOnSecondaryDisplay() {
+ enableUsersOnSecondaryDisplays();
+ mockCurrentUser(USER_ID);
+
+ assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
+ .that(getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
+ }
+
+ // TODO(b/244644281): scenario below shouldn't happen on "real life", as the profile cannot be
+ // started on secondary display if its parent isn't, so we might need to remove (or refactor
+ // this test) if/when the underlying logic changes
+ @Test
+ public void testGetUserAssignedToDisplay_mumd_profileOnSecondaryDisplay() {
+ enableUsersOnSecondaryDisplays();
+ addDefaultProfileAndParent();
+ mockCurrentUser(USER_ID);
+ assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
+
+ assertWithMessage("getUserAssignedToDisplay(%s)", SECONDARY_DISPLAY_ID)
+ .that(getUserAssignedToDisplay(SECONDARY_DISPLAY_ID)).isEqualTo(USER_ID);
+ }
+
+ // NOTE: we don't need to add tests for profiles (started / stopped profiles of bg user), as
+ // getUserAssignedToDisplay() for bg users relies only on the user / display assignments
+
+
+ ///////////////////////////////////////////
+ // Helper methods exposed to sub-classes //
+ ///////////////////////////////////////////
+
+ /**
+ * Change test fixtures to use a version that supports {@code MUMD} (Multiple Users on Multiple
+ * Displays).
+ */
+ protected final void enableUsersOnSecondaryDisplays() {
+ setServiceFixtures(/* usersOnSecondaryDisplaysEnabled= */ true);
+ }
+
+ protected final void mockCurrentUser(@UserIdInt int userId) {
+ mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
+
+ when(mActivityManagerInternal.getCurrentUserId()).thenReturn(userId);
+ }
+
+ protected final <T> void mockGetLocalService(Class<T> serviceClass, T service) {
+ doReturn(service).when(() -> LocalServices.getService(serviceClass));
+ }
+
+ protected final void addDefaultProfileAndParent() {
+ addUser(PARENT_USER_ID);
+ addProfile(PROFILE_USER_ID, PARENT_USER_ID);
+ }
+
+ protected final void addProfile(@UserIdInt int profileId, @UserIdInt int parentId) {
+ TestUserData profileData = new TestUserData(profileId);
+ profileData.info.flags = UserInfo.FLAG_PROFILE;
+ profileData.info.profileGroupId = parentId;
+
+ addUserData(profileData);
+ }
+
+ protected final void addUser(@UserIdInt int userId) {
+ TestUserData userData = new TestUserData(userId);
+
+ addUserData(userData);
+ }
+
+ protected final void startDefaultProfile() {
+ setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
+ }
+
+ protected final void stopDefaultProfile() {
+ // TODO(b/244798930): should set it to STATE_STOPPING or STATE_SHUTDOWN instead
+ removeUserState(PROFILE_USER_ID);
+ }
+
+ // NOTE: should only called by tests that indirectly needs to check user assignments (like
+ // isUserVisible), not by tests for the user assignment methods per se.
+ protected final void assignUserToDisplay(@UserIdInt int userId, int displayId) {
+ mUsersOnSecondaryDisplays.put(userId, displayId);
+ }
+
+ protected final void assertNoUserAssignedToDisplay() {
+ assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap())
+ .isEmpty();
+ }
+
+ protected final void assertUserAssignedToDisplay(@UserIdInt int userId, int displayId) {
+ assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap())
+ .containsExactly(userId, displayId);
+ }
+
+ @SafeVarargs
+ protected final void assertUsersAssignedToDisplays(@UserIdInt int userId, int displayId,
+ @SuppressWarnings("unchecked") Pair<Integer, Integer>... others) {
+ Object[] otherObjects = new Object[others.length * 2];
+ for (int i = 0; i < others.length; i++) {
+ Pair<Integer, Integer> other = others[i];
+ otherObjects[i * 2] = other.first;
+ otherObjects[i * 2 + 1] = other.second;
+
+ }
+ assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap())
+ .containsExactly(userId, displayId, otherObjects);
+ }
+
+ protected static Pair<Integer, Integer> pair(@UserIdInt int userId, int secondaryDisplayId) {
+ return new Pair<>(userId, secondaryDisplayId);
+ }
+
+ ///////////////////
+ // Private infra //
+ ///////////////////
+
+ private void setServiceFixtures(boolean usersOnSecondaryDisplaysEnabled) {
+ Log.d(TAG, "Setting fixtures for usersOnSecondaryDisplaysEnabled="
+ + usersOnSecondaryDisplaysEnabled);
+ if (usersOnSecondaryDisplaysEnabled) {
+ mUms = mMumdUms;
+ mUmi = mMumdUmi;
+ } else {
+ mUms = mStandardUms;
+ mUmi = mStandardUmi;
+ }
+ }
+
+ private void mockIsUsersOnSecondaryDisplaysEnabled(boolean enabled) {
+ Log.d(TAG, "Mocking UserManager.isUsersOnSecondaryDisplaysEnabled() to return " + enabled);
+ doReturn(enabled).when(() -> UserManager.isUsersOnSecondaryDisplaysEnabled());
+ }
+
+ private void addUserData(TestUserData userData) {
+ Log.d(TAG, "Adding " + userData);
+ mUsers.put(userData.info.id, userData);
+ }
+
+ private void setUserState(@UserIdInt int userId, int userState) {
+ mUmi.setUserState(userId, userState);
+ }
+
+ private void removeUserState(@UserIdInt int userId) {
+ mUmi.removeUserState(userId);
+ }
+
+ private Map<Integer, Integer> usersOnSecondaryDisplaysAsMap() {
+ int size = mUsersOnSecondaryDisplays.size();
+ Map<Integer, Integer> map = new LinkedHashMap<>(size);
+ for (int i = 0; i < size; i++) {
+ map.put(mUsersOnSecondaryDisplays.keyAt(i), mUsersOnSecondaryDisplays.valueAt(i));
+ }
+ return map;
+ }
+
+ private static final class TestUserData extends UserData {
+
+ @SuppressWarnings("deprecation")
+ TestUserData(@UserIdInt int userId) {
+ info = new UserInfo();
+ info.id = userId;
+ }
+
+ @Override
+ public String toString() {
+ return "TestUserData[" + info.toFullString() + "]";
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 64931116bd01..8388a70a87e5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -15,87 +15,20 @@
*/
package com.android.server.pm;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-
import static com.google.common.truth.Truth.assertWithMessage;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
-import android.content.Context;
-import android.content.pm.UserInfo;
import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.test.annotation.UiThreadTest;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-import com.android.server.ExtendedMockitoTestCase;
-import com.android.server.LocalServices;
-import com.android.server.am.UserState;
-import com.android.server.pm.UserManagerService.UserData;
-
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
-import org.mockito.Mock;
/**
* Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest}
*/
-public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
-
- private static final String TAG = UserManagerServiceTest.class.getSimpleName();
-
- private final Object mPackagesLock = new Object();
- private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation()
- .getTargetContext();
- private Context mSpiedContext;
-
- private @Mock PackageManagerService mMockPms;
- private @Mock UserDataPreparer mMockUserDataPreparer;
- private @Mock ActivityManagerInternal mActivityManagerInternal;
-
- private final SparseArray<UserData> mUsers = new SparseArray<>();
- private UserManagerService mUms;
- private UserManagerInternal mUmi;
-
- @Override
- protected void initializeSession(StaticMockitoSessionBuilder builder) {
- builder
- .spyStatic(UserManager.class)
- .spyStatic(LocalServices.class);
- }
-
- @Before
- @UiThreadTest // Needed to initialize main handler
- public void setFixtures() {
- mSpiedContext = spy(mRealContext);
-
- // Called when WatchedUserStates is constructed
- doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache());
-
- mUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
- mPackagesLock, mRealContext.getDataDir(), mUsers);
- mUmi = LocalServices.getService(UserManagerInternal.class);
-
- assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mUmi)
- .isNotNull();
- }
-
- @After
- public void resetLocalService() {
- // LocalServices follows the "Highlander rule" - There can be only one!
- LocalServices.removeServiceForTest(UserManagerInternal.class);
- }
+public final class UserManagerServiceTest extends UserManagerServiceOrInternalTestCase {
@Test
- public void testgetCurrentUserId_amInternalNotReady() {
+ public void testGetCurrentUserId_amInternalNotReady() {
mockGetLocalService(ActivityManagerInternal.class, null);
assertWithMessage("getCurrentUserId()").that(mUms.getCurrentUserId())
@@ -103,119 +36,75 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
}
@Test
- public void testgetCurrentUserId() {
- mockCurrentUser(42);
+ public void testGetCurrentUserId() {
+ mockCurrentUser(USER_ID);
assertWithMessage("getCurrentUserId()").that(mUms.getCurrentUserId())
- .isEqualTo(42);
+ .isEqualTo(USER_ID);
}
@Test
public void testIsCurrentUserOrRunningProfileOfCurrentUser_currentUser() {
- int userId = 42;
- mockCurrentUser(userId);
+ mockCurrentUser(USER_ID);
- assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId)
- .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(userId)).isTrue();
+ assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", USER_ID)
+ .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(USER_ID)).isTrue();
}
@Test
public void testIsCurrentUserOrRunningProfileOfCurrentUser_notCurrentUser() {
- int userId = 42;
- mockCurrentUser(108);
+ mockCurrentUser(OTHER_USER_ID);
- assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", userId)
- .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(userId)).isFalse();
+ assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", USER_ID)
+ .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(USER_ID)).isFalse();
}
@Test
public void testIsCurrentUserOrRunningProfileOfCurrentUser_startedProfileOfCurrentUser() {
- int parentId = 108;
- int profileId = 42;
- addUser(parentId);
- addProfile(profileId, parentId);
- mockCurrentUser(parentId);
- setUserState(profileId, UserState.STATE_RUNNING_UNLOCKED);
-
- assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId)
- .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isTrue();
+ addDefaultProfileAndParent();
+ startDefaultProfile();
+ mockCurrentUser(PARENT_USER_ID);
+
+ assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", PROFILE_USER_ID)
+ .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isTrue();
}
@Test
public void testIsCurrentUserOrRunningProfileOfCurrentUser_stoppedProfileOfCurrentUser() {
- int parentId = 108;
- int profileId = 42;
- addUser(parentId);
- addProfile(profileId, parentId);
- mockCurrentUser(parentId);
- // TODO(b/244798930): should set it to STATE_STOPPING or STATE_SHUTDOWN instead
- removeUserState(profileId);
-
- assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId)
- .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isFalse();
+ addDefaultProfileAndParent();
+ stopDefaultProfile();
+ mockCurrentUser(PARENT_USER_ID);
+
+ assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", PROFILE_USER_ID)
+ .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isFalse();
}
@Test
public void testIsCurrentUserOrRunningProfileOfCurrentUser_profileOfNonCurrentUSer() {
- int parentId = 108;
- int profileId = 42;
- int currentUserId = 666;
- addUser(parentId);
- addProfile(profileId, parentId);
- mockCurrentUser(currentUserId);
-
- assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", profileId)
- .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(profileId)).isFalse();
- }
-
- private void mockCurrentUser(@UserIdInt int userId) {
- mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
-
- when(mActivityManagerInternal.getCurrentUserId()).thenReturn(userId);
- }
-
- private <T> void mockGetLocalService(Class<T> serviceClass, T service) {
- doReturn(service).when(() -> LocalServices.getService(serviceClass));
- }
-
- private void addProfile(@UserIdInt int profileId, @UserIdInt int parentId) {
- TestUserData profileData = new TestUserData(profileId);
- profileData.info.flags = UserInfo.FLAG_PROFILE;
- profileData.info.profileGroupId = parentId;
+ addDefaultProfileAndParent();
+ mockCurrentUser(OTHER_USER_ID);
- addUserData(profileData);
+ assertWithMessage("isCurrentUserOrRunningProfileOfCurrentUser(%s)", PROFILE_USER_ID)
+ .that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isFalse();
}
- private void addUser(@UserIdInt int userId) {
- TestUserData userData = new TestUserData(userId);
-
- addUserData(userData);
- }
-
- private void addUserData(TestUserData userData) {
- Log.d(TAG, "Adding " + userData);
- mUsers.put(userData.info.id, userData);
+ @Override
+ protected boolean isUserVisible(int userId) {
+ return mUms.isUserVisibleUnchecked(userId);
}
- private void setUserState(@UserIdInt int userId, int userState) {
- mUmi.setUserState(userId, userState);
+ @Override
+ protected boolean isUserVisibleOnDisplay(int userId, int displayId) {
+ return mUms.isUserVisibleOnDisplay(userId, displayId);
}
- private void removeUserState(@UserIdInt int userId) {
- mUmi.removeUserState(userId);
+ @Override
+ protected int getDisplayAssignedToUser(int userId) {
+ return mUms.getDisplayAssignedToUser(userId);
}
- private static final class TestUserData extends UserData {
-
- @SuppressWarnings("unused")
- TestUserData(@UserIdInt int userId) {
- info = new UserInfo();
- info.id = userId;
- }
-
- @Override
- public String toString() {
- return "TestUserData[" + info.toFullString() + "]";
- }
+ @Override
+ protected int getUserAssignedToDisplay(int displayId) {
+ return mUms.getUserAssignedToDisplay(displayId);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index c7757f7832fa..acbcad54626d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import static com.android.server.accessibility.AccessibilityWindowManagerTest.DisplayIdMatcher.displayId;
import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges;
import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId;
@@ -587,10 +588,12 @@ public class AccessibilityWindowManagerTest {
verify(mMockA11yEventSender, times(2))
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
- allOf(a11yWindowId(currentActiveWindowId),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(currentActiveWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
assertThat(captor.getAllValues().get(1),
- allOf(a11yWindowId(eventWindowId),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(eventWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
}
@@ -600,10 +603,59 @@ public class AccessibilityWindowManagerTest {
DEFAULT_FOCUSED_INDEX);
final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId(
AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
- assertThat(currentA11yFocusedWindowId, is(not(eventWindowId)));
+ assertThat(currentA11yFocusedWindowId, is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID));
+
+ final int noUse = 0;
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ eventWindowId,
+ AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ noUse);
+ assertThat(mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId));
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(eventWindowId),
+ a11yWindowChanges(
+ AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
+ }
+
+ @Test
+ public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_defaultToSecondary()
+ throws RemoteException {
+ runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
+ Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID);
+ }
+
+ @Test
+ public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_SecondaryToDefault()
+ throws RemoteException {
+ runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
+ SECONDARY_DISPLAY_ID, Display.DEFAULT_DISPLAY);
+ }
+ private void runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest(
+ int initialDisplayId, int eventDisplayId) throws RemoteException {
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+ final int initialWindowId = getWindowIdFromWindowInfosForDisplay(
+ initialDisplayId, DEFAULT_FOCUSED_INDEX);
final int noUse = 0;
mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
+ initialWindowId,
+ AccessibilityNodeInfo.ROOT_NODE_ID,
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ noUse);
+ assertThat(mA11yWindowManager.getFocusedWindowId(
+ AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(initialWindowId));
+ Mockito.reset(mMockA11yEventSender);
+
+ final int eventWindowId = getWindowIdFromWindowInfosForDisplay(
+ eventDisplayId, DEFAULT_FOCUSED_INDEX);
+ mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
eventWindowId,
AccessibilityNodeInfo.ROOT_NODE_ID,
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
@@ -615,11 +667,13 @@ public class AccessibilityWindowManagerTest {
verify(mMockA11yEventSender, times(2))
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
- allOf(a11yWindowId(currentA11yFocusedWindowId),
+ allOf(displayId(initialDisplayId),
+ a11yWindowId(initialWindowId),
a11yWindowChanges(
AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
assertThat(captor.getAllValues().get(1),
- allOf(a11yWindowId(eventWindowId),
+ allOf(displayId(eventDisplayId),
+ a11yWindowId(eventWindowId),
a11yWindowChanges(
AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)));
}
@@ -674,10 +728,12 @@ public class AccessibilityWindowManagerTest {
verify(mMockA11yEventSender, times(2))
.sendAccessibilityEventForCurrentUserLocked(captor.capture());
assertThat(captor.getAllValues().get(0),
- allOf(a11yWindowId(eventWindowId),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(eventWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
assertThat(captor.getAllValues().get(1),
- allOf(a11yWindowId(currentActiveWindowId),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(currentActiveWindowId),
a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)));
}
@@ -861,6 +917,77 @@ public class AccessibilityWindowManagerTest {
assertTrue(TextUtils.equals(layoutParams.accessibilityTitle, a11yWindow.getTitle()));
}
+ @Test
+ public void sendAccessibilityEventOnWindowRemoval() {
+ final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+
+ // Removing index 0 because it's not focused, and avoids unnecessary layer change.
+ final int windowId =
+ getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
+ infos.remove(0);
+ for (WindowInfo info : infos) {
+ // Adjust layer number because it should start from 0.
+ info.layer--;
+ }
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(windowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED)));
+ }
+
+ @Test
+ public void sendAccessibilityEventOnWindowAddition() throws RemoteException {
+ final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+
+ for (WindowInfo info : infos) {
+ // Adjust layer number because new window will have 0 so that layer number in
+ // A11yWindowInfo in window won't be changed.
+ info.layer++;
+ }
+
+ final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, USER_SYSTEM_ID);
+ addWindowInfo(infos, token, 0);
+ final int windowId =
+ getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, infos.size() - 1);
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(windowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED)));
+ }
+
+ @Test
+ public void sendAccessibilityEventOnWindowChange() {
+ final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY);
+ infos.get(0).title = "new title";
+ final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0);
+
+ onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
+
+ final ArgumentCaptor<AccessibilityEvent> captor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mMockA11yEventSender, times(1))
+ .sendAccessibilityEventForCurrentUserLocked(captor.capture());
+ assertThat(captor.getAllValues().get(0),
+ allOf(displayId(Display.DEFAULT_DISPLAY),
+ a11yWindowId(windowId),
+ a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE)));
+ }
+
private void registerLeashedTokenAndWindowId() {
mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID);
mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID);
@@ -1018,6 +1145,29 @@ public class AccessibilityWindowManagerTest {
}
}
+ static class DisplayIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
+ private final int mDisplayId;
+
+ DisplayIdMatcher(int displayId) {
+ super();
+ mDisplayId = displayId;
+ }
+
+ static DisplayIdMatcher displayId(int displayId) {
+ return new DisplayIdMatcher(displayId);
+ }
+
+ @Override
+ protected boolean matchesSafely(AccessibilityEvent event) {
+ return event.getDisplayId() == mDisplayId;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Matching to displayId " + mDisplayId);
+ }
+ }
+
static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
private int mWindowId;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
index b7f564094cde..cc5ed92e02d9 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
@@ -25,7 +25,6 @@ import static org.mockito.Mockito.when;
import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.IInputManager;
-import android.hardware.input.InputDeviceCountryCode;
import android.hardware.input.InputManager;
import android.os.RemoteException;
import android.testing.TestableLooper;
@@ -81,11 +80,16 @@ class InputManagerMockHelper {
private Void handleNativeOpenInputDevice(InvocationOnMock inv) {
Objects.requireNonNull(mDevicesChangedListener,
"InputController did not register an InputDevicesChangedListener.");
- // We only use a subset of the fields of InputDevice in InputController.
- final InputDevice device = new InputDevice(mDevices.size() /*id*/, 1 /*generation*/, 0,
- inv.getArgument(0) /*name*/, inv.getArgument(1) /*vendorId*/,
- inv.getArgument(2) /*productId*/, inv.getArgument(3) /*descriptor*/, true, 0, 0,
- null, InputDeviceCountryCode.INVALID, false, false, false, false, false);
+
+ final InputDevice device = new InputDevice.Builder()
+ .setId(mDevices.size())
+ .setName(inv.getArgument(0))
+ .setVendorId(inv.getArgument(1))
+ .setProductId(inv.getArgument(2))
+ .setDescriptor(inv.getArgument(3))
+ .setExternal(true)
+ .build();
+
mDevices.add(device);
try {
mDevicesChangedListener.onInputDevicesChanged(
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index eda91330547e..171b895b93aa 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -402,7 +402,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// PO needs to be a DA.
dpm.setActiveAdmin(admin, /*replace=*/ false);
// Fire!
- assertThat(dpm.setProfileOwner(admin, "owner-name", CALLER_USER_HANDLE)).isTrue();
+ assertThat(dpm.setProfileOwner(admin, CALLER_USER_HANDLE)).isTrue();
// Check
assertThat(dpm.getProfileOwnerAsUser(CALLER_USER_HANDLE)).isEqualTo(admin);
});
@@ -966,7 +966,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Set admin1 to active admin and device owner
dpm.setActiveAdmin(admin1, false);
- dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM);
+ dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM);
// Save password history length
dpm.setPasswordHistoryLength(admin1, passwordHistoryLength);
@@ -1074,7 +1074,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.setActiveAdmin(admin2, /* refreshing= */ true, UserHandle.USER_SYSTEM);
assertExpectException(IllegalStateException.class,
/* messageRegex= */ "already has a device owner",
- () -> dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM));
+ () -> dpm.setProfileOwner(admin2, UserHandle.USER_SYSTEM));
// DO admin can't be deactivated.
dpm.removeActiveAdmin(admin1);
@@ -1098,7 +1098,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.setActiveAdmin(admin2, /* refreshing= */ true, CALLER_USER_HANDLE);
assertExpectException(IllegalStateException.class,
/* messageRegex= */ "profile owner is already set",
- () -> dpm.setProfileOwner(admin2, "owner-name", CALLER_USER_HANDLE));
+ () -> dpm.setProfileOwner(admin2, CALLER_USER_HANDLE));
// DO admin can't be deactivated.
dpm.removeActiveAdmin(admin1);
@@ -1124,7 +1124,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.setActiveAdmin(admin1, /* replace =*/ false);
// Fire!
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
// getDeviceOwnerComponent should return the admin1 component.
assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(admin1);
@@ -1181,7 +1181,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// DO needs to be a DA
dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
// DO should be set on headless system user
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
// PO should be set on calling user.
assertThat(dpm.getProfileOwnerAsUser(CALLER_USER_HANDLE)).isEqualTo(admin1);
});
@@ -1353,7 +1353,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertExpectException(IllegalArgumentException.class,
/* messageRegex= */ "Invalid component",
- () -> dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"), /* ownerName= */ null,
+ () -> dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"),
UserHandle.USER_SYSTEM));
}
@@ -1363,13 +1363,13 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Package doesn't exist and caller is not system
assertExpectException(SecurityException.class,
/* messageRegex= */ "Calling identity is not authorized",
- () -> dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM));
+ () -> dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM));
// Package exists, but caller is not system
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
assertExpectException(SecurityException.class,
/* messageRegex= */ "Calling identity is not authorized",
- () -> dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM));
+ () -> dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM));
}
@Test
@@ -1389,7 +1389,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
@@ -1452,7 +1452,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
verify(getServices().ibackupManager, times(1)).setBackupServiceActive(
eq(UserHandle.USER_SYSTEM), eq(false));
@@ -1493,7 +1493,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
// Now call clear from the secondary user, which should throw.
@@ -1527,7 +1527,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Set admin1 to active admin and device owner
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM);
+ dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM);
// Add reset password token
final long handle = 12000;
@@ -1566,8 +1566,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.setActiveAdmin(admin2, /* refreshing= */ true, CALLER_USER_HANDLE);
assertExpectException(IllegalStateException.class,
/* messageRegex= */ "already has a profile owner",
- () -> dpm.setDeviceOwner(admin2, "owner-name",
- CALLER_USER_HANDLE));
+ () -> dpm.setDeviceOwner(admin2, CALLER_USER_HANDLE));
});
}
@@ -1604,13 +1603,13 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Package doesn't exist and caller is not system
assertExpectException(SecurityException.class,
/* messageRegex= */ "Calling identity is not authorized",
- () -> dpm.setProfileOwner(admin1, "owner-name", UserHandle.USER_SYSTEM));
+ () -> dpm.setProfileOwner(admin1, UserHandle.USER_SYSTEM));
// Package exists, but caller is not system
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
assertExpectException(SecurityException.class,
/* messageRegex= */ "Calling identity is not authorized",
- () -> dpm.setProfileOwner(admin1, "owner-name", UserHandle.USER_SYSTEM));
+ () -> dpm.setProfileOwner(admin1, UserHandle.USER_SYSTEM));
}
@Test
@@ -1656,7 +1655,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Set DO on the system user which is only allowed during first boot.
setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
- assertThat(dpm.setDeviceOwner(admin3, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin3, UserHandle.USER_SYSTEM)).isTrue();
assertThat(dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false)).isEqualTo(admin3);
// Then check getDeviceOwnerAdminLocked().
@@ -1979,8 +1978,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Call.
dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name",
- UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertNoDeviceOwnerRestrictions();
reset(getServices().userManagerInternal);
@@ -2358,8 +2356,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name",
- UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertNoDeviceOwnerRestrictions();
@@ -2686,7 +2683,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
() -> dpm.getWifiMacAddress(admin1));
// Test 3. Caller has PO, but not DO.
- assertThat(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setProfileOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertExpectException(SecurityException.class,
/* messageRegex= */ INVALID_CALLING_IDENTITY_MSG,
() -> dpm.getWifiMacAddress(admin1));
@@ -2695,7 +2692,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.clearProfileOwner(admin1);
dpm.setActiveAdmin(admin1, false);
// Test 4, Caller is DO now.
- assertThat(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
// 4-1. But WifiManager is not ready.
assertThat(dpm.getWifiMacAddress(admin1)).isNull();
@@ -2738,14 +2735,14 @@ public class DevicePolicyManagerTest extends DpmTestBase {
INVALID_CALLING_IDENTITY_MSG, () -> dpm.reboot(admin1));
// Set admin1 as PO.
- assertThat(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setProfileOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertExpectException(SecurityException.class, /* messageRegex= */
INVALID_CALLING_IDENTITY_MSG, () -> dpm.reboot(admin1));
// Remove PO and add DO.
dpm.clearProfileOwner(admin1);
dpm.setActiveAdmin(admin1, false);
- assertThat(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
// admin1 is DO.
// Set current call state of device to ringing.
@@ -3030,7 +3027,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Set a device owner on the system user. Check that the system user becomes affiliated.
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
assertThat(dpm.isAffiliatedUser()).isTrue();
assertThat(dpm.getAffiliationIds(admin1).isEmpty()).isTrue();
@@ -3234,7 +3231,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
dpm.setActiveAdmin(admin1, false);
- assertThat(dpm.setProfileOwner(admin1, null, CALLER_USER_HANDLE)).isTrue();
+ assertThat(dpm.setProfileOwner(admin1, CALLER_USER_HANDLE)).isTrue();
mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
}
@@ -3244,7 +3241,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setUpPackageManagerForAdmin(admin1, DpmMockContext.SYSTEM_UID);
dpm.setActiveAdmin(admin1, false);
- assertThat(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setProfileOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
}
@@ -3254,7 +3251,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, false);
- assertThat(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue();
mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
}
@@ -3767,7 +3764,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS);
setUpPackageManagerForFakeAdmin(admin1, managedProfileAdminUid, admin1);
dpm.setActiveAdmin(admin1, false, userId);
- assertThat(dpm.setProfileOwner(admin1, null, userId)).isFalse();
+ assertThat(dpm.setProfileOwner(admin1, userId)).isFalse();
mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
}
@@ -8713,7 +8710,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setUpPackageManagerForFakeAdmin(admin, adminUid, /* enabledSetting= */ null,
appTargetSdk, copyFromAdmin);
dpm.setActiveAdmin(admin, false, userId);
- assertThat(dpm.setProfileOwner(admin, null, userId)).isTrue();
+ assertThat(dpm.setProfileOwner(admin, userId)).isTrue();
mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
}
diff --git a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
new file mode 100644
index 000000000000..6e422fa7af7f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.hardware.BatteryState.STATUS_CHARGING
+import android.hardware.BatteryState.STATUS_FULL
+import android.hardware.input.IInputDeviceBatteryListener
+import android.hardware.input.IInputManager
+import android.hardware.input.InputDeviceCountryCode
+import android.hardware.input.InputManager
+import android.os.Binder
+import android.os.IBinder
+import android.platform.test.annotations.Presubmit
+import android.view.InputDevice
+import androidx.test.InstrumentationRegistry
+import org.junit.After
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.notNull
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+
+/**
+ * Tests for {@link InputDeviceBatteryController}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:InputDeviceBatteryControllerTests
+ */
+@Presubmit
+class BatteryControllerTests {
+ companion object {
+ const val PID = 42
+ const val DEVICE_ID = 13
+ const val SECOND_DEVICE_ID = 11
+ }
+
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ @Mock
+ private lateinit var native: NativeInputManagerService
+ @Mock
+ private lateinit var iInputManager: IInputManager
+
+ private lateinit var batteryController: BatteryController
+ private lateinit var context: Context
+
+ @Before
+ fun setup() {
+ context = spy(ContextWrapper(InstrumentationRegistry.getContext()))
+ val inputManager = InputManager.resetInstance(iInputManager)
+ `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
+ `when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID, SECOND_DEVICE_ID))
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(createInputDevice(DEVICE_ID))
+ `when`(iInputManager.getInputDevice(SECOND_DEVICE_ID))
+ .thenReturn(createInputDevice(SECOND_DEVICE_ID))
+
+ batteryController = BatteryController(context, native)
+ }
+
+ private fun createInputDevice(deviceId: Int): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setExternal(true)
+ .setHasBattery(true)
+ .build()
+
+ @After
+ fun tearDown() {
+ InputManager.clearInstance()
+ }
+
+ private fun createMockListener(): IInputDeviceBatteryListener {
+ val listener = mock(IInputDeviceBatteryListener::class.java)
+ val binder = mock(Binder::class.java)
+ `when`(listener.asBinder()).thenReturn(binder)
+ return listener
+ }
+
+ @Test
+ fun testRegisterAndUnregisterBinderLifecycle() {
+ val listener = createMockListener()
+ // Ensure the binder lifecycle is tracked when registering a listener.
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener.asBinder()).linkToDeath(notNull(), anyInt())
+ batteryController.registerBatteryListener(SECOND_DEVICE_ID, listener, PID)
+ verify(listener.asBinder(), times(1)).linkToDeath(notNull(), anyInt())
+
+ // Ensure the binder lifecycle stops being tracked when all devices stopped being monitored.
+ batteryController.unregisterBatteryListener(SECOND_DEVICE_ID, listener, PID)
+ verify(listener.asBinder(), never()).unlinkToDeath(notNull(), anyInt())
+ batteryController.unregisterBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener.asBinder()).unlinkToDeath(notNull(), anyInt())
+ }
+
+ @Test
+ fun testOneListenerPerProcess() {
+ val listener1 = createMockListener()
+ batteryController.registerBatteryListener(DEVICE_ID, listener1, PID)
+ verify(listener1.asBinder()).linkToDeath(notNull(), anyInt())
+
+ // If a second listener is added for the same process, a security exception is thrown.
+ val listener2 = createMockListener()
+ try {
+ batteryController.registerBatteryListener(DEVICE_ID, listener2, PID)
+ fail("Expected security exception when registering more than one listener per process")
+ } catch (ignored: SecurityException) {
+ }
+ }
+
+ @Test
+ fun testProcessDeathRemovesListener() {
+ val deathRecipient = ArgumentCaptor.forClass(IBinder.DeathRecipient::class.java)
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener.asBinder()).linkToDeath(deathRecipient.capture(), anyInt())
+
+ // When the binder dies, the callback is unregistered.
+ deathRecipient.value!!.binderDied(listener.asBinder())
+ verify(listener.asBinder()).unlinkToDeath(notNull(), anyInt())
+
+ // It is now possible to register the same listener again.
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener.asBinder(), times(2)).linkToDeath(notNull(), anyInt())
+ }
+
+ @Test
+ fun testRegisteringListenerNotifiesStateImmediately() {
+ `when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_FULL)
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(100)
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
+ eq(STATUS_FULL), eq(1f), anyLong())
+
+ `when`(native.getBatteryStatus(SECOND_DEVICE_ID)).thenReturn(STATUS_CHARGING)
+ `when`(native.getBatteryCapacity(SECOND_DEVICE_ID)).thenReturn(78)
+ batteryController.registerBatteryListener(SECOND_DEVICE_ID, listener, PID)
+ verify(listener).onBatteryStateChanged(eq(SECOND_DEVICE_ID), eq(true /*isPresent*/),
+ eq(STATUS_CHARGING), eq(0.78f), anyLong())
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index 047fcd6ed108..d5b923a81e4a 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.BatteryManager;
import android.os.BatteryStats;
+import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.MeasuredEnergyDetails;
import android.os.Parcel;
import android.util.Log;
@@ -40,7 +41,9 @@ import com.android.internal.os.Clock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.io.File;
@@ -63,6 +66,8 @@ public class BatteryStatsHistoryTest {
private final Clock mClock = new MockClock();
private BatteryStatsHistory mHistory;
@Mock
+ private BatteryStatsHistory.TraceDelegate mTracer;
+ @Mock
private BatteryStatsHistory.HistoryStepDetailsCalculator mStepDetailsCalculator;
@Before
@@ -79,13 +84,70 @@ public class BatteryStatsHistoryTest {
}
mHistoryDir.delete();
mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
- mStepDetailsCalculator, mClock);
+ mStepDetailsCalculator, mClock, mTracer);
when(mStepDetailsCalculator.getHistoryStepDetails())
.thenReturn(new BatteryStats.HistoryStepDetails());
}
@Test
+ public void testAtraceBinaryState1() {
+ mHistory.forceRecordAllHistory();
+
+ InOrder inOrder = Mockito.inOrder(mTracer);
+ Mockito.when(mTracer.tracingEnabled()).thenReturn(true);
+
+ mHistory.recordStateStartEvent(mClock.elapsedRealtime(),
+ mClock.uptimeMillis(), HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG);
+ mHistory.recordStateStopEvent(mClock.elapsedRealtime(),
+ mClock.uptimeMillis(), HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG);
+ mHistory.recordStateStartEvent(mClock.elapsedRealtime(),
+ mClock.uptimeMillis(), HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG);
+
+ inOrder.verify(mTracer).traceCounter("battery_stats.mobile_radio", 1);
+ inOrder.verify(mTracer).traceCounter("battery_stats.mobile_radio", 0);
+ inOrder.verify(mTracer).traceCounter("battery_stats.mobile_radio", 1);
+ }
+
+ @Test
+ public void testAtraceBinaryState2() {
+ mHistory.forceRecordAllHistory();
+
+ InOrder inOrder = Mockito.inOrder(mTracer);
+ Mockito.when(mTracer.tracingEnabled()).thenReturn(true);
+
+ mHistory.recordState2StartEvent(mClock.elapsedRealtime(),
+ mClock.uptimeMillis(), HistoryItem.STATE2_WIFI_ON_FLAG);
+ mHistory.recordState2StopEvent(mClock.elapsedRealtime(),
+ mClock.uptimeMillis(), HistoryItem.STATE2_WIFI_ON_FLAG);
+ mHistory.recordState2StartEvent(mClock.elapsedRealtime(),
+ mClock.uptimeMillis(), HistoryItem.STATE2_WIFI_ON_FLAG);
+
+ inOrder.verify(mTracer).traceCounter("battery_stats.wifi", 1);
+ inOrder.verify(mTracer).traceCounter("battery_stats.wifi", 0);
+ inOrder.verify(mTracer).traceCounter("battery_stats.wifi", 1);
+ }
+
+ @Test
+ public void testAtraceNumericalState() {
+ mHistory.forceRecordAllHistory();
+
+ InOrder inOrder = Mockito.inOrder(mTracer);
+ Mockito.when(mTracer.tracingEnabled()).thenReturn(true);
+
+ mHistory.recordDataConnectionTypeChangeEvent(mClock.elapsedRealtime(),
+ mClock.uptimeMillis(), 1);
+ mHistory.recordDataConnectionTypeChangeEvent(mClock.elapsedRealtime(),
+ mClock.uptimeMillis(), 2);
+ mHistory.recordDataConnectionTypeChangeEvent(mClock.elapsedRealtime(),
+ mClock.uptimeMillis(), 3);
+
+ inOrder.verify(mTracer).traceCounter("battery_stats.data_conn", 1);
+ inOrder.verify(mTracer).traceCounter("battery_stats.data_conn", 2);
+ inOrder.verify(mTracer).traceCounter("battery_stats.data_conn", 3);
+ }
+
+ @Test
public void testConstruct() {
createActiveFile(mHistory);
verifyFileNumbers(mHistory, Arrays.asList(0));
@@ -131,7 +193,7 @@ public class BatteryStatsHistoryTest {
// create a new BatteryStatsHistory object, it will pick up existing history files.
BatteryStatsHistory history2 = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
- null, mClock);
+ null, mClock, mTracer);
// verify constructor can pick up all files from file system.
verifyFileNumbers(history2, fileList);
verifyActiveFile(history2, "33.bin");
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
index ec4ad8965f7d..2ac8b3700043 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
@@ -33,7 +33,6 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.IInputManager;
-import android.hardware.input.InputDeviceCountryCode;
import android.hardware.input.InputManager;
import android.os.CombinedVibration;
import android.os.Handler;
@@ -328,10 +327,11 @@ public class InputDeviceDelegateTest {
}
private InputDevice createInputDevice(int id, boolean hasVibrator) {
- return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
- null, InputDeviceCountryCode.INVALID, hasVibrator, false, false,
- false /* hasSensor */, false /* hasBattery */);
-
-
+ return new InputDevice.Builder()
+ .setId(id)
+ .setName("name")
+ .setDescriptor("descriptor")
+ .setHasVibrator(hasVibrator)
+ .build();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 8d53b71a93dc..c46fecd1a55e 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -46,7 +46,6 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManagerInternal;
import android.hardware.input.IInputManager;
-import android.hardware.input.InputDeviceCountryCode;
import android.hardware.input.InputManager;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.IVibratorManager;
@@ -1980,9 +1979,11 @@ public class VibratorManagerServiceTest {
}
private InputDevice createInputDeviceWithVibrator(int id) {
- return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
- null, InputDeviceCountryCode.INVALID, /* hasVibrator= */ true, false, false, false,
- false);
+ return new InputDevice.Builder()
+ .setId(id)
+ .setName("Test Device " + id)
+ .setHasVibrator(true)
+ .build();
}
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 6ed84606176a..333be7bd8c61 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2991,6 +2991,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// Add a decor insets provider window.
final WindowState navbar = createNavBarWithProvidedInsets(squareDisplay);
squareDisplay.getDisplayPolicy().updateDecorInsetsInfoIfNeeded(navbar);
+ squareDisplay.sendNewConfiguration();
final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
// create a fixed portrait activity
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 20b1120d7d3e..2fccd64bceca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -220,10 +220,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
// Check that changes are reported
Configuration c = new Configuration(newDisp1.getRequestedOverrideConfiguration());
c.windowConfiguration.setBounds(new Rect(0, 0, 1000, 1300));
- newDisp1.onRequestedOverrideConfigurationChanged(c);
- mAtm.mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
- newDisp1.mDisplayId, false /* markFrozenIfConfigChanged */,
- false /* deferResume */);
+ newDisp1.performDisplayOverrideConfigUpdate(c);
assertEquals(0, added.size());
assertEquals(1, changed.size());
assertEquals(0, removed.size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
index 71118521acf5..e57ad5d9ff8c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
@@ -17,9 +17,12 @@
package com.android.server.wm;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
import android.app.ActivityOptions;
import android.platform.test.annotations.Presubmit;
+import android.window.WindowContainerToken;
import androidx.test.filters.MediumTest;
@@ -43,4 +46,21 @@ public class SafeActivityOptionsTest {
final ActivityOptions result = options.mergeActivityOptions(opts1, opts2);
assertEquals(6, result.getLaunchDisplayId());
}
+
+ @Test
+ public void test_selectiveCloneDisplayOptions() {
+ final WindowContainerToken token = mock(WindowContainerToken.class);
+ final int launchDisplayId = 5;
+ final int callerDisplayId = 6;
+
+ final SafeActivityOptions clone = new SafeActivityOptions(ActivityOptions.makeBasic()
+ .setLaunchTaskDisplayArea(token)
+ .setLaunchDisplayId(launchDisplayId)
+ .setCallerDisplayId(callerDisplayId))
+ .selectiveCloneDisplayOptions();
+
+ assertSame(clone.getOriginalOptions().getLaunchTaskDisplayArea(), token);
+ assertEquals(clone.getOriginalOptions().getLaunchDisplayId(), launchDisplayId);
+ assertEquals(clone.getOriginalOptions().getCallerDisplayId(), callerDisplayId);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 5c7b882f8dab..e8c805450091 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -671,11 +671,9 @@ public class WindowStateTests extends WindowTestsBase {
verify(t, never()).setMatrix(any(), anyInt(), anyInt(), anyInt(), anyInt());
// According to "dp * density / 160 = px", density is scaled and the size in dp is the same.
- final CompatibilityInfo compatInfo = cmp.compatibilityInfoForPackageLocked(
- mContext.getApplicationInfo());
final Configuration winConfig = w.getConfiguration();
final Configuration clientConfig = new Configuration(w.getConfiguration());
- compatInfo.applyToConfiguration(clientConfig.densityDpi, clientConfig);
+ CompatibilityInfo.scaleConfiguration(w.mInvGlobalScale, clientConfig);
assertEquals(winConfig.screenWidthDp, clientConfig.screenWidthDp);
assertEquals(winConfig.screenHeightDp, clientConfig.screenHeightDp);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index aa8011bb899d..b4244dd09bbd 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6893,11 +6893,11 @@ public class TelephonyManager {
*
* @hide
*/
- public void setCellInfoListRate(int rateInMillis) {
+ public void setCellInfoListRate(int rateInMillis, int subId) {
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- telephony.setCellInfoListRate(rateInMillis);
+ telephony.setCellInfoListRate(rateInMillis, subId);
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index f794a7971343..6df9f9b84b6d 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1109,6 +1109,7 @@ public class ApnSetting implements Parcelable {
sb.append(", ").append(mCarrierId);
sb.append(", ").append(mSkip464Xlat);
sb.append(", ").append(mAlwaysOn);
+ sb.append(", ").append(Objects.hash(mUser, mPassword));
return sb.toString();
}
@@ -1297,8 +1298,6 @@ public class ApnSetting implements Parcelable {
other.mLingeringNetworkTypeBitmask)
&& Objects.equals(this.mProfileId, other.mProfileId)
&& Objects.equals(this.mPersistent, other.mPersistent)
- && Objects.equals(this.mMvnoType, other.mMvnoType)
- && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
&& Objects.equals(this.mApnSetId, other.mApnSetId)
&& Objects.equals(this.mCarrierId, other.mCarrierId)
&& Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat)
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 42cac6630506..3032babee07f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -621,7 +621,7 @@ interface ITelephony {
/**
* Sets minimum time in milli-seconds between onCellInfoChanged
*/
- void setCellInfoListRate(int rateInMillis);
+ void setCellInfoListRate(int rateInMillis, int subId);
/**
* Opens a logical channel to the ICC card.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 8df3548465bd..1e798f3ed12e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -58,9 +58,7 @@ abstract class BaseTest @JvmOverloads constructor(
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
- test {
- testSpec.setIsTablet(wmHelper.currentState.wmState.isTablet)
- }
+ testSpec.setIsTablet(wmHelper.currentState.wmState.isTablet)
}
transition()
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt
index 1a40f82654ff..34544eab8e57 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt
@@ -49,21 +49,15 @@ class OpenActivityEmbeddingPlaceholderSplit(
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotationCheckEnabled(false)
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- }
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
}
transitions {
testApp.launchPlaceholderSplit(wmHelper)
}
teardown {
- test {
- tapl.goHome()
- testApp.exit(wmHelper)
- }
+ tapl.goHome()
+ testApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 55d412927ba6..1322a1cfed2a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -71,9 +71,7 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
get() = {
super.transition(this)
setup {
- test {
- tapl.setExpectedRotationCheckEnabled(false)
- }
+ tapl.setExpectedRotationCheckEnabled(false)
}
transitions {
// Can't use TAPL at the moment because of rotation test issues
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index cb197cdf04d3..f296d9746aef 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -36,18 +36,12 @@ abstract class CloseAppTransition(testSpec: FlickerTestParameter) : BaseTest(tes
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotation(testSpec.startRotation)
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.startRotation)
- }
+ tapl.setExpectedRotation(testSpec.startRotation)
+ testApp.launchViaIntent(wmHelper)
+ this.setRotation(testSpec.startRotation)
}
teardown {
- test {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 725c10a8ada0..479ac8ed55a3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -57,14 +57,10 @@ class CloseImeAutoOpenWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- }
+ testApp.launchViaIntent(wmHelper)
}
teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
testApp.closeIME(wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 8832686b43aa..07b52a6679bf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -57,17 +57,11 @@ class CloseImeAutoOpenWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTes
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotationCheckEnabled(false)
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- }
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
}
teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
tapl.goHome()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
index 71e0aa1f9628..fec7727b5731 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
@@ -46,13 +46,9 @@ class CloseImeEditorPopupDialogTest(testSpec: FlickerTestParameter) : BaseTest(t
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotationCheckEnabled(false)
- }
- eachRun {
- imeTestApp.launchViaIntent(wmHelper)
- imeTestApp.openIME(wmHelper)
- }
+ tapl.setExpectedRotationCheckEnabled(false)
+ imeTestApp.launchViaIntent(wmHelper)
+ imeTestApp.openIME(wmHelper)
}
transitions {
imeTestApp.dismissDialog(wmHelper)
@@ -61,13 +57,11 @@ class CloseImeEditorPopupDialogTest(testSpec: FlickerTestParameter) : BaseTest(t
.waitForAndVerify()
}
teardown {
- eachRun {
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .waitForAndVerify()
- imeTestApp.exit(wmHelper)
- }
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ imeTestApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 0f91fd58abb7..50596f1668eb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -50,17 +50,11 @@ class CloseImeWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest(testSpe
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- testApp.launchViaIntent(wmHelper)
- }
- eachRun {
- testApp.openIME(wmHelper)
- }
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(wmHelper)
}
teardown {
- test {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
testApp.closeIME(wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 007a4f1835d7..17eb8f9a005e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -49,13 +49,9 @@ class CloseImeWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTest(testSp
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotationCheckEnabled(false)
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(wmHelper)
- }
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(wmHelper)
}
transitions {
tapl.goHome()
@@ -65,9 +61,7 @@ class CloseImeWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTest(testSp
.waitForAndVerify()
}
teardown {
- test {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
index 216e0edaf451..f0776c10bd69 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -55,22 +55,18 @@ class LaunchAppShowImeAndDialogThemeAppTest(
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- wmHelper.StateSyncBuilder()
- .withImeShown()
- .waitForAndVerify()
- testApp.startDialogThemedActivity(wmHelper)
- // Verify IME insets isn't visible on dialog since it's non-IME focusable window
- assertFalse(testApp.getInsetsVisibleFromDialog(ime()))
- assertTrue(testApp.getInsetsVisibleFromDialog(statusBars()))
- assertTrue(testApp.getInsetsVisibleFromDialog(navigationBars()))
- }
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder()
+ .withImeShown()
+ .waitForAndVerify()
+ testApp.startDialogThemedActivity(wmHelper)
+ // Verify IME insets isn't visible on dialog since it's non-IME focusable window
+ assertFalse(testApp.getInsetsVisibleFromDialog(ime()))
+ assertTrue(testApp.getInsetsVisibleFromDialog(statusBars()))
+ assertTrue(testApp.getInsetsVisibleFromDialog(navigationBars()))
}
teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
testApp.dismissDialog(wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
index 868290ec7585..85e9e75776fb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
@@ -74,16 +74,12 @@ class LaunchAppShowImeOnStartTest(testSpec: FlickerTestParameter) : BaseTest(tes
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- eachRun {
- initializeApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.startRotation)
- }
+ initializeApp.launchViaIntent(wmHelper)
+ this.setRotation(testSpec.startRotation)
}
teardown {
- eachRun {
- initializeApp.exit(wmHelper)
- testApp.exit(wmHelper)
- }
+ initializeApp.exit(wmHelper)
+ testApp.exit(wmHelper)
}
transitions {
testApp.launchViaIntent(wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt
index 16c23b93a5de..d42a6c3d3d80 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt
@@ -52,19 +52,15 @@ class OpenImeWindowAndCloseTest(testSpec: FlickerTestParameter) : BaseTest(testS
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- eachRun {
- simpleApp.launchViaIntent(wmHelper)
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(wmHelper)
- }
+ simpleApp.launchViaIntent(wmHelper)
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(wmHelper)
}
transitions {
testApp.finishActivity(wmHelper)
}
teardown {
- test {
- simpleApp.exit(wmHelper)
- }
+ simpleApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
index e5874921ddfa..c41c65950e52 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
@@ -54,20 +54,16 @@ class OpenImeWindowFromFixedOrientationAppTest(
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotationCheckEnabled(false)
- }
- test {
- // Launch the activity with expecting IME will be shown.
- imeTestApp.launchViaIntent(wmHelper)
- }
- eachRun {
- // Swiping out the IME activity to home.
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .waitForAndVerify()
- }
+ tapl.setExpectedRotationCheckEnabled(false)
+
+ // Launch the activity with expecting IME will be shown.
+ imeTestApp.launchViaIntent(wmHelper)
+
+ // Swiping out the IME activity to home.
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
}
transitions {
// Bring the exist IME activity to the front in landscape mode device rotation.
@@ -75,9 +71,7 @@ class OpenImeWindowFromFixedOrientationAppTest(
imeTestApp.launchViaIntent(wmHelper)
}
teardown {
- test {
- imeTestApp.exit(wmHelper)
- }
+ imeTestApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index c1f17f3deb2e..84b0f607056c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -48,20 +48,14 @@ class OpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- testApp.launchViaIntent(wmHelper)
- }
+ testApp.launchViaIntent(wmHelper)
}
transitions {
testApp.openIME(wmHelper)
}
teardown {
- eachRun {
- testApp.closeIME(wmHelper)
- }
- test {
- testApp.exit(wmHelper)
- }
+ testApp.closeIME(wmHelper)
+ testApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
index 5fd94427dc82..1af88204c876 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
@@ -57,9 +57,7 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- eachRun {
- imeTestApp.launchViaIntent(wmHelper)
- }
+ imeTestApp.launchViaIntent(wmHelper)
}
transitions {
device.pressRecentApps()
@@ -69,13 +67,11 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes
builder.waitForAndVerify()
}
teardown {
- test {
- device.pressHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .waitForAndVerify()
- imeTestApp.exit(wmHelper)
- }
+ device.pressHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ imeTestApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 0281a60bbc3b..f7e5b239ec29 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -52,17 +52,13 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
testApp.launchViaIntent(wmHelper)
testApp.openIME(wmHelper)
- }
- eachRun {
this.setRotation(testSpec.startRotation)
device.pressRecentApps()
wmHelper.StateSyncBuilder()
.withRecentsActivityVisible()
.waitForAndVerify()
- }
}
transitions {
device.reopenAppFromOverview(wmHelper)
@@ -72,9 +68,7 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
.waitForAndVerify()
}
teardown {
- test {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 85bf6d752bf5..b75183d063a1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -64,33 +64,27 @@ open class SwitchImeWindowsFromGestureNavTest(
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotationCheckEnabled(false)
- }
- eachRun {
- this.setRotation(testSpec.startRotation)
- testApp.launchViaIntent(wmHelper)
- wmHelper.StateSyncBuilder()
- .withFullScreenApp(testApp)
- .waitForAndVerify()
-
- imeTestApp.launchViaIntent(wmHelper)
- wmHelper.StateSyncBuilder()
- .withFullScreenApp(imeTestApp)
- .waitForAndVerify()
-
- imeTestApp.openIME(wmHelper)
- }
+ tapl.setExpectedRotationCheckEnabled(false)
+ this.setRotation(testSpec.startRotation)
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder()
+ .withFullScreenApp(testApp)
+ .waitForAndVerify()
+
+ imeTestApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder()
+ .withFullScreenApp(imeTestApp)
+ .waitForAndVerify()
+
+ imeTestApp.openIME(wmHelper)
}
teardown {
- eachRun {
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .waitForAndVerify()
- testApp.exit(wmHelper)
- imeTestApp.exit(wmHelper)
- }
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ testApp.exit(wmHelper)
+ imeTestApp.exit(wmHelper)
}
transitions {
// [Step1]: Swipe right from imeTestApp to testApp task
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index eb9acc4b8e4e..08d7be2ac662 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -62,15 +62,11 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotation(testSpec.startRotation)
- testApp.launchViaIntent(wmHelper)
- }
+ tapl.setExpectedRotation(testSpec.startRotation)
+ testApp.launchViaIntent(wmHelper)
}
teardown {
- test {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
testApp.openSecondActivity(device, wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
index 3ff59e9d5716..5eba78c51059 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
@@ -52,21 +52,15 @@ open class OpenAppAfterCameraTest(
get() = {
super.transition(this)
setup {
- test{
- tapl.setExpectedRotationCheckEnabled(false)
- }
- eachRun {
- // 1. Open camera - cold -> close it first
- cameraApp.exit(wmHelper)
- cameraApp.launchViaIntent(wmHelper)
- // 2. Press home button (button nav mode) / swipe up to home (gesture nav mode)
- tapl.goHome()
- }
+ tapl.setExpectedRotationCheckEnabled(false)
+ // 1. Open camera - cold -> close it first
+ cameraApp.exit(wmHelper)
+ cameraApp.launchViaIntent(wmHelper)
+ // 2. Press home button (button nav mode) / swipe up to home (gesture nav mode)
+ tapl.goHome()
}
teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
testApp.launchViaIntent(wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index accf8afe14ea..354964d33ece 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -62,16 +62,12 @@ class OpenAppColdFromIcon(
get() = {
super.transition(this)
setup {
- eachRun {
- tapl.setExpectedRotation(Surface.ROTATION_0)
- RemoveAllTasksButHomeRule.removeAllTasksButHome()
- this.setRotation(testSpec.startRotation)
- }
+ tapl.setExpectedRotation(Surface.ROTATION_0)
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ this.setRotation(testSpec.startRotation)
}
teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
tapl.goHome()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index cd01f7467c17..2c776681b054 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -64,15 +64,11 @@ open class OpenAppColdTest(
get() = {
super.transition(this)
setup {
- eachRun {
- removeAllTasksButHome()
- this.setRotation(testSpec.startRotation)
- }
+ removeAllTasksButHome()
+ this.setRotation(testSpec.startRotation)
}
teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
testApp.launchViaIntent(wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
index b3db5b70fafa..b70bdd730a5a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
@@ -64,12 +64,10 @@ open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter) :
// Needs to run at the end of the setup, so after the setup defined in super.transition
setup {
- eachRun {
- device.sleep()
- wmHelper.StateSyncBuilder()
- .withoutTopVisibleAppWindows()
- .waitForAndVerify()
- }
+ device.sleep()
+ wmHelper.StateSyncBuilder()
+ .withoutTopVisibleAppWindows()
+ .waitForAndVerify()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
index 8c1d244b69e0..48602c43c6f2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
@@ -68,12 +68,10 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter) :
// Needs to run at the end of the setup, so after the setup defined in super.transition
setup {
- eachRun {
- device.sleep()
- wmHelper.StateSyncBuilder()
- .withoutTopVisibleAppWindows()
- .waitForAndVerify()
- }
+ device.sleep()
+ wmHelper.StateSyncBuilder()
+ .withoutTopVisibleAppWindows()
+ .waitForAndVerify()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
index caf2e2dbadc6..83350ea9ce74 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
@@ -59,26 +59,22 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet
super.transition(this)
setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
+ device.wakeUpAndGoToHomeScreen()
- // Launch an activity that is shown when the device is locked
- showWhenLockedApp.launchViaIntent(wmHelper)
- wmHelper.StateSyncBuilder()
- .withFullScreenApp(showWhenLockedApp)
- .waitForAndVerify()
+ // Launch an activity that is shown when the device is locked
+ showWhenLockedApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder()
+ .withFullScreenApp(showWhenLockedApp)
+ .waitForAndVerify()
- device.sleep()
- wmHelper.StateSyncBuilder()
- .withoutTopVisibleAppWindows()
- .waitForAndVerify()
- }
+ device.sleep()
+ wmHelper.StateSyncBuilder()
+ .withoutTopVisibleAppWindows()
+ .waitForAndVerify()
}
teardown {
- test {
- showWhenLockedApp.exit(wmHelper)
- }
+ showWhenLockedApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
index ecc60b8d45c0..f574c9e12c96 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
@@ -39,17 +39,13 @@ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter) :
override val transition: FlickerBuilder.() -> Unit = {
super.transition(this)
setup {
- eachRun {
- device.sleep()
- wmHelper.StateSyncBuilder()
- .withoutTopVisibleAppWindows()
- .waitForAndVerify()
- }
+ device.sleep()
+ wmHelper.StateSyncBuilder()
+ .withoutTopVisibleAppWindows()
+ .waitForAndVerify()
}
teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
testApp.launchViaIntent(wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
index e744d44bc542..24f2ae961b43 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
@@ -51,13 +51,11 @@ open class OpenAppFromNotificationCold(
super.transition(this)
setup {
- eachRun {
- // Close the app that posted the notification to trigger a cold start next time
- // it is open - can't just kill it because that would remove the notification.
- tapl.goHome()
- tapl.workspace.switchToOverview()
- tapl.overview.dismissAllTasks()
- }
+ // Close the app that posted the notification to trigger a cold start next time
+ // it is open - can't just kill it because that would remove the notification.
+ tapl.goHome()
+ tapl.workspace.switchToOverview()
+ tapl.overview.dismissAllTasks()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index 4ea42433e054..77f28f60d2cc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -67,21 +67,17 @@ open class OpenAppFromNotificationWarm(
override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(testSpec.startRotation)
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- wmHelper.StateSyncBuilder()
- .withFullScreenApp(testApp)
- .waitForAndVerify()
- testApp.postNotification(wmHelper)
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .waitForAndVerify()
- }
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(testSpec.startRotation)
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder()
+ .withFullScreenApp(testApp)
+ .waitForAndVerify()
+ testApp.postNotification(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
}
transitions {
@@ -125,9 +121,7 @@ open class OpenAppFromNotificationWarm(
}
teardown {
- test {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index a3dd0cbcf64f..bc86cdfd6b1b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -69,27 +69,23 @@ open class OpenAppFromOverviewTest(
get() = {
super.transition(this)
setup {
- test {
- tapl.setExpectedRotationCheckEnabled(false)
- testApp.launchViaIntent(wmHelper)
- }
- eachRun {
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .waitForAndVerify()
- // By default, launcher doesn't rotate on phones, but rotates on tablets
- if (testSpec.isTablet) {
- tapl.setExpectedRotation(testSpec.startRotation)
- } else {
- tapl.setExpectedRotation(Surface.ROTATION_0)
- }
- tapl.workspace.switchToOverview()
- wmHelper.StateSyncBuilder()
- .withRecentsActivityVisible()
- .waitForAndVerify()
- this.setRotation(testSpec.startRotation)
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ // By default, launcher doesn't rotate on phones, but rotates on tablets
+ if (testSpec.isTablet) {
+ tapl.setExpectedRotation(testSpec.startRotation)
+ } else {
+ tapl.setExpectedRotation(Surface.ROTATION_0)
}
+ tapl.workspace.switchToOverview()
+ wmHelper.StateSyncBuilder()
+ .withRecentsActivityVisible()
+ .waitForAndVerify()
+ this.setRotation(testSpec.startRotation)
}
transitions {
tapl.overview.currentTask.open()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 8658c03acf60..face7da6f623 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -36,16 +36,12 @@ abstract class OpenAppTransition(testSpec: FlickerTestParameter) : BaseTest(test
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotation(testSpec.startRotation)
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(testSpec.startRotation)
- }
+ tapl.setExpectedRotation(testSpec.startRotation)
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(testSpec.startRotation)
}
teardown {
- test {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 4f69f01ce39b..8077398073da 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -65,22 +65,16 @@ open class OpenAppWarmTest(testSpec: FlickerTestParameter) :
get() = {
super.transition(this)
setup {
- test {
- tapl.setExpectedRotationCheckEnabled(false)
- testApp.launchViaIntent(wmHelper)
- }
- eachRun {
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .waitForAndVerify()
- this.setRotation(testSpec.startRotation)
- }
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ this.setRotation(testSpec.startRotation)
}
teardown {
- test {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
testApp.launchViaIntent(wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 5f342a00bf8c..26f46cd073f1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -65,14 +65,10 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- }
+ testApp.launchViaIntent(wmHelper)
}
teardown {
- test {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
testApp.openNewTask(device, wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index f85bad33c4fd..a1df1df8c55c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
@@ -70,15 +69,11 @@ open class QuickSwitchBetweenTwoAppsBackTest(
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotation(testSpec.startRotation)
- }
- eachRun {
- testApp1.launchViaIntent(wmHelper)
- testApp2.launchViaIntent(wmHelper)
- startDisplayBounds = wmHelper.currentState.layerState
- .physicalDisplayBounds ?: error("Display not found")
- }
+ tapl.setExpectedRotation(testSpec.startRotation)
+ testApp1.launchViaIntent(wmHelper)
+ testApp2.launchViaIntent(wmHelper)
+ startDisplayBounds = wmHelper.currentState.layerState
+ .physicalDisplayBounds ?: error("Display not found")
}
transitions {
tapl.launchedAppState.quickSwitchToPreviousApp()
@@ -90,10 +85,8 @@ open class QuickSwitchBetweenTwoAppsBackTest(
}
teardown {
- test {
- testApp1.exit(wmHelper)
- testApp2.exit(wmHelper)
- }
+ testApp1.exit(wmHelper)
+ testApp2.exit(wmHelper)
}
}
@@ -266,7 +259,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(
}
/** {@inheritDoc} */
- @Postsubmit
+ @Presubmit
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
index 461bae482299..49dcbcf10e15 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -57,29 +56,6 @@ open class QuickSwitchBetweenTwoAppsBackTest_ShellTransit(
Assume.assumeTrue(isShellTransitionsEnabled)
}
- @FlakyTest(bugId = 228009808)
- @Test
- override fun app1LayerIsVisibleOnceApp2LayerIsInvisible() =
- super.app1LayerIsVisibleOnceApp2LayerIsInvisible()
-
- @FlakyTest(bugId = 228009808)
- @Test
- override fun app1WindowBecomesAndStaysVisible() = super.app1WindowBecomesAndStaysVisible()
-
- @FlakyTest(bugId = 228009808)
- @Test
- override fun endsWithApp1BeingOnTop() = super.endsWithApp1BeingOnTop()
-
- @FlakyTest(bugId = 239147075)
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest(bugId = 239147075)
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
/** {@inheritDoc} */
@Ignore("Nav bar window becomes invisible during quick switch")
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index f6392cab4a35..5ab9f1435b08 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
@@ -71,10 +69,8 @@ open class QuickSwitchBetweenTwoAppsForwardTest(
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
tapl.setExpectedRotation(testSpec.startRotation)
- }
- eachRun {
+
testApp1.launchViaIntent(wmHelper)
testApp2.launchViaIntent(wmHelper)
tapl.launchedAppState.quickSwitchToPreviousApp()
@@ -85,7 +81,6 @@ open class QuickSwitchBetweenTwoAppsForwardTest(
.waitForAndVerify()
startDisplayBounds = wmHelper.currentState.layerState
.physicalDisplayBounds ?: error("Display not found")
- }
}
transitions {
tapl.launchedAppState.quickSwitchToPreviousAppSwipeLeft()
@@ -97,10 +92,8 @@ open class QuickSwitchBetweenTwoAppsForwardTest(
}
teardown {
- test {
- testApp1.exit(wmHelper)
- testApp2.exit(wmHelper)
- }
+ testApp1.exit(wmHelper)
+ testApp2.exit(wmHelper)
}
}
@@ -274,22 +267,10 @@ open class QuickSwitchBetweenTwoAppsForwardTest(
}
/** {@inheritDoc} */
- @Postsubmit
+ @Presubmit
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
- /** {@inheritDoc} */
- @FlakyTest(bugId = 239148258)
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- /** {@inheritDoc} */
- @FlakyTest(bugId = 239148258)
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
companion object {
private var startDisplayBounds = Rect.EMPTY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
index f644b97e03df..7c7be89f5108 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -57,19 +56,6 @@ open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit(
Assume.assumeTrue(isShellTransitionsEnabled)
}
- @FlakyTest(bugId = 228009808)
- @Test
- override fun app2LayerIsVisibleOnceApp1LayerIsInvisible() =
- super.app2LayerIsVisibleOnceApp1LayerIsInvisible()
-
- @FlakyTest(bugId = 228009808)
- @Test
- override fun app2WindowBecomesAndStaysVisible() = super.app2WindowBecomesAndStaysVisible()
-
- @FlakyTest(bugId = 228009808)
- @Test
- override fun endsWithApp2BeingOnTop() = super.endsWithApp2BeingOnTop()
-
/** {@inheritDoc} */
@Ignore("Nav bar window becomes invisible during quick switch")
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index a714111fae21..00e60234ba77 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
@@ -62,24 +60,19 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- tapl.setExpectedRotationCheckEnabled(false)
- }
- test {
- tapl.setExpectedRotation(testSpec.startRotation)
- }
+ tapl.setExpectedRotationCheckEnabled(false)
- eachRun {
- testApp.launchViaIntent(wmHelper)
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .withWindowSurfaceDisappeared(testApp)
- .waitForAndVerify()
+ tapl.setExpectedRotation(testSpec.startRotation)
- startDisplayBounds = wmHelper.currentState.layerState
- .physicalDisplayBounds ?: error("Display not found")
- }
+ testApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .withWindowSurfaceDisappeared(testApp)
+ .waitForAndVerify()
+
+ startDisplayBounds = wmHelper.currentState.layerState
+ .physicalDisplayBounds ?: error("Display not found")
}
transitions {
tapl.workspace.quickSwitchToPreviousApp()
@@ -89,11 +82,8 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes
.withStatusBarVisible()
.waitForAndVerify()
}
-
teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
}
@@ -284,22 +274,11 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes
}
/** {@inheritDoc} */
- @Postsubmit
+ @Presubmit
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
- @FlakyTest(bugId = 239148258)
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest(bugId = 239148258)
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- /** {@inheritDoc} */
@Ignore("Nav bar window becomes invisible during quick switch")
@Test
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index e6c1eaca9380..d09507f9cd21 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -80,9 +80,7 @@ class ChangeAppRotationTest(
get() = {
super.transition(this)
setup {
- test {
- testApp.launchViaIntent(wmHelper)
- }
+ testApp.launchViaIntent(wmHelper)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 7e159d465b13..afe2ea678e39 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -34,14 +34,10 @@ abstract class RotationTransition(testSpec: FlickerTestParameter) : BaseTest(tes
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
- eachRun {
- this.setRotation(testSpec.startRotation)
- }
+ this.setRotation(testSpec.startRotation)
}
teardown {
- test {
- testApp.exit(wmHelper)
- }
+ testApp.exit(wmHelper)
}
transitions {
this.setRotation(testSpec.endRotation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 07c213034642..16ad6309af40 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -87,15 +87,13 @@ open class SeamlessAppRotationTest(
get() = {
super.transition(this)
setup {
- test {
- testApp.launchViaIntent(
- wmHelper,
- stringExtras = mapOf(
- ActivityOptions.EXTRA_STARVE_UI_THREAD
- to testSpec.starveUiThread.toString()
- )
+ testApp.launchViaIntent(
+ wmHelper,
+ stringExtras = mapOf(
+ ActivityOptions.EXTRA_STARVE_UI_THREAD
+ to testSpec.starveUiThread.toString()
)
- }
+ )
}
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 61aadaacdfa5..54662ef2d0b5 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -202,5 +202,17 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".GameActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
+ android:immersive="true"
+ android:theme="@android:style/Theme.NoTitleBar"
+ android:configChanges="screenSize"
+ android:label="GameApp"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml
new file mode 100644
index 000000000000..0b4693dec6e1
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/holo_orange_light">
+
+ <SurfaceView
+ android:id="@+id/surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 19fafb7d2e95..72a02f24629f 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -100,4 +100,9 @@ public class ActivityOptions {
ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME = new ComponentName(
FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity");
+
+ public static final String GAME_ACTIVITY_LAUNCHER_NAME = "GameApp";
+ public static final ComponentName GAME_ACTIVITY_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".GameActivity");
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java
new file mode 100644
index 000000000000..ef75d4ddcdcd
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+import androidx.core.view.WindowInsetsControllerCompat;
+
+public class GameActivity extends Activity implements SurfaceHolder.Callback {
+ private SurfaceHolder mSurfaceHolder;
+ private SurfaceView mSurfaceView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_surfaceview);
+
+ mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
+ mSurfaceView.setZOrderOnTop(true);
+ mSurfaceHolder = mSurfaceView.getHolder();
+ mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
+ mSurfaceHolder.addCallback(this);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ hideSystemBars();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(Color.BLUE);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+
+ private void hideSystemBars() {
+ WindowInsetsControllerCompat windowInsetsController =
+ ViewCompat.getWindowInsetsController(getWindow().getDecorView());
+ if (windowInsetsController == null) {
+ return;
+ }
+ // Configure the behavior of the hidden system bars.
+ windowInsetsController.setSystemBarsBehavior(
+ WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ );
+ // Hide both the status bar and the navigation bar.
+ windowInsetsController.hide(WindowInsetsCompat.Type.systemBars());
+ }
+}
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index 836d406d903c..06a96dfb8176 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -69,13 +69,25 @@ public class InputDeviceTest {
}
private void assertInputDeviceParcelUnparcel(KeyCharacterMap keyCharacterMap) {
- final InputDevice device =
- new InputDevice(DEVICE_ID, 0 /* generation */, 0 /* controllerNumber */, "name",
- 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
- 0 /* sources */, 0 /* keyboardType */, keyCharacterMap,
- InputDeviceCountryCode.INTERNATIONAL, false /* hasVibrator */,
- false /* hasMicrophone */, false /* hasButtonUnderpad */,
- true /* hasSensor */, false /* hasBattery */);
+ final InputDevice device = new InputDevice.Builder()
+ .setId(DEVICE_ID)
+ .setGeneration(42)
+ .setControllerNumber(43)
+ .setName("Test Device " + DEVICE_ID)
+ .setVendorId(44)
+ .setProductId(45)
+ .setDescriptor("descriptor")
+ .setExternal(true)
+ .setSources(InputDevice.SOURCE_HDMI)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC)
+ .setKeyCharacterMap(keyCharacterMap)
+ .setHasVibrator(true)
+ .setHasMicrophone(true)
+ .setHasButtonUnderPad(true)
+ .setHasSensor(true)
+ .setHasBattery(true)
+ .setCountryCode(InputDeviceCountryCode.INTERNATIONAL)
+ .build();
Parcel parcel = Parcel.obtain();
device.writeToParcel(parcel, 0);