summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp6
-rw-r--r--Android.bp3
-rw-r--r--Ravenwood.bp82
-rw-r--r--core/api/current.txt4
-rw-r--r--core/api/system-current.txt34
-rw-r--r--core/java/android/accounts/AccountManager.java2
-rw-r--r--core/java/android/app/ApplicationPackageManager.java2
-rw-r--r--core/java/android/app/IActivityManager.aidl9
-rw-r--r--core/java/android/app/compat/ChangeIdStateCache.java2
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java177
-rw-r--r--core/java/android/app/servertransaction/ClientTransactionListenerController.java5
-rw-r--r--core/java/android/app/servertransaction/RefreshCallbackItem.java4
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java59
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutorHelper.java23
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java10
-rw-r--r--core/java/android/companion/virtual/IVirtualDevice.aidl14
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceInternal.java15
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java19
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig8
-rw-r--r--core/java/android/content/Intent.java2
-rw-r--r--core/java/android/content/pm/ActivityInfo.java17
-rw-r--r--core/java/android/content/pm/PackageManager.java4
-rw-r--r--core/java/android/credentials/selection/IntentFactory.java12
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java10
-rw-r--r--core/java/android/hardware/input/InputManager.java10
-rw-r--r--core/java/android/hardware/input/VirtualRotaryEncoder.java62
-rw-r--r--core/java/android/hardware/input/VirtualRotaryEncoderConfig.aidl19
-rw-r--r--core/java/android/hardware/input/VirtualRotaryEncoderConfig.java81
-rw-r--r--core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.aidl19
-rw-r--r--core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java145
-rw-r--r--core/java/android/os/ServiceManagerNative.java4
-rw-r--r--core/java/android/os/SharedMemory.java18
-rw-r--r--core/java/android/os/flags.aconfig7
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/security/flags.aconfig8
-rw-r--r--core/java/android/service/chooser/flags.aconfig8
-rw-r--r--core/java/android/service/notification/Adjustment.java9
-rw-r--r--core/java/android/util/SequenceUtils.java63
-rw-r--r--core/java/android/view/EventLogTags.logtags3
-rw-r--r--core/java/android/view/InputMonitor.java3
-rw-r--r--core/java/android/view/InsetsAnimationThreadControlRunner.java2
-rw-r--r--core/java/android/view/InsetsSourceControl.java16
-rw-r--r--core/java/android/view/InsetsState.java17
-rw-r--r--core/java/android/view/SurfaceControl.java9
-rw-r--r--core/java/android/view/SurfaceView.java44
-rw-r--r--core/java/android/view/View.java9
-rw-r--r--core/java/android/view/ViewRootImpl.java88
-rw-r--r--core/java/android/view/WindowManager.java21
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java15
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl2
-rw-r--r--core/java/android/widget/RemoteViews.java75
-rw-r--r--core/java/android/widget/RemoteViewsService.java18
-rw-r--r--core/java/android/window/ClientWindowFrames.java9
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig18
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java29
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java30
-rw-r--r--core/java/com/android/internal/accessibility/util/ShortcutUtils.java21
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetService.aidl1
-rw-r--r--core/java/com/android/internal/view/SurfaceCallbackHelper.java18
-rw-r--r--core/java/com/android/internal/widget/IRemoteViewsFactory.aidl2
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_database_SQLiteConnection.cpp7
-rw-r--r--core/jni/android_os_ServiceManagerNative.cpp42
-rw-r--r--core/res/AndroidManifest.xml13
-rw-r--r--core/res/res/values/attrs.xml2
-rw-r--r--core/res/res/values/colors.xml4
-rw-r--r--core/res/res/values/strings.xml11
-rw-r--r--core/res/res/values/symbols.xml12
-rw-r--r--core/res/res/values/themes_device_defaults.xml102
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java18
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java22
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java19
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java4
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java22
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java73
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java53
-rw-r--r--core/tests/coretests/src/android/content/ContentResolverTest.java2
-rw-r--r--core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java99
-rw-r--r--core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java1
-rw-r--r--core/tests/coretests/src/android/os/MessageQueueTest.java2
-rw-r--r--core/tests/coretests/src/android/util/SequenceUtilsTest.java89
-rw-r--r--core/tests/coretests/src/android/util/SparseSetArrayTest.java3
-rw-r--r--core/tests/coretests/src/android/view/ViewFrameRateTest.java49
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java33
-rw-r--r--core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java3
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java25
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetHelperTest.java74
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java52
-rw-r--r--core/tests/utiltests/src/android/util/TimeUtilsTest.java33
-rw-r--r--graphics/java/android/graphics/SurfaceTexture.java3
-rw-r--r--graphics/java/android/graphics/drawable/Icon.java41
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp61
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifest.xml35
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml25
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/AndroidTest.xml35
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.pngbin0 -> 56102 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.pngbin0 -> 56102 bytes
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties2
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt58
-rw-r--r--libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/testing/goldenpathmanager/WMShellGoldenPathManager.kt53
l---------libs/WindowManager/Shell/multivalentScreenshotTestsForDevice1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java46
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt180
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt48
-rw-r--r--location/Android.bp3
-rw-r--r--location/java/android/location/flags/location.aconfig7
-rw-r--r--packages/CtsShim/Android.bp2
-rw-r--r--packages/CtsShim/build/Android.bp2
-rw-r--r--packages/CtsShim/build/jni/Android.bp1
-rw-r--r--packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java2
-rw-r--r--packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java14
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SystemUI/OWNERS4
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig27
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt27
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt132
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt221
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt59
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt40
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt192
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt4
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt18
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt11
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt24
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt57
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt103
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt97
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt58
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt43
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt)4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt45
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt177
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt44
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt219
-rw-r--r--packages/SystemUI/res/drawable/face_dialog_dark_to_checkmark.xml637
-rw-r--r--packages/SystemUI/res/drawable/face_dialog_dark_to_error.xml473
-rw-r--r--packages/SystemUI/res/drawable/face_dialog_error_to_idle.xml509
-rw-r--r--packages/SystemUI/res/drawable/face_dialog_idle_static.xml276
-rw-r--r--packages/SystemUI/res/drawable/face_dialog_pulse_dark_to_light.xml183
-rw-r--r--packages/SystemUI/res/drawable/face_dialog_pulse_light_to_dark.xml183
-rw-r--r--packages/SystemUI/res/drawable/face_dialog_wink_from_dark.xml208
-rw-r--r--packages/SystemUI/res/layout/ongoing_activity_chip.xml2
-rw-r--r--packages/SystemUI/res/raw/face_dialog_authenticating.json2
-rw-r--r--packages/SystemUI/res/raw/face_dialog_dark_to_checkmark.json1
-rw-r--r--packages/SystemUI/res/raw/face_dialog_dark_to_error.json1
-rw-r--r--packages/SystemUI/res/raw/face_dialog_error_to_idle.json1
-rw-r--r--packages/SystemUI/res/raw/face_dialog_idle_static.json1
-rw-r--r--packages/SystemUI/res/raw/face_dialog_wink_from_dark.json1
-rw-r--r--packages/SystemUI/res/values/strings.xml20
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt41
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java612
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java641
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt228
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt (renamed from packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperRepository.kt)24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperState.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt177
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandHeadsUpOnInlineReply.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt59
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt1219
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt102
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt140
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java128
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java183
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java27
-rw-r--r--packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt85
-rw-r--r--packages/SystemUI/tests/utils/src/android/hardware/input/InputManagerKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt57
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/DismissCallbackRegistryKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt15
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt2
-rw-r--r--ravenwood/Android.bp74
-rw-r--r--services/accessibility/accessibility.aconfig10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java38
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java312
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java46
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java5
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java45
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java35
-rw-r--r--services/core/Android.bp3
-rw-r--r--services/core/java/com/android/server/SystemConfig.java2
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java18
-rw-r--r--services/core/java/com/android/server/am/AppStartInfoTracker.java64
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java6
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java70
-rw-r--r--services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java64
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java21
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig16
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java2
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java9
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java20
-rw-r--r--services/core/java/com/android/server/audio/SoundDoseHelper.java46
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java23
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java2
-rw-r--r--services/core/java/com/android/server/display/BrightnessSetting.java19
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java16
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java4
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java19
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java10
-rw-r--r--services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java3
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java56
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java27
-rw-r--r--services/core/java/com/android/server/inputmethod/UserDataRepository.java10
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java2
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java118
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java52
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java7
-rw-r--r--services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java254
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java32
-rw-r--r--services/core/java/com/android/server/pm/ReconcilePackageUtils.java3
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java5
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java26
-rw-r--r--services/core/java/com/android/server/power/Notifier.java162
-rw-r--r--services/core/java/com/android/server/power/feature/PowerManagerFlags.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java15
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/AppCompatController.java10
-rw-r--r--services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java (renamed from services/core/java/com/android/server/wm/AppCompatOrientationCapability.java)33
-rw-r--r--services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java16
-rw-r--r--services/core/java/com/android/server/wm/AppCompatOverrides.java (renamed from services/core/java/com/android/server/wm/AppCompatCapability.java)36
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java3
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java5
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java70
-rw-r--r--services/core/java/com/android/server/wm/ClientLifecycleManager.java51
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java3
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java47
-rw-r--r--services/core/java/com/android/server/wm/RemoteDisplayChangeController.java8
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java17
-rw-r--r--services/core/java/com/android/server/wm/Task.java35
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java29
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java16
-rw-r--r--services/core/java/com/android/server/wm/Transition.java25
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java62
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java47
-rw-r--r--services/core/jni/com_android_server_companion_virtual_InputController.cpp23
-rw-r--r--services/fakes/Android.bp2
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java12
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java86
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java27
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java179
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java138
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java126
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java134
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java7
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java382
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java60
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java64
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationCapabilityTest.java433
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java329
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java260
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java84
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatTransparentActivityRobot.java139
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java41
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java25
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java583
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java55
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/utils/CurrentTimeMillisSupplierFake.java43
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java19
-rw-r--r--test-mock/Android.bp4
-rw-r--r--tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt4
393 files changed, 10844 insertions, 7899 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 96d6f32fc1df..5e9af6adfb88 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -405,6 +405,12 @@ cc_aconfig_library {
aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
}
+cc_aconfig_library {
+ name: "android.companion.virtualdevice.flags-aconfig-cc-host",
+ aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
+ host_supported: true,
+}
+
java_aconfig_library {
name: "android.companion.virtualdevice.flags-aconfig-java",
aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index fb1fa3b58f74..f0aa62cc37ae 100644
--- a/Android.bp
+++ b/Android.bp
@@ -511,6 +511,9 @@ java_library {
},
lint: {
baseline_filename: "lint-baseline.xml",
+ warning_checks: [
+ "FlaggedApi",
+ ],
},
jarjar_prefix: "com.android.internal.hidden_from_bootclasspath",
}
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 11da20aa6e02..159c17e13106 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -215,111 +215,37 @@ genrule {
java_library {
name: "services.core.ravenwood-jarjar",
+ defaults: ["ravenwood-internal-only-visibility-java"],
installable: false,
static_libs: [
"services.core.ravenwood",
],
jarjar_rules: ":ravenwood-services-jarjar-rules",
- visibility: ["//visibility:private"],
-}
-
-java_library {
- name: "services.fakes.ravenwood-jarjar",
- installable: false,
- srcs: [":services.fakes-sources"],
- libs: [
- "ravenwood-framework",
- "services.core.ravenwood",
- ],
- jarjar_rules: ":ravenwood-services-jarjar-rules",
- visibility: ["//visibility:private"],
-}
-
-java_library {
- name: "mockito-ravenwood-prebuilt",
- installable: false,
- static_libs: [
- "mockito-robolectric-prebuilt",
- ],
-}
-
-java_library {
- name: "inline-mockito-ravenwood-prebuilt",
- installable: false,
- static_libs: [
- "inline-mockito-robolectric-prebuilt",
- ],
}
// Jars in "ravenwood-runtime" are set to the classpath, sorted alphabetically.
// Rename some of the dependencies to make sure they're included in the intended order.
java_genrule {
name: "100-framework-minus-apex.ravenwood",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
cmd: "cp $(in) $(out)",
srcs: [":framework-minus-apex.ravenwood"],
out: ["100-framework-minus-apex.ravenwood.jar"],
- visibility: ["//visibility:private"],
}
java_genrule {
// Use 200 to make sure it comes before the mainline stub ("all-updatable...").
name: "200-kxml2-android",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
cmd: "cp $(in) $(out)",
srcs: [":kxml2-android"],
out: ["200-kxml2-android.jar"],
- visibility: ["//visibility:private"],
}
java_genrule {
name: "z00-all-updatable-modules-system-stubs",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
cmd: "cp $(in) $(out)",
srcs: [":all-updatable-modules-system-stubs"],
out: ["z00-all-updatable-modules-system-stubs.jar"],
- visibility: ["//visibility:private"],
-}
-
-android_ravenwood_libgroup {
- name: "ravenwood-runtime",
- libs: [
- "100-framework-minus-apex.ravenwood",
- "200-kxml2-android",
-
- "ravenwood-runtime-common-ravenwood",
-
- "android.test.mock.ravenwood",
- "ravenwood-helper-runtime",
- "hoststubgen-helper-runtime.ravenwood",
- "services.core.ravenwood-jarjar",
- "services.fakes.ravenwood-jarjar",
-
- // Provide runtime versions of utils linked in below
- "junit",
- "truth",
- "flag-junit",
- "ravenwood-framework",
- "ravenwood-junit-impl",
- "ravenwood-junit-impl-flag",
- "mockito-ravenwood-prebuilt",
- "inline-mockito-ravenwood-prebuilt",
-
- // It's a stub, so it should be towards the end.
- "z00-all-updatable-modules-system-stubs",
- ],
- jni_libs: [
- "libandroid_runtime",
- "libravenwood_runtime",
- ],
-}
-
-android_ravenwood_libgroup {
- name: "ravenwood-utils",
- libs: [
- "junit",
- "truth",
- "flag-junit",
- "ravenwood-framework",
- "ravenwood-junit",
- "mockito-ravenwood-prebuilt",
- "inline-mockito-ravenwood-prebuilt",
- ],
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 69ead8f85623..44a6c6b2803b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11336,7 +11336,7 @@ package android.content {
field public static final String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
field public static final String CATEGORY_VOICE = "android.intent.category.VOICE";
field public static final String CATEGORY_VR_HOME = "android.intent.category.VR_HOME";
- field @FlaggedApi("android.service.chooser.chooser_album_text") public static final int CHOOSER_CONTENT_TYPE_ALBUM = 1; // 0x1
+ field public static final int CHOOSER_CONTENT_TYPE_ALBUM = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.content.Intent> CREATOR;
field public static final String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
field public static final String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE";
@@ -11359,7 +11359,7 @@ package android.content {
field public static final String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
field public static final String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
field @FlaggedApi("android.service.chooser.chooser_payload_toggling") public static final String EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI = "android.intent.extra.CHOOSER_ADDITIONAL_CONTENT_URI";
- field @FlaggedApi("android.service.chooser.chooser_album_text") public static final String EXTRA_CHOOSER_CONTENT_TYPE_HINT = "android.intent.extra.CHOOSER_CONTENT_TYPE_HINT";
+ field public static final String EXTRA_CHOOSER_CONTENT_TYPE_HINT = "android.intent.extra.CHOOSER_CONTENT_TYPE_HINT";
field public static final String EXTRA_CHOOSER_CUSTOM_ACTIONS = "android.intent.extra.CHOOSER_CUSTOM_ACTIONS";
field @FlaggedApi("android.service.chooser.chooser_payload_toggling") public static final String EXTRA_CHOOSER_FOCUSED_ITEM_POSITION = "android.intent.extra.CHOOSER_FOCUSED_ITEM_POSITION";
field public static final String EXTRA_CHOOSER_MODIFY_SHARE_ACTION = "android.intent.extra.CHOOSER_MODIFY_SHARE_ACTION";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9e2872f8f6cc..b3c471c461be 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -105,6 +105,7 @@ package android {
field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
field @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") public static final String CAMERA_PRIVACY_ALLOWLIST = "android.permission.CAMERA_PRIVACY_ALLOWLIST";
field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
+ field @FlaggedApi("android.os.allow_consentless_bugreport_delegated_consent") public static final String CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT = "android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT";
field public static final String CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD";
field public static final String CAPTURE_MEDIA_OUTPUT = "android.permission.CAPTURE_MEDIA_OUTPUT";
field public static final String CAPTURE_TUNER_AUDIO_INPUT = "android.permission.CAPTURE_TUNER_AUDIO_INPUT";
@@ -3475,6 +3476,7 @@ package android.companion.virtual {
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.input.VirtualMouseConfig);
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualNavigationTouchpad createVirtualNavigationTouchpad(@NonNull android.hardware.input.VirtualNavigationTouchpadConfig);
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull android.hardware.input.VirtualRotaryEncoderConfig);
method @FlaggedApi("android.companion.virtual.flags.virtual_stylus") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig);
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
@@ -5761,6 +5763,37 @@ package android.hardware.input {
method @NonNull public android.hardware.input.VirtualNavigationTouchpadConfig build();
}
+ @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") public class VirtualRotaryEncoder implements java.io.Closeable {
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull android.hardware.input.VirtualRotaryEncoderScrollEvent);
+ }
+
+ @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") public final class VirtualRotaryEncoderConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualRotaryEncoderConfig> CREATOR;
+ }
+
+ public static final class VirtualRotaryEncoderConfig.Builder extends android.hardware.input.VirtualInputDeviceConfig.Builder<android.hardware.input.VirtualRotaryEncoderConfig.Builder> {
+ ctor public VirtualRotaryEncoderConfig.Builder();
+ method @NonNull public android.hardware.input.VirtualRotaryEncoderConfig build();
+ }
+
+ @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") public final class VirtualRotaryEncoderScrollEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getEventTimeNanos();
+ method @FloatRange(from=-1.0F, to=1.0f) public float getScrollAmount();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualRotaryEncoderScrollEvent> CREATOR;
+ }
+
+ public static final class VirtualRotaryEncoderScrollEvent.Builder {
+ ctor public VirtualRotaryEncoderScrollEvent.Builder();
+ method @NonNull public android.hardware.input.VirtualRotaryEncoderScrollEvent build();
+ method @NonNull public android.hardware.input.VirtualRotaryEncoderScrollEvent.Builder setEventTimeNanos(long);
+ method @NonNull public android.hardware.input.VirtualRotaryEncoderScrollEvent.Builder setScrollAmount(@FloatRange(from=-1.0F, to=1.0f) float);
+ }
+
@FlaggedApi("android.companion.virtual.flags.virtual_stylus") public class VirtualStylus implements java.io.Closeable {
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull android.hardware.input.VirtualStylusButtonEvent);
@@ -12906,7 +12939,6 @@ package android.service.notification {
field @FlaggedApi("android.service.notification.notification_classification") public static final int TYPE_OTHER = 0; // 0x0
field @FlaggedApi("android.service.notification.notification_classification") public static final int TYPE_PROMOTION = 1; // 0x1
field @FlaggedApi("android.service.notification.notification_classification") public static final int TYPE_SOCIAL_MEDIA = 2; // 0x2
- field @FlaggedApi("android.service.notification.notification_classification") public static final int TYPE_UNKNOWN = -1; // 0xffffffff
}
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 497d47adc7cc..87acbbf65b2f 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -385,7 +385,7 @@ public class AccountManager {
/**
* @hide
*/
- public static final int CACHE_USER_DATA_SIZE = 4;
+ public static final int CACHE_USER_DATA_SIZE = 32;
private static final class AccountKeyData {
final public Account account;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5956e2bde242..80764afd3644 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1128,7 +1128,7 @@ public class ApplicationPackageManager extends PackageManager {
private static final PropertyInvalidatedCache<Integer, GetPackagesForUidResult>
mGetPackagesForUidCache =
new PropertyInvalidatedCache<Integer, GetPackagesForUidResult>(
- 32, CACHE_KEY_PACKAGES_FOR_UID_PROPERTY) {
+ 1024, CACHE_KEY_PACKAGES_FOR_UID_PROPERTY) {
@Override
public GetPackagesForUidResult recompute(Integer uid) {
try {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 15b13dc97554..ffb920b907ab 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -757,6 +757,15 @@ interface IActivityManager {
void addStartInfoTimestamp(int key, long timestampNs, int userId);
/**
+ * Reports view related timestamps to be added to the calling apps most
+ * recent {@link ApplicationStartInfo}.
+ *
+ * @param renderThreadDrawStartTimeNs Clock monotonic time in nanoseconds of RenderThread draw start
+ * @param framePresentedTimeNs Clock monotonic time in nanoseconds of frame presented
+ */
+ oneway void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs);
+
+ /**
* Return a list of {@link ApplicationExitInfo} records.
*
* <p class="note"> Note: System stores these historical information in a ring buffer, older
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
index dea4e9c82f56..7948cec545c3 100644
--- a/core/java/android/app/compat/ChangeIdStateCache.java
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -32,7 +32,7 @@ import com.android.internal.compat.IPlatformCompat;
public final class ChangeIdStateCache
extends PropertyInvalidatedCache<ChangeIdStateQuery, Boolean> {
private static final String CACHE_KEY = "cache_key.is_compat_change_enabled";
- private static final int MAX_ENTRIES = 64;
+ private static final int MAX_ENTRIES = 2048;
private static boolean sDisabled = false;
private volatile IPlatformCompat mPlatformCompat;
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 9a04dedb7c85..6fb852f50154 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -52,8 +52,8 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
* List of transaction items that should be executed in order. Including both
* {@link ActivityLifecycleItem} and other {@link ClientTransactionItem}.
*/
- @Nullable
- private List<ClientTransactionItem> mTransactionItems;
+ @NonNull
+ private final List<ClientTransactionItem> mTransactionItems = new ArrayList<>();
/** @deprecated use {@link #getTransactionItems} instead. */
@Nullable
@@ -89,12 +89,7 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
* @param item A single message that can contain a client activity/window request/callback.
*/
public void addTransactionItem(@NonNull ClientTransactionItem item) {
- if (Flags.bundleClientTransactionFlag()) {
- if (mTransactionItems == null) {
- mTransactionItems = new ArrayList<>();
- }
- mTransactionItems.add(item);
- }
+ mTransactionItems.add(item);
// TODO(b/324203798): cleanup after remove UnsupportedAppUsage
// Populate even if mTransactionItems is set to support the UnsupportedAppUsage.
@@ -107,9 +102,8 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
/**
* Gets the list of client window requests/callbacks.
- * TODO(b/260873529): must be non null after remove the deprecated methods.
*/
- @Nullable
+ @NonNull
public List<ClientTransactionItem> getTransactionItems() {
return mTransactionItems;
}
@@ -197,22 +191,9 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
* requested by transaction items.
*/
public void preExecute(@NonNull ClientTransactionHandler clientTransactionHandler) {
- if (mTransactionItems != null) {
- final int size = mTransactionItems.size();
- for (int i = 0; i < size; ++i) {
- mTransactionItems.get(i).preExecute(clientTransactionHandler);
- }
- return;
- }
-
- if (mActivityCallbacks != null) {
- final int size = mActivityCallbacks.size();
- for (int i = 0; i < size; ++i) {
- mActivityCallbacks.get(i).preExecute(clientTransactionHandler);
- }
- }
- if (mLifecycleStateRequest != null) {
- mLifecycleStateRequest.preExecute(clientTransactionHandler);
+ final int size = mTransactionItems.size();
+ for (int i = 0; i < size; ++i) {
+ mTransactionItems.get(i).preExecute(clientTransactionHandler);
}
}
@@ -252,29 +233,13 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
if (Flags.disableObjectPool()) {
return;
}
- if (mTransactionItems != null) {
- int size = mTransactionItems.size();
- for (int i = 0; i < size; i++) {
- mTransactionItems.get(i).recycle();
- }
- mTransactionItems = null;
- mActivityCallbacks = null;
- mLifecycleStateRequest = null;
- } else {
- // Only needed when mTransactionItems is null, otherwise these will have the same
- // reference as mTransactionItems to support UnsupportedAppUsage.
- if (mActivityCallbacks != null) {
- int size = mActivityCallbacks.size();
- for (int i = 0; i < size; i++) {
- mActivityCallbacks.get(i).recycle();
- }
- mActivityCallbacks = null;
- }
- if (mLifecycleStateRequest != null) {
- mLifecycleStateRequest.recycle();
- mLifecycleStateRequest = null;
- }
+ int size = mTransactionItems.size();
+ for (int i = 0; i < size; i++) {
+ mTransactionItems.get(i).recycle();
}
+ mTransactionItems.clear();
+ mActivityCallbacks = null;
+ mLifecycleStateRequest = null;
mClient = null;
mActivityToken = null;
ObjectPool.recycle(this);
@@ -286,60 +251,24 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
@SuppressWarnings("AndroidFrameworkEfficientParcelable") // Item class is not final.
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- final boolean writeTransactionItems = mTransactionItems != null;
- dest.writeBoolean(writeTransactionItems);
- if (writeTransactionItems) {
- dest.writeParcelableList(mTransactionItems, flags);
- } else {
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
- // Only write mLifecycleStateRequest and mActivityCallbacks when mTransactionItems is
- // null
- dest.writeParcelable(mLifecycleStateRequest, flags);
- final boolean writeActivityCallbacks = mActivityCallbacks != null;
- dest.writeBoolean(writeActivityCallbacks);
- if (writeActivityCallbacks) {
- dest.writeParcelableList(mActivityCallbacks, flags);
- }
- }
+ dest.writeParcelableList(mTransactionItems, flags);
}
/** Read from Parcel. */
private ClientTransaction(@NonNull Parcel in) {
- final boolean readTransactionItems = in.readBoolean();
- if (readTransactionItems) {
- mTransactionItems = new ArrayList<>();
- in.readParcelableList(mTransactionItems, getClass().getClassLoader(),
- ClientTransactionItem.class);
-
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
- // Populate mLifecycleStateRequest and mActivityCallbacks from mTransactionItems so
- // that they have the same reference when there is UnsupportedAppUsage to those fields.
- final int size = mTransactionItems.size();
- for (int i = 0; i < size; i++) {
- final ClientTransactionItem item = mTransactionItems.get(i);
- if (item.isActivityLifecycleItem()) {
- setLifecycleStateRequest((ActivityLifecycleItem) item);
- } else {
- addCallback(item);
- }
- }
- } else {
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
- // Only read mLifecycleStateRequest and mActivityCallbacks when mTransactionItems is
- // null
- mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(),
- ActivityLifecycleItem.class);
- setActivityTokenIfNotSet(mLifecycleStateRequest);
- final boolean readActivityCallbacks = in.readBoolean();
- if (readActivityCallbacks) {
- mActivityCallbacks = new ArrayList<>();
- in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(),
- ClientTransactionItem.class);
- final int size = mActivityCallbacks.size();
- for (int i = 0; mActivityToken == null && i < size; i++) {
- final ClientTransactionItem item = mActivityCallbacks.get(i);
- setActivityTokenIfNotSet(item);
- }
+ in.readParcelableList(mTransactionItems, getClass().getClassLoader(),
+ ClientTransactionItem.class);
+
+ // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
+ // Populate mLifecycleStateRequest and mActivityCallbacks from mTransactionItems so
+ // that they have the same reference when there is UnsupportedAppUsage to those fields.
+ final int size = mTransactionItems.size();
+ for (int i = 0; i < size; i++) {
+ final ClientTransactionItem item = mTransactionItems.get(i);
+ if (item.isActivityLifecycleItem()) {
+ setLifecycleStateRequest((ActivityLifecycleItem) item);
+ } else {
+ addCallback(item);
}
}
}
@@ -390,25 +319,12 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("ClientTransaction{");
- if (mTransactionItems != null) {
- // #addTransactionItem
- sb.append("\n transactionItems=[");
- final int size = mTransactionItems.size();
- for (int i = 0; i < size; i++) {
- sb.append("\n ").append(mTransactionItems.get(i));
- }
- sb.append("\n ]");
- } else {
- // #addCallback
- sb.append("\n callbacks=[");
- final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
- for (int i = 0; i < size; i++) {
- sb.append("\n ").append(mActivityCallbacks.get(i));
- }
- sb.append("\n ]");
- // #setLifecycleStateRequest
- sb.append("\n stateRequest=").append(mLifecycleStateRequest);
+ sb.append("\n transactionItems=[");
+ final int size = mTransactionItems.size();
+ for (int i = 0; i < size; i++) {
+ sb.append("\n ").append(mTransactionItems.get(i));
}
+ sb.append("\n ]");
sb.append("\n}");
return sb.toString();
}
@@ -417,41 +333,18 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
void dump(@NonNull String prefix, @NonNull PrintWriter pw,
@NonNull ClientTransactionHandler transactionHandler) {
pw.append(prefix).println("ClientTransaction{");
- if (mTransactionItems != null) {
- pw.append(prefix).print(" transactionItems=[");
- final String itemPrefix = prefix + " ";
- final int size = mTransactionItems.size();
- if (size > 0) {
- pw.println();
- for (int i = 0; i < size; i++) {
- mTransactionItems.get(i).dump(itemPrefix, pw, transactionHandler);
- }
- pw.append(prefix).println(" ]");
- } else {
- pw.println("]");
- }
- pw.append(prefix).println("}");
- return;
- }
- pw.append(prefix).print(" callbacks=[");
+ pw.append(prefix).print(" transactionItems=[");
final String itemPrefix = prefix + " ";
- final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
+ final int size = mTransactionItems.size();
if (size > 0) {
pw.println();
for (int i = 0; i < size; i++) {
- mActivityCallbacks.get(i).dump(itemPrefix, pw, transactionHandler);
+ mTransactionItems.get(i).dump(itemPrefix, pw, transactionHandler);
}
pw.append(prefix).println(" ]");
} else {
pw.println("]");
}
-
- pw.append(prefix).println(" stateRequest=");
- if (mLifecycleStateRequest != null) {
- mLifecycleStateRequest.dump(itemPrefix, pw, transactionHandler);
- } else {
- pw.append(itemPrefix).println("null");
- }
pw.append(prefix).println("}");
}
}
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index 9b53461568ca..0c1e7a32ae5e 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.areConfigurationsEqualForDisplay;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.window.flags.Flags.activityWindowInfoFlag;
-import static com.android.window.flags.Flags.bundleClientTransactionFlag;
import static java.util.Objects.requireNonNull;
@@ -196,7 +195,7 @@ public class ClientTransactionListenerController {
/** Called before updating the Configuration of the given {@code context}. */
public void onContextConfigurationPreChanged(@NonNull Context context) {
- if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
+ if (ActivityThread.isSystem()) {
// Not enable for system server.
return;
}
@@ -212,7 +211,7 @@ public class ClientTransactionListenerController {
/** Called after updating the Configuration of the given {@code context}. */
public void onContextConfigurationPostChanged(@NonNull Context context) {
- if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
+ if (ActivityThread.isSystem()) {
// Not enable for system server.
return;
}
diff --git a/core/java/android/app/servertransaction/RefreshCallbackItem.java b/core/java/android/app/servertransaction/RefreshCallbackItem.java
index 368ed765b922..a3f82e907215 100644
--- a/core/java/android/app/servertransaction/RefreshCallbackItem.java
+++ b/core/java/android/app/servertransaction/RefreshCallbackItem.java
@@ -29,8 +29,8 @@ import android.os.Parcel;
/**
* Callback that allows to {@link TransactionExecutor#cycleToPath} to {@link ON_PAUSE} or
- * {@link ON_STOP} in {@link TransactionExecutor#executeCallbacks} for activity "refresh" flow
- * that goes through "paused -> resumed" or "stopped -> resumed" cycle.
+ * {@link ON_STOP} in {@link TransactionExecutor#executeTransactionItems} for activity "refresh"
+ * flow that goes through "paused -> resumed" or "stopped -> resumed" cycle.
*
* <p>This is used in combination with {@link com.android.server.wm.DisplayRotationCompatPolicy}
* for camera compatibility treatment that handles orientation mismatch between camera buffers and
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 480205ebc756..68012e235f97 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -26,7 +26,6 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import static android.app.servertransaction.TransactionExecutorHelper.getShortActivityName;
import static android.app.servertransaction.TransactionExecutorHelper.getStateName;
-import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;
import static android.app.servertransaction.TransactionExecutorHelper.shouldExcludeLastLifecycleState;
import static android.app.servertransaction.TransactionExecutorHelper.tId;
import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;
@@ -77,13 +76,7 @@ public class TransactionExecutor {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionExecuted");
try {
- if (transaction.getTransactionItems() != null) {
- executeTransactionItems(transaction);
- } else {
- // TODO(b/260873529): cleanup after launch.
- executeCallbacks(transaction);
- executeLifecycleState(transaction);
- }
+ executeTransactionItems(transaction);
} catch (Exception e) {
Slog.e(TAG, "Failed to execute the transaction: "
+ transactionToString(transaction, mTransactionHandler));
@@ -112,41 +105,6 @@ public class TransactionExecutor {
}
}
- /**
- * Cycle through all states requested by callbacks and execute them at proper times.
- * @deprecated use {@link #executeTransactionItems} instead.
- */
- @VisibleForTesting
- @Deprecated
- public void executeCallbacks(@NonNull ClientTransaction transaction) {
- final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
- if (callbacks == null || callbacks.isEmpty()) {
- // No callbacks to execute, return early.
- return;
- }
- if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callbacks in transaction");
-
- // In case when post-execution state of the last callback matches the final state requested
- // for the activity in this transaction, we won't do the last transition here and do it when
- // moving to final state instead (because it may contain additional parameters from server).
- final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
- final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState()
- : UNDEFINED;
- // Index of the last callback that requests some post-execution state.
- final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);
-
- final int size = callbacks.size();
- for (int i = 0; i < size; ++i) {
- final ClientTransactionItem item = callbacks.get(i);
-
- // Skip the very last transition and perform it by explicit state request instead.
- final int postExecutionState = item.getPostExecutionState();
- final boolean shouldExcludeLastLifecycleState = postExecutionState != UNDEFINED
- && i == lastCallbackRequestingState && finalState == postExecutionState;
- executeNonLifecycleItem(transaction, item, shouldExcludeLastLifecycleState);
- }
- }
-
private void executeNonLifecycleItem(@NonNull ClientTransaction transaction,
@NonNull ClientTransactionItem item, boolean shouldExcludeLastLifecycleState) {
final IBinder token = item.getActivityToken();
@@ -184,21 +142,6 @@ public class TransactionExecutor {
}
}
- /**
- * Transition to the final state if requested by the transaction.
- * @deprecated use {@link #executeTransactionItems} instead
- */
- @Deprecated
- private void executeLifecycleState(@NonNull ClientTransaction transaction) {
- final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
- if (lifecycleItem == null) {
- // No lifecycle request, return early.
- return;
- }
-
- executeLifecycleItem(transaction, lifecycleItem);
- }
-
private void executeLifecycleItem(@NonNull ClientTransaction transaction,
@NonNull ActivityLifecycleItem lifecycleItem) {
final IBinder token = lifecycleItem.getActivityToken();
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 710261ab4c97..9f622e9e7dda 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -226,29 +226,6 @@ public class TransactionExecutorHelper {
}
/**
- * Return the index of the last callback that requests the state in which activity will be after
- * execution. If there is a group of callbacks in the end that requests the same specific state
- * or doesn't request any - we will find the first one from such group.
- *
- * E.g. ActivityResult requests RESUMED post-execution state, Configuration does not request any
- * specific state. If there is a sequence
- * Configuration - ActivityResult - Configuration - ActivityResult
- * index 1 will be returned, because ActivityResult request on position 1 will be the last
- * request that moves activity to the RESUMED state where it will eventually end.
- * @deprecated to be removed with {@link TransactionExecutor#executeCallbacks}.
- */
- @Deprecated
- static int lastCallbackRequestingState(@NonNull ClientTransaction transaction) {
- final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
- if (callbacks == null || callbacks.isEmpty()
- || transaction.getLifecycleStateRequest() == null) {
- return -1;
- }
- return lastCallbackRequestingStateIndex(callbacks, 0, callbacks.size() - 1,
- transaction.getLifecycleStateRequest().getActivityToken());
- }
-
- /**
* Returns the index of the last callback between the start index and last index that requests
* the state for the given activity token in which that activity will be after execution.
* If there is a group of callbacks in the end that requests the same specific state or doesn't
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 3213b40b3437..abb562d8ddaf 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -523,6 +523,8 @@ public class AppWidgetManager {
private final IAppWidgetService mService;
private final DisplayMetrics mDisplayMetrics;
+ private int mMaxBitmapMemory = 0;
+
private boolean mHasPostedLegacyLists = false;
/**
@@ -548,6 +550,12 @@ public class AppWidgetManager {
if (mService == null) {
return;
}
+ // Allowing some buffer when estimating the maximum bitmap cache size
+ try {
+ mMaxBitmapMemory = (int) (mService.getMaxBitmapMemory() * 0.9);
+ } catch (Exception e) {
+ Log.e(TAG, "Error setting the maximum bitmap memory", e);
+ }
BackgroundThread.getExecutor().execute(() -> {
try {
mService.notifyProviderInheritance(getInstalledProvidersForPackage(mPackageName,
@@ -576,7 +584,7 @@ public class AppWidgetManager {
final RemoteViews viewsCopy = new RemoteViews(original);
Runnable updateWidgetWithTask = () -> {
try {
- viewsCopy.collectAllIntents().get();
+ viewsCopy.collectAllIntents(mMaxBitmapMemory).get();
action.acceptOrThrow(viewsCopy);
} catch (Exception e) {
Log.e(TAG, failureMsg, e);
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 24f18cc257f8..31157caaa11e 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -35,6 +35,8 @@ import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseConfig;
import android.hardware.input.VirtualMouseRelativeEvent;
import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualRotaryEncoderConfig;
+import android.hardware.input.VirtualRotaryEncoderScrollEvent;
import android.hardware.input.VirtualStylusButtonEvent;
import android.hardware.input.VirtualStylusConfig;
import android.hardware.input.VirtualStylusMotionEvent;
@@ -158,6 +160,12 @@ interface IVirtualDevice {
void createVirtualStylus(in VirtualStylusConfig config, IBinder token);
/**
+ * Creates a new rotary encoder and registers it with the input framework with the given token.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ void createVirtualRotaryEncoder(in VirtualRotaryEncoderConfig config, IBinder token);
+
+ /**
* Removes the input device corresponding to the given token from the framework.
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
@@ -218,6 +226,12 @@ interface IVirtualDevice {
boolean sendStylusButtonEvent(IBinder token, in VirtualStylusButtonEvent event);
/**
+ * Injects a scroll event from the virtual rotary encoder corresponding to the given token.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ boolean sendRotaryEncoderScrollEvent(IBinder token, in VirtualRotaryEncoderScrollEvent event);
+
+ /**
* Returns all virtual sensors created for this device.
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 60448bad8e69..af86c97c632f 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -46,6 +46,8 @@ import android.hardware.input.VirtualMouse;
import android.hardware.input.VirtualMouseConfig;
import android.hardware.input.VirtualNavigationTouchpad;
import android.hardware.input.VirtualNavigationTouchpadConfig;
+import android.hardware.input.VirtualRotaryEncoder;
+import android.hardware.input.VirtualRotaryEncoderConfig;
import android.hardware.input.VirtualStylus;
import android.hardware.input.VirtualStylusConfig;
import android.hardware.input.VirtualTouchscreen;
@@ -335,6 +337,19 @@ public class VirtualDeviceInternal {
}
}
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull VirtualRotaryEncoderConfig config) {
+ try {
+ final IBinder token = new Binder(
+ "android.hardware.input.VirtualRotaryEncoder:" + config.getInputDeviceName());
+ mVirtualDevice.createVirtualRotaryEncoder(config, token);
+ return new VirtualRotaryEncoder(config, mVirtualDevice, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@NonNull
VirtualNavigationTouchpad createVirtualNavigationTouchpad(
@NonNull VirtualNavigationTouchpadConfig config) {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index ed55a3fe4392..c68014d7e3d4 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -56,6 +56,8 @@ import android.hardware.input.VirtualMouse;
import android.hardware.input.VirtualMouseConfig;
import android.hardware.input.VirtualNavigationTouchpad;
import android.hardware.input.VirtualNavigationTouchpadConfig;
+import android.hardware.input.VirtualRotaryEncoder;
+import android.hardware.input.VirtualRotaryEncoderConfig;
import android.hardware.input.VirtualStylus;
import android.hardware.input.VirtualStylusConfig;
import android.hardware.input.VirtualTouchscreen;
@@ -947,6 +949,23 @@ public final class VirtualDeviceManager {
}
/**
+ * Creates a virtual rotary encoder.
+ *
+ * @param config the configuration for the virtual rotary encoder.
+ * @see android.view.InputDevice#SOURCE_ROTARY_ENCODER
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_VIRTUAL_ROTARY)
+ public VirtualRotaryEncoder createVirtualRotaryEncoder(
+ @NonNull VirtualRotaryEncoderConfig config) {
+ if (!android.companion.virtualdevice.flags.Flags.virtualRotary()) {
+ throw new UnsupportedOperationException("Virtual rotary support not enabled");
+ }
+ return mVirtualDeviceInternal.createVirtualRotaryEncoder(config);
+ }
+
+ /**
* Creates a VirtualAudioDevice, capable of recording audio emanating from this device,
* or injecting audio from another device.
*
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 1e7815329f3b..cd8082c1e27d 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -47,6 +47,14 @@ flag {
}
flag {
+ name: "virtual_rotary"
+ is_exported: true
+ namespace: "virtual_devices"
+ description: "Enable virtual rotary input"
+ bug: "320328752"
+}
+
+flag {
namespace: "virtual_devices"
name: "device_aware_drm"
description: "Makes MediaDrm APIs device-aware"
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a6edab1420ba..112507e00e85 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6068,7 +6068,6 @@ public class Intent implements Parcelable, Cloneable {
* @see #CHOOSER_CONTENT_TYPE_ALBUM
* @see #createChooser(Intent, CharSequence)
*/
- @FlaggedApi(android.service.chooser.Flags.FLAG_CHOOSER_ALBUM_TEXT)
public static final String EXTRA_CHOOSER_CONTENT_TYPE_HINT =
"android.intent.extra.CHOOSER_CONTENT_TYPE_HINT";
@@ -6085,7 +6084,6 @@ public class Intent implements Parcelable, Cloneable {
*
* @see #EXTRA_CHOOSER_CONTENT_TYPE_HINT
*/
- @FlaggedApi(android.service.chooser.Flags.FLAG_CHOOSER_ALBUM_TEXT)
public static final int CHOOSER_CONTENT_TYPE_ALBUM = 1;
/**
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 4f062090ca40..57ffed47bbe3 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1331,6 +1331,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* This change id is the gatekeeper for all treatments that force a given min aspect ratio.
* Enabling this change will allow the following min aspect ratio treatments to be applied:
* <ul>
+ * <li>OVERRIDE_MIN_ASPECT_RATIO_SMALL
* <li>OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
* <li>OVERRIDE_MIN_ASPECT_RATIO_LARGE
* </ul>
@@ -1372,6 +1373,22 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // buganizer id
/**
+ * This change id sets the activity's min aspect ratio to a small value as defined by
+ * OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE.
+ *
+ * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ // TODO(b/349060719): Add CTS tests.
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_SMALL = 349045028L; // buganizer id
+
+ /** @hide Small override aspect ratio, currently 4:3. */
+ public static final float OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE = 4 / 3f;
+
+ /**
* This change id sets the activity's min aspect ratio to a medium value as defined by
* OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 282ede385ba3..8c56a9d443ab 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -11436,7 +11436,7 @@ public abstract class PackageManager {
private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>
sApplicationInfoCache =
new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>(
- 32, PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO,
"getApplicationInfo") {
@Override
public ApplicationInfo recompute(ApplicationInfoQuery query) {
@@ -11537,7 +11537,7 @@ public abstract class PackageManager {
private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>
sPackageInfoCache =
new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>(
- 64, PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO,
"getPackageInfo") {
@Override
public PackageInfo recompute(PackageInfoQuery query) {
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index b98a0d825227..c521b96fd8ee 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -232,7 +232,17 @@ public class IntentFactory {
oemComponentName,
PackageManager.ComponentInfoFlags.of(
PackageManager.MATCH_SYSTEM_ONLY));
- if (info.enabled && info.exported) {
+ boolean oemComponentEnabled = info.enabled;
+ int runtimeComponentEnabledState = context.getPackageManager()
+ .getComponentEnabledSetting(oemComponentName);
+ if (runtimeComponentEnabledState == PackageManager
+ .COMPONENT_ENABLED_STATE_ENABLED) {
+ oemComponentEnabled = true;
+ } else if (runtimeComponentEnabledState == PackageManager
+ .COMPONENT_ENABLED_STATE_DISABLED) {
+ oemComponentEnabled = false;
+ }
+ if (oemComponentEnabled && info.exported) {
intentResultBuilder.setOemUiUsageStatus(IntentCreationResult
.OemUiUsageStatus.SUCCESS);
Slog.i(TAG,
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index faa2c7018d6f..e0dc568790a3 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -239,8 +239,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
NoPreloadHolder.DEBUG_SQL_STATEMENTS, NoPreloadHolder.DEBUG_SQL_TIME,
mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
} catch (SQLiteCantOpenDatabaseException e) {
- final StringBuilder message = new StringBuilder("Cannot open database '")
- .append(file).append('\'')
+ final StringBuilder message = new StringBuilder(e.getMessage())
+ .append(" '").append(file).append("'")
.append(" with flags 0x")
.append(Integer.toHexString(mConfiguration.openFlags));
@@ -265,12 +265,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
message.append(": File ").append(path).append(" is not readable");
} else if (Files.isDirectory(path)) {
message.append(": Path ").append(path).append(" is a directory");
- } else {
- message.append(": Unable to deduct failure reason");
}
} catch (Throwable th) {
- message.append(": Unable to deduct failure reason"
- + " because filesystem couldn't be examined: ").append(th.getMessage());
+ // Ignore any exceptions generated whilst attempting to create extended diagnostic
+ // messages.
}
throw new SQLiteCantOpenDatabaseException(message.toString(), e);
} finally {
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 53771e38e4a5..21c900260d2e 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1025,8 +1025,18 @@ public final class InputManager {
/**
* Monitor input on the specified display for gestures.
*
+ * NOTE: New usages of Gesture Monitors are strongly discouraged. Gesture Monitors are
+ * deprecated, in favor of spy windows (see {@link LayoutParams#INPUT_FEATURE_SPY}).
+ * The spy window should be configured specifically to receive the desired events,
+ * unlike the gesture monitor which receives all events on the display.
+ *
* @hide
+ * @deprecated
+ * @see LayoutParams#INPUT_FEATURE_SPY
+ * @see android.os.InputConfig#SPY
+ * @see #pilferPointers(IBinder)
*/
+ @Deprecated
public InputMonitor monitorGestureInput(String name, int displayId) {
return mGlobal.monitorGestureInput(name, displayId);
}
diff --git a/core/java/android/hardware/input/VirtualRotaryEncoder.java b/core/java/android/hardware/input/VirtualRotaryEncoder.java
new file mode 100644
index 000000000000..bc131df9d111
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualRotaryEncoder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.companion.virtual.IVirtualDevice;
+import android.companion.virtualdevice.flags.Flags;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A virtual rotary encoder representing a scroll input mechanism on a remote device.
+ *
+ * <p>This registers an {@link android.view.InputDevice} that is interpreted like a
+ * physically-connected device and dispatches received events to it.</p>
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_ROTARY)
+@SystemApi
+public class VirtualRotaryEncoder extends VirtualInputDevice {
+ /** @hide */
+ public VirtualRotaryEncoder(VirtualRotaryEncoderConfig config, IVirtualDevice virtualDevice,
+ IBinder token) {
+ super(config, virtualDevice, token);
+ }
+
+ /**
+ * Sends a scroll event to the system.
+ *
+ * @param event the event to send
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void sendScrollEvent(@NonNull VirtualRotaryEncoderScrollEvent event) {
+ try {
+ if (!mVirtualDevice.sendRotaryEncoderScrollEvent(mToken, event)) {
+ Log.w(TAG, "Failed to send scroll event from virtual rotary "
+ + mConfig.getInputDeviceName());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/hardware/input/VirtualRotaryEncoderConfig.aidl b/core/java/android/hardware/input/VirtualRotaryEncoderConfig.aidl
new file mode 100644
index 000000000000..2d9a8995c6d8
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualRotaryEncoderConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+parcelable VirtualRotaryEncoderConfig;
diff --git a/core/java/android/hardware/input/VirtualRotaryEncoderConfig.java b/core/java/android/hardware/input/VirtualRotaryEncoderConfig.java
new file mode 100644
index 000000000000..8d81fa938e2a
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualRotaryEncoderConfig.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Configuration for creating a virtual rotary encoder.
+ *
+ * @see android.companion.virtual.VirtualDeviceManager.VirtualDevice#createVirtualRotaryEncoder
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_ROTARY)
+@SystemApi
+public final class VirtualRotaryEncoderConfig extends VirtualInputDeviceConfig
+ implements Parcelable {
+ @NonNull
+ public static final Creator<VirtualRotaryEncoderConfig> CREATOR = new Creator<>() {
+ @Override
+ public VirtualRotaryEncoderConfig createFromParcel(Parcel in) {
+ return new VirtualRotaryEncoderConfig(in);
+ }
+
+ @Override
+ public VirtualRotaryEncoderConfig[] newArray(int size) {
+ return new VirtualRotaryEncoderConfig[size];
+ }
+ };
+
+ private VirtualRotaryEncoderConfig(@NonNull VirtualRotaryEncoderConfig.Builder builder) {
+ super(builder);
+ }
+
+ private VirtualRotaryEncoderConfig(@NonNull Parcel in) {
+ super(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Builder for creating a {@link VirtualRotaryEncoderConfig}.
+ */
+ public static final class Builder extends VirtualInputDeviceConfig.Builder<Builder> {
+
+ /**
+ * Builds the {@link VirtualRotaryEncoderConfig} instance.
+ */
+ @NonNull
+ public VirtualRotaryEncoderConfig build() {
+ return new VirtualRotaryEncoderConfig(this);
+ }
+ }
+}
diff --git a/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.aidl b/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.aidl
new file mode 100644
index 000000000000..0b238ee1c4c7
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+parcelable VirtualRotaryEncoderScrollEvent;
diff --git a/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java b/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java
new file mode 100644
index 000000000000..b8f2c008d8b2
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.view.InputEvent;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * An event describing a rotary encoder scroll interaction originating from a remote device.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_ROTARY)
+@SystemApi
+public final class VirtualRotaryEncoderScrollEvent implements Parcelable {
+
+ private final float mScrollAmount;
+ private final long mEventTimeNanos;
+
+ private VirtualRotaryEncoderScrollEvent(float scrollAmount, long eventTimeNanos) {
+ mScrollAmount = scrollAmount;
+ mEventTimeNanos = eventTimeNanos;
+ }
+
+ private VirtualRotaryEncoderScrollEvent(@NonNull Parcel parcel) {
+ mScrollAmount = parcel.readFloat();
+ mEventTimeNanos = parcel.readLong();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+ parcel.writeFloat(mScrollAmount);
+ parcel.writeLong(mEventTimeNanos);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "VirtualRotaryScrollEvent("
+ + " scrollAmount=" + mScrollAmount
+ + " eventTime(ns)=" + mEventTimeNanos;
+ }
+
+ /**
+ * Returns the scroll amount, normalized from -1.0 to 1.0, inclusive. Positive values
+ * indicate scrolling forward (e.g. down in a vertical list); negative values, backward.
+ */
+ public @FloatRange(from = -1.0f, to = 1.0f) float getScrollAmount() {
+ return mScrollAmount;
+ }
+
+ /**
+ * Returns the time this event occurred, in the {@link SystemClock#uptimeMillis()} time base but
+ * with nanosecond (instead of millisecond) precision.
+ *
+ * @see InputEvent#getEventTime()
+ */
+ public long getEventTimeNanos() {
+ return mEventTimeNanos;
+ }
+
+ /**
+ * Builder for {@link VirtualRotaryEncoderScrollEvent}.
+ */
+ public static final class Builder {
+
+ private float mScrollAmount;
+ private long mEventTimeNanos = 0L;
+
+ /**
+ * Creates a {@link VirtualRotaryEncoderScrollEvent} object with the current configuration.
+ */
+ public @NonNull VirtualRotaryEncoderScrollEvent build() {
+ return new VirtualRotaryEncoderScrollEvent(mScrollAmount, mEventTimeNanos);
+ }
+
+ /**
+ * Sets the scroll amount, normalized from -1.0 to 1.0, inclusive. Positive values
+ * indicate scrolling forward (e.g. down in a vertical list); negative values, backward.
+ *
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setScrollAmount(
+ @FloatRange(from = -1.0f, to = 1.0f) float scrollAmount) {
+ Preconditions.checkArgumentInRange(scrollAmount, -1f, 1f, "scrollAmount");
+ mScrollAmount = scrollAmount;
+ return this;
+ }
+
+ /**
+ * Sets the time (in nanoseconds) when this specific event was generated. This may be
+ * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
+ * millisecond), but can be different depending on the use case.
+ * This field is optional and can be omitted.
+ *
+ * @return this builder, to allow for chaining of calls
+ * @see InputEvent#getEventTime()
+ */
+ public @NonNull Builder setEventTimeNanos(long eventTimeNanos) {
+ if (eventTimeNanos < 0L) {
+ throw new IllegalArgumentException("Event time cannot be negative");
+ }
+ mEventTimeNanos = eventTimeNanos;
+ return this;
+ }
+ }
+
+ public static final @NonNull Creator<VirtualRotaryEncoderScrollEvent> CREATOR =
+ new Creator<VirtualRotaryEncoderScrollEvent>() {
+ public VirtualRotaryEncoderScrollEvent createFromParcel(Parcel source) {
+ return new VirtualRotaryEncoderScrollEvent(source);
+ }
+
+ public VirtualRotaryEncoderScrollEvent[] newArray(int size) {
+ return new VirtualRotaryEncoderScrollEvent[size];
+ }
+ };
+}
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index f2143f63d1ee..7b91dd5822e7 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -50,7 +50,7 @@ public final class ServiceManagerNative {
class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
- mServiceManager = IServiceManager.Stub.asInterface(remote);
+ mServiceManager = IServiceManager.Stub.asInterface(this.getNativeServiceManager());
}
public IBinder asBinder() {
@@ -128,4 +128,6 @@ class ServiceManagerProxy implements IServiceManager {
private IBinder mRemote;
private IServiceManager mServiceManager;
+
+ private native IBinder getNativeServiceManager();
}
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index cba4423831a2..c801fabf9427 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -25,6 +25,8 @@ import android.system.OsConstants;
import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -63,7 +65,7 @@ public final class SharedMemory implements Parcelable, Closeable {
mMemoryRegistration = new MemoryRegistration(mSize);
mCleaner = Cleaner.create(mFileDescriptor,
- new Closer(mFileDescriptor.getInt$(), mMemoryRegistration));
+ new Closer(mFileDescriptor, mMemoryRegistration));
}
/**
@@ -276,7 +278,6 @@ public final class SharedMemory implements Parcelable, Closeable {
*/
@Override
public void close() {
- mFileDescriptor.setInt$(-1);
if (mCleaner != null) {
mCleaner.clean();
mCleaner = null;
@@ -326,21 +327,20 @@ public final class SharedMemory implements Parcelable, Closeable {
* Cleaner that closes the FD
*/
private static final class Closer implements Runnable {
- private int mFd;
+ private FileDescriptor mFd;
private MemoryRegistration mMemoryReference;
- private Closer(int fd, MemoryRegistration memoryReference) {
+ private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
mFd = fd;
+ IoUtils.setFdOwner(mFd, this);
mMemoryReference = memoryReference;
}
@Override
public void run() {
- try {
- FileDescriptor fd = new FileDescriptor();
- fd.setInt$(mFd);
- Os.close(fd);
- } catch (ErrnoException e) { /* swallow error */ }
+ IoUtils.closeQuietly(mFd);
+ mFd = null;
+
mMemoryReference.release();
mMemoryReference = null;
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index e82c4b064bf2..76cf901ff30d 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -201,3 +201,10 @@ flag {
description: "Tracing using Perfetto SDK."
bug: "303199244"
}
+
+flag {
+ name: "allow_consentless_bugreport_delegated_consent"
+ namespace: "crumpet"
+ description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead"
+ bug: "324046728"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4e8a04f0993f..493d6760e5eb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11072,6 +11072,13 @@ public final class Settings {
"active_unlock_on_biometric_fail";
/**
+ * Whether or not active unlock triggers on legacy unlock intents.
+ * @hide
+ */
+ public static final String ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY =
+ "active_unlock_on_unlock_intent_legacy";
+
+ /**
* If active unlock triggers on biometric failures, include the following error codes
* as a biometric failure. See {@link android.hardware.biometrics.BiometricFaceConstants}.
* Error codes should be separated by a pipe. For example: "1|4|5". If active unlock
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index fe7eab7abeda..3954bc2318cb 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -27,14 +27,6 @@ flag {
}
flag {
- name: "fix_unlocked_device_required_keys_v2"
- namespace: "hardware_backed_security"
- description: "Fix bugs in behavior of UnlockedDeviceRequired keystore keys"
- bug: "296464083"
- is_fixed_read_only: true
-}
-
-flag {
name: "keyinfo_unlocked_device_required"
is_exported: true
namespace: "hardware_backed_security"
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index ed20207cfb0b..82c16138f967 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -2,14 +2,6 @@ package: "android.service.chooser"
container: "system"
flag {
- name: "chooser_album_text"
- is_exported: true
- namespace: "intentresolver"
- description: "Flag controlling the album text subtype hint for sharesheet"
- bug: "323380224"
-}
-
-flag {
name: "enable_sharesheet_metadata_extra"
is_exported: true
namespace: "intentresolver"
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 63410c787540..073f512a85fb 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -184,7 +184,6 @@ public final class Adjustment implements Parcelable {
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
- TYPE_UNKNOWN,
TYPE_OTHER,
TYPE_PROMOTION,
TYPE_SOCIAL_MEDIA,
@@ -195,12 +194,8 @@ public final class Adjustment implements Parcelable {
public @interface Types {}
/**
- * The type of this notification is unknown.
- */
- @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
- public static final int TYPE_UNKNOWN = -1;
- /**
- * The type of this notification is not one of ones known to the NotificationAssistantService.
+ * This notification can be categorized, but not into one of the other categories known to the
+ * OS at a given version.
*/
@FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
public static final int TYPE_OTHER = 0;
diff --git a/core/java/android/util/SequenceUtils.java b/core/java/android/util/SequenceUtils.java
new file mode 100644
index 000000000000..f833ce314c91
--- /dev/null
+++ b/core/java/android/util/SequenceUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Utilities to manage an info change seq id to ensure the update is in sync between client and
+ * system server. This should be used for info that can be updated though multiple IPC channel.
+ *
+ * To use it:
+ * 1. The system server should store the current seq as the source of truth, with initializing to
+ * {@link #getInitSeq}.
+ * 2. Whenever a newer info needs to be sent to the client side, the system server should first
+ * update its seq with {@link #getNextSeq}, then send the new info with the new seq to the client.
+ * 3. On the client side, when receiving a new info, it should only consume it if it is newer than
+ * the last received info seq by checking {@link #isIncomingSeqNewer}.
+ *
+ * @hide
+ */
+public final class SequenceUtils {
+
+ private SequenceUtils() {
+ }
+
+ /**
+ * Returns {@code true} if the incomingSeq is newer than the curSeq.
+ */
+ public static boolean isIncomingSeqNewer(int curSeq, int incomingSeq) {
+ // Convert to long for comparison.
+ final long diff = (long) incomingSeq - curSeq;
+ // If there has been a sufficiently large jump, assume the sequence has wrapped around.
+ // For example, when the last seq is MAX_VALUE, the incoming seq will be MIN_VALUE + 1.
+ // diff = MIN_VALUE + 1 - MAX_VALUE. It is smaller than 0, but should be treated as newer.
+ return diff > 0 || diff < Integer.MIN_VALUE;
+ }
+
+ /** Returns the initial seq. */
+ public static int getInitSeq() {
+ return Integer.MIN_VALUE;
+ }
+
+ /** Returns the next seq. */
+ public static int getNextSeq(int seq) {
+ return seq == Integer.MAX_VALUE
+ // Skip the initial seq, so that when the app process is relaunched, the incoming
+ // seq from the server is always treated as newer.
+ ? getInitSeq() + 1
+ : ++seq;
+ }
+}
diff --git a/core/java/android/view/EventLogTags.logtags b/core/java/android/view/EventLogTags.logtags
index f1cd671ef176..f3792930647a 100644
--- a/core/java/android/view/EventLogTags.logtags
+++ b/core/java/android/view/EventLogTags.logtags
@@ -62,6 +62,9 @@ option java_package android.view
# following other view events defined in system/logging/logcat/event.logtags
# ViewRoot Draw Events
60004 viewroot_draw_event (window|3),(event|3)
+# SurfaceView Events
+60005 surfaceview_layout (window|3),(format|1),(width|1),(height|1),(z|1),(sizeFrom|3),(attached|1),(lifecycleStrategy|1),(viewVisible|1)
+60006 surfaceview_callback (window|3),(callback|3)
# NOTE - the range 1000000-2000000 is reserved for partners and others who
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index 4996f5a41c82..2302dc7bd8c8 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -32,8 +32,11 @@ import com.android.internal.util.DataClass;
* registered to monitor that type of event on the targeted display.
*
* @hide
+ * @deprecated See {@link android.hardware.input.InputManager#monitorGestureInput(String, int)}
+ * for more details.
*/
@DataClass(genToString = true)
+@Deprecated
public final class InputMonitor implements Parcelable {
private static final String TAG = "InputMonitor";
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 83ff88bdcc1c..1b3b3ebbecfc 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -89,7 +89,7 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
applyParams(t, surfaceParams, mTmpFloat9);
}
- t.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+ t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
t.apply();
t.close();
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 487214c5c33a..2efa647d3169 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -18,6 +18,7 @@ package android.view;
import static android.graphics.PointProto.X;
import static android.graphics.PointProto.Y;
+import static android.util.SequenceUtils.getInitSeq;
import static android.view.InsetsSourceControlProto.LEASH;
import static android.view.InsetsSourceControlProto.POSITION;
import static android.view.InsetsSourceControlProto.TYPE_NUMBER;
@@ -266,6 +267,9 @@ public class InsetsSourceControl implements Parcelable {
private @Nullable InsetsSourceControl[] mControls;
+ /** To make sure the info update between client and system server is in order. */
+ private int mSeq = getInitSeq();
+
public Array() {
}
@@ -280,9 +284,18 @@ public class InsetsSourceControl implements Parcelable {
readFromParcel(in);
}
+ public int getSeq() {
+ return mSeq;
+ }
+
+ public void setSeq(int seq) {
+ mSeq = seq;
+ }
+
/** Updates the current Array to the given Array. */
public void setTo(@NonNull Array other, boolean copyControls) {
set(other.mControls, copyControls);
+ mSeq = other.mSeq;
}
/** Updates the current controls to the given controls. */
@@ -336,11 +349,13 @@ public class InsetsSourceControl implements Parcelable {
public void readFromParcel(Parcel in) {
mControls = in.createTypedArray(InsetsSourceControl.CREATOR);
+ mSeq = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeTypedArray(mControls, flags);
+ out.writeInt(mSeq);
}
public static final @NonNull Creator<Array> CREATOR = new Creator<>() {
@@ -362,6 +377,7 @@ public class InsetsSourceControl implements Parcelable {
return false;
}
final InsetsSourceControl.Array other = (InsetsSourceControl.Array) o;
+ // mSeq is for internal bookkeeping only.
return Arrays.equals(mControls, other.mControls);
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 21eec67bfe10..bbd9acfd4cd7 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -17,6 +17,7 @@
package android.view;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.util.SequenceUtils.getInitSeq;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
@@ -95,6 +96,9 @@ public class InsetsState implements Parcelable {
/** The display shape */
private DisplayShape mDisplayShape = DisplayShape.NONE;
+ /** To make sure the info update between client and system server is in order. */
+ private int mSeq = getInitSeq();
+
public InsetsState() {
mSources = new SparseArray<>();
}
@@ -586,6 +590,14 @@ public class InsetsState implements Parcelable {
}
}
+ public int getSeq() {
+ return mSeq;
+ }
+
+ public void setSeq(int seq) {
+ mSeq = seq;
+ }
+
public void set(InsetsState other) {
set(other, false /* copySources */);
}
@@ -597,6 +609,7 @@ public class InsetsState implements Parcelable {
mRoundedCornerFrame.set(other.mRoundedCornerFrame);
mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds();
mDisplayShape = other.getDisplayShape();
+ mSeq = other.mSeq;
mSources.clear();
for (int i = 0, size = other.mSources.size(); i < size; i++) {
final InsetsSource otherSource = other.mSources.valueAt(i);
@@ -620,6 +633,7 @@ public class InsetsState implements Parcelable {
mRoundedCornerFrame.set(other.mRoundedCornerFrame);
mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds();
mDisplayShape = other.getDisplayShape();
+ mSeq = other.mSeq;
if (types == 0) {
return;
}
@@ -705,6 +719,7 @@ public class InsetsState implements Parcelable {
|| !mRoundedCornerFrame.equals(state.mRoundedCornerFrame)
|| !mPrivacyIndicatorBounds.equals(state.mPrivacyIndicatorBounds)
|| !mDisplayShape.equals(state.mDisplayShape)) {
+ // mSeq is for internal bookkeeping only.
return false;
}
@@ -778,6 +793,7 @@ public class InsetsState implements Parcelable {
mRoundedCornerFrame.writeToParcel(dest, flags);
dest.writeTypedObject(mPrivacyIndicatorBounds, flags);
dest.writeTypedObject(mDisplayShape, flags);
+ dest.writeInt(mSeq);
final int size = mSources.size();
dest.writeInt(size);
for (int i = 0; i < size; i++) {
@@ -803,6 +819,7 @@ public class InsetsState implements Parcelable {
mRoundedCornerFrame.readFromParcel(in);
mPrivacyIndicatorBounds = in.readTypedObject(PrivacyIndicatorBounds.CREATOR);
mDisplayShape = in.readTypedObject(DisplayShape.CREATOR);
+ mSeq = in.readInt();
final int size = in.readInt();
final SparseArray<InsetsSource> sources;
if (mSources == null) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 405abd784bc9..f6535243eaac 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -4508,6 +4508,10 @@ public final class SurfaceControl implements Parcelable {
Log.w(TAG, "addTransactionCompletedListener was called but flag is disabled");
return this;
}
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setFrameTimeline", this, null, "vsyncId=" + vsyncId);
+ }
nativeSetFrameTimelineVsync(mNativeObject, vsyncId);
return this;
}
@@ -4515,6 +4519,11 @@ public final class SurfaceControl implements Parcelable {
/** @hide */
@NonNull
public Transaction setFrameTimelineVsync(long frameTimelineVsyncId) {
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setFrameTimelineVsync", this, null, "frameTimelineVsyncId="
+ + frameTimelineVsyncId);
+ }
nativeSetFrameTimelineVsync(mNativeObject, frameTimelineVsyncId);
return this;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1d70d18ac4c8..fedbe4a65e07 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -49,6 +49,7 @@ import android.os.SystemClock;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
+import android.util.EventLog;
import android.util.Log;
import android.view.SurfaceControl.Transaction;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -163,6 +164,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private static final boolean DEBUG_POSITION = false;
private static final long FORWARD_BACK_KEY_TOLERANCE_MS = 100;
+ private static final int LOGTAG_SURFACEVIEW_LAYOUT = 60005;
+ private static final int LOGTAG_SURFACEVIEW_CALLBACK = 60006;
@UnsupportedAppUsage(
maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
@@ -320,6 +323,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private final ConcurrentLinkedQueue<WindowManager.LayoutParams> mEmbeddedWindowParams =
new ConcurrentLinkedQueue<>();
+ private String mTag = TAG;
+
private final ISurfaceControlViewHostParent mSurfaceControlViewHostParent =
new ISurfaceControlViewHostParent.Stub() {
@Override
@@ -418,13 +423,26 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
updateSurface();
}
+ private void setTag() {
+ String windowName = "";
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ // strip package name
+ final String[] split = viewRoot.mWindowAttributes.getTitle().toString().split("\\.");
+ if (split.length > 0) {
+ windowName = " " + split[split.length - 1];
+ }
+ }
+
+ mTag = "SV[" + System.identityHashCode(this) + windowName + "]";
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
-
+ setTag();
getViewRootImpl().addSurfaceChangedCallback(this);
mWindowStopped = false;
-
mViewVisibility = getVisibility() == VISIBLE;
updateRequestedVisibility();
@@ -1146,13 +1164,22 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
+ " format=" + formatChanged + " size=" + sizeChanged
+ " visible=" + visibleChanged + " alpha=" + alphaChanged
+ " hint=" + hintChanged
- + " visible=" + visibleChanged
+ " left=" + (mWindowSpaceLeft != mLocation[0])
+ " top=" + (mWindowSpaceTop != mLocation[1])
+ " z=" + relativeZChanged
+ " attached=" + mAttachedToWindow
+ " lifecycleStrategy=" + surfaceLifecycleStrategyChanged);
+ if (creating || formatChanged || sizeChanged || visibleChanged
+ || layoutSizeChanged || relativeZChanged || !mAttachedToWindow
+ || surfaceLifecycleStrategyChanged ) {
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_LAYOUT,
+ mTag, mRequestedFormat, myWidth, myHeight, mRequestedSubLayer,
+ (mRequestedWidth > 0 ? "setFixedSize" : "layout"),
+ (mAttachedToWindow ? 1 : 0),
+ mRequestedSurfaceLifecycleStrategy, (mRequestedVisible ? 1 : 0));
+ }
+
try {
mVisible = mRequestedVisible;
mWindowSpaceLeft = mLocation[0];
@@ -1235,6 +1262,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mIsCreating = true;
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "visibleChanged -- surfaceCreated");
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_CALLBACK, mTag,
+ "surfaceCreated");
callbacks = getSurfaceCallbacks();
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
@@ -1245,6 +1274,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "surfaceChanged -- format=" + mFormat
+ " w=" + myWidth + " h=" + myHeight);
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_CALLBACK, mTag,
+ "surfaceChanged -- format=" + mFormat
+ + " w=" + myWidth + " h=" + myHeight);
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
@@ -1256,6 +1288,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (DEBUG) {
Log.i(TAG, System.identityHashCode(this) + " surfaceRedrawNeeded");
}
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_CALLBACK, mTag,
+ "surfaceRedrawNeeded");
+
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
@@ -1337,7 +1372,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private void redrawNeededAsync(SurfaceHolder.Callback[] callbacks,
Runnable callbacksCollected) {
- SurfaceCallbackHelper sch = new SurfaceCallbackHelper(callbacksCollected);
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(callbacksCollected, mTag);
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
}
@@ -2100,6 +2135,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (mSurface.isValid()) {
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "surfaceDestroyed");
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_CALLBACK, mTag, "surfaceDestroyed");
SurfaceHolder.Callback[] callbacks = getSurfaceCallbacks();
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8c3390cc6462..47669427cb9d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -33933,6 +33933,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
float velocity = mFrameContentVelocity;
final float frameRate = mPreferredFrameRate;
ViewParent parent = mParent;
+ boolean isInputMethodWindowType = false;
+ if (mAttachInfo != null && mAttachInfo.mViewRootImpl != null) {
+ isInputMethodWindowType =
+ mAttachInfo.mViewRootImpl.mWindowAttributes.type == TYPE_INPUT_METHOD;
+ }
if (velocity <= 0 && Float.isNaN(frameRate)) {
// The most common case is when nothing is set, so this special case is called
// often.
@@ -33942,7 +33947,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|| mLastFrameTop != mTop)
&& viewRootImpl.shouldCheckFrameRate(false)
&& parent instanceof View
- && ((View) parent).mFrameContentVelocity <= 0) {
+ && ((View) parent).mFrameContentVelocity <= 0
+ && !isInputMethodWindowType) {
viewRootImpl.votePreferredFrameRate(MAX_FRAME_RATE, FRAME_RATE_COMPATIBILITY_GTE);
}
if (viewRootImpl.shouldCheckFrameRateCategory()) {
@@ -33965,6 +33971,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|| mLastFrameTop != mTop)
&& mParent instanceof View
&& ((View) mParent).mFrameContentVelocity <= 0
+ && !isInputMethodWindowType
) {
// This current calculation is very simple. If something on the screen
// moved, then it votes for the highest velocity.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1494d21e7769..bf5169fc2f82 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -178,6 +178,7 @@ import android.graphics.Region;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
+import android.hardware.SyncFence;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.display.DisplayManagerGlobal;
@@ -220,6 +221,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.InputDevice.InputSourceClass;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceControl.TransactionStats;
import android.view.View.AttachInfo;
import android.view.View.FocusDirection;
import android.view.View.MeasureSpec;
@@ -295,6 +297,7 @@ import java.util.OptionalInt;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* The top of a view hierarchy, implementing the needed protocol between View
@@ -1185,6 +1188,13 @@ public final class ViewRootImpl implements ViewParent,
private String mFpsTraceName;
private String mLargestViewTraceName;
+ private final boolean mAppStartInfoTimestampsFlagValue;
+ @GuardedBy("this")
+ private boolean mAppStartTimestampsSent = false;
+ private boolean mAppStartTrackingStarted = false;
+ private long mRenderThreadDrawStartTimeNs = -1;
+ private long mFirstFramePresentedTimeNs = -1;
+
private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue;
private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
@@ -1250,7 +1260,6 @@ public final class ViewRootImpl implements ViewParent,
mDensity = context.getResources().getDisplayMetrics().densityDpi;
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
- // TODO(b/222696368): remove getSfInstance usage and use vsyncId for transactions
mChoreographer = Choreographer.getInstance();
mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
mImeBackAnimationController = new ImeBackAnimationController(this, mInsetsController);
@@ -1302,6 +1311,8 @@ public final class ViewRootImpl implements ViewParent,
} else {
mSensitiveContentProtectionService = null;
}
+
+ mAppStartInfoTimestampsFlagValue = android.app.Flags.appStartInfoTimestamps();
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -2577,6 +2588,12 @@ public final class ViewRootImpl implements ViewParent,
notifySurfaceDestroyed();
}
destroySurface();
+
+ // Reset so they can be sent again for warm starts.
+ mAppStartTimestampsSent = false;
+ mAppStartTrackingStarted = false;
+ mRenderThreadDrawStartTimeNs = -1;
+ mFirstFramePresentedTimeNs = -1;
}
}
}
@@ -4375,6 +4392,30 @@ public final class ViewRootImpl implements ViewParent,
reportDrawFinished(t, seqId);
}
});
+
+ // Only trigger once per {@link ViewRootImpl} instance, so don't add listener if
+ // {link mTransactionCompletedTimeNs} has already been set.
+ if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) {
+ mAppStartTrackingStarted = true;
+ Transaction transaction = new Transaction();
+ transaction.addTransactionCompletedListener(mSimpleExecutor,
+ new Consumer<TransactionStats>() {
+ @Override
+ public void accept(TransactionStats transactionStats) {
+ SyncFence presentFence = transactionStats.getPresentFence();
+ if (presentFence.awaitForever()) {
+ if (mFirstFramePresentedTimeNs == -1) {
+ // Only trigger once per {@link ViewRootImpl} instance.
+ mFirstFramePresentedTimeNs = presentFence.getSignalTime();
+ maybeSendAppStartTimes();
+ }
+ }
+ presentFence.close();
+ }
+ });
+ applyTransactionOnDraw(transaction);
+ }
+
if (DEBUG_BLAST) {
Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName());
}
@@ -4382,6 +4423,45 @@ public final class ViewRootImpl implements ViewParent,
mWmsRequestSyncGroup.add(this, null /* runnable */);
}
+ private void maybeSendAppStartTimes() {
+ synchronized (this) {
+ if (mAppStartTimestampsSent) {
+ // Don't send timestamps more than once.
+ return;
+ }
+
+ // If we already have {@link mRenderThreadDrawStartTimeNs} then pass it through, if not
+ // post to main thread and check if we have it there.
+ if (mRenderThreadDrawStartTimeNs != -1) {
+ sendAppStartTimesLocked();
+ } else {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (ViewRootImpl.this) {
+ if (mRenderThreadDrawStartTimeNs == -1) {
+ return;
+ }
+ sendAppStartTimesLocked();
+ }
+ }
+ });
+ }
+ }
+ }
+
+ @GuardedBy("this")
+ private void sendAppStartTimesLocked() {
+ try {
+ ActivityManager.getService().reportStartInfoViewTimestamps(
+ mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs);
+ mAppStartTimestampsSent = true;
+ } catch (RemoteException e) {
+ // Ignore, timestamps may be lost.
+ if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e);
+ }
+ }
+
/**
* Helper used to notify the service to block projection when a sensitive
* view (the view displays sensitive content) is attached to the window.
@@ -5568,7 +5648,13 @@ public final class ViewRootImpl implements ViewParent,
registerCallbackForPendingTransactions();
}
+ long timeNs = SystemClock.uptimeNanos();
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
+
+ // Only trigger once per {@link ViewRootImpl} instance.
+ if (mAppStartInfoTimestampsFlagValue && mRenderThreadDrawStartTimeNs == -1) {
+ mRenderThreadDrawStartTimeNs = timeNs;
+ }
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index ae051f96fd03..18006bb7866a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -6387,10 +6387,6 @@ public interface WindowManager extends ViewManager {
* The host is likely to be an {@link AttachedSurfaceControl} so the host token can be
* retrieved via {@link AttachedSurfaceControl#getInputTransferToken()}.
* <p><br>
- * Only the window currently receiving touch is allowed to transfer the gesture so if the caller
- * attempts to transfer touch gesture from a token that doesn't have touch, it will fail the
- * transfer.
- * <p><br>
* When the host wants to transfer touch gesture to the embedded, it can retrieve the embedded
* token via {@link SurfaceControlViewHost.SurfacePackage#getInputTransferToken()} or use the
* value returned from either
@@ -6414,6 +6410,23 @@ public interface WindowManager extends ViewManager {
* arrives, input dispatcher will do a new round of hit testing. So, if the host window is
* still the first thing that's being touched, then it will receive the new gesture again. It
* will again be up to the host to transfer this new gesture to the embedded.
+ * <p><br>
+ * The call can fail for the following reasons:
+ * <ul>
+ * <li>
+ * Caller attempts to transfer touch gesture from a token that doesn't have an active gesture.
+ * </li>
+ * <li>
+ * The gesture is transferred to a token that is not associated with the transferFromToken. For
+ * example, if the caller transfers to a {@link SurfaceControlViewHost} not attached to the
+ * host window via {@link SurfaceView#setChildSurfacePackage(SurfacePackage)}.
+ * </li>
+ * <li>
+ * The active gesture completes before the transfer is complete, such as in the case of a
+ * fling.
+ * </li>
+ * </ul>
+ * <p>
*
* @param transferFromToken the InputTransferToken for the currently active gesture
* @param transferToToken the InputTransferToken to transfer the gesture to.
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 3fc9ebc7c9fb..a4cea3364998 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -19,6 +19,8 @@ package android.view.accessibility;
import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -62,6 +64,7 @@ import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Display;
import android.view.IWindow;
import android.view.SurfaceControl;
import android.view.View;
@@ -69,6 +72,7 @@ import android.view.accessibility.AccessibilityEvent.EventType;
import com.android.internal.R;
import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IntPair;
@@ -1565,7 +1569,7 @@ public final class AccessibilityManager {
@SystemApi
@RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
public void performAccessibilityShortcut() {
- performAccessibilityShortcut(null);
+ performAccessibilityShortcut(Display.DEFAULT_DISPLAY, HARDWARE, null);
}
/**
@@ -1577,7 +1581,8 @@ public final class AccessibilityManager {
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
- public void performAccessibilityShortcut(@Nullable String targetName) {
+ public void performAccessibilityShortcut(
+ int displayId, @UserShortcutType int shortcutType, @Nullable String targetName) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -1586,7 +1591,7 @@ public final class AccessibilityManager {
}
}
try {
- service.performAccessibilityShortcut(targetName);
+ service.performAccessibilityShortcut(displayId, shortcutType, targetName);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re);
}
@@ -1602,7 +1607,7 @@ public final class AccessibilityManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
public void enableShortcutsForTargets(boolean enable,
- @ShortcutConstants.UserShortcutType int shortcutTypes, @NonNull Set<String> targets,
+ @UserShortcutType int shortcutTypes, @NonNull Set<String> targets,
@UserIdInt int userId) {
final IAccessibilityManager service;
synchronized (mLock) {
@@ -1817,7 +1822,7 @@ public final class AccessibilityManager {
@RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
@NonNull
public List<String> getAccessibilityShortcutTargets(
- @ShortcutConstants.UserShortcutType int shortcutType) {
+ @UserShortcutType int shortcutType) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index cd1131496be0..72a1fe424906 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -92,7 +92,7 @@ interface IAccessibilityManager {
void notifyAccessibilityButtonVisibilityChanged(boolean available);
@EnforcePermission("MANAGE_ACCESSIBILITY")
- void performAccessibilityShortcut(String targetName);
+ void performAccessibilityShortcut(int displayId, int shortcutType, String targetName);
@EnforcePermission("MANAGE_ACCESSIBILITY")
List<String> getAccessibilityShortcutTargets(int shortcutType);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 9896d981c9b3..21d61845edb0 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -140,10 +140,12 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -1236,8 +1238,8 @@ public class RemoteViews implements Parcelable, Filter {
/**
* @hide
*/
- public CompletableFuture<Void> collectAllIntents() {
- return mCollectionCache.collectAllIntentsNoComplete(this);
+ public CompletableFuture<Void> collectAllIntents(int bitmapSizeLimit) {
+ return mCollectionCache.collectAllIntentsNoComplete(this, bitmapSizeLimit);
}
private class RemoteCollectionCache {
@@ -1286,7 +1288,7 @@ public class RemoteViews implements Parcelable, Filter {
}
public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete(
- @NonNull RemoteViews inViews) {
+ @NonNull RemoteViews inViews, int bitmapSizeLimit) {
SparseArray<Intent> idToIntentMapping = new SparseArray<>();
// Collect the number of uinque Intent (which is equal to the number of new connections
// to make) for size allocation and exclude certain collections from being written to
@@ -1314,7 +1316,11 @@ public class RemoteViews implements Parcelable, Filter {
? 0
: remainingSize / numOfIntents;
- return connectAllUniqueIntents(individualSize, idToIntentMapping);
+ int individualBitmapSizeLimit = (bitmapSizeLimit - getBitmapMemoryUsedByActions())
+ / numOfIntents;
+
+ return connectAllUniqueIntents(individualSize, individualBitmapSizeLimit,
+ idToIntentMapping);
}
private void collectAllIntentsInternal(@NonNull RemoteViews inViews,
@@ -1380,13 +1386,13 @@ public class RemoteViews implements Parcelable, Filter {
}
private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize,
- @NonNull SparseArray<Intent> idToIntentMapping) {
+ int individualBitmapSize, @NonNull SparseArray<Intent> idToIntentMapping) {
List<CompletableFuture<Void>> intentFutureList = new ArrayList<>();
for (int i = 0; i < idToIntentMapping.size(); i++) {
String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i));
Intent currentIntent = idToIntentMapping.valueAt(i);
intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent,
- individualSize)
+ individualSize, individualBitmapSize)
.thenAccept(items -> {
items.setHierarchyRootData(getHierarchyRootData());
mUriToCollectionMapping.put(currentIntentUri, items);
@@ -1397,7 +1403,7 @@ public class RemoteViews implements Parcelable, Filter {
}
private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
- Intent intent, int individualSize) {
+ Intent intent, int individualSize, int individualBitmapSize) {
if (intent == null) {
Log.e(LOG_TAG, "Null intent received when generating adapter future");
return CompletableFuture.completedFuture(new RemoteCollectionItems
@@ -1415,7 +1421,8 @@ public class RemoteViews implements Parcelable, Filter {
RemoteCollectionItems items;
try {
items = IRemoteViewsFactory.Stub.asInterface(iBinder)
- .getRemoteCollectionItems(individualSize);
+ .getRemoteCollectionItems(individualSize,
+ individualBitmapSize);
} catch (RemoteException re) {
items = new RemoteCollectionItems.Builder().build();
Log.e(LOG_TAG, "Error getting collection items from the"
@@ -2007,7 +2014,14 @@ public class RemoteViews implements Parcelable, Filter {
}
}
- private static class BitmapCache {
+ /**
+ * @hide
+ */
+ @NonNull BitmapCache getBitmapCache() {
+ return mBitmapCache;
+ }
+
+ static class BitmapCache {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ArrayList<Bitmap> mBitmaps;
SparseIntArray mBitmapHashes;
@@ -2029,6 +2043,11 @@ public class RemoteViews implements Parcelable, Filter {
}
}
+ BitmapCache(BitmapCache other) {
+ mBitmaps = new ArrayList<>(other.mBitmaps);
+ mBitmapHashes = other.mBitmapHashes.clone();
+ }
+
public int getBitmapId(Bitmap b) {
if (b == null) {
return -1;
@@ -2071,6 +2090,12 @@ public class RemoteViews implements Parcelable, Filter {
}
return mBitmapMemory;
}
+
+ public void mergeWithCache(BitmapCache other) {
+ for (int i = 0; i < other.mBitmaps.size(); i++) {
+ getBitmapId(other.mBitmaps.get(i));
+ }
+ }
}
private class BitmapReflectionAction extends Action {
@@ -7343,6 +7368,38 @@ public class RemoteViews implements Parcelable, Filter {
return true;
}
+ private int getBitmapMemoryUsedByActions() {
+ Set<Integer> bitmapIdSet = getBitmapIdsUsedByActions(new HashSet<>());
+ int result = 0;
+ for (int bitmapId: bitmapIdSet) {
+ result += mBitmapCache.getBitmapForId(bitmapId).getAllocationByteCount();
+ }
+
+ return result;
+ }
+
+ private Set<Integer> getBitmapIdsUsedByActions(@NonNull Set<Integer> intSet) {
+ if (hasSizedRemoteViews()) {
+ for (RemoteViews views: mSizedRemoteViews) {
+ views.getBitmapIdsUsedByActions(intSet);
+ }
+ } else if (hasLandscapeAndPortraitLayouts()) {
+ mLandscape.getBitmapIdsUsedByActions(intSet);
+ mPortrait.getBitmapIdsUsedByActions(intSet);
+ } else if (mActions != null) {
+ for (Action action: mActions) {
+ if (action instanceof ViewGroupActionAdd vgaa
+ && vgaa.mNestedViews != null) {
+ vgaa.mNestedViews.getBitmapIdsUsedByActions(intSet);
+ } else if (action instanceof BitmapReflectionAction bitmapAction) {
+ intSet.add(bitmapAction.mBitmapId);
+ }
+ }
+ }
+
+ return intSet;
+ }
+
/** Representation of a fixed list of items to be displayed in a RemoteViews collection. */
public static final class RemoteCollectionItems implements Parcelable {
private final long[] mIds;
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index c79eac605e64..4045d4275e52 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -128,7 +128,8 @@ public abstract class RemoteViewsService extends Service {
/**
* @hide
*/
- default RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
+ default RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
+ int capBitmapSize) {
RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
.Builder().build();
Parcel capSizeTestParcel = Parcel.obtain();
@@ -138,6 +139,7 @@ public abstract class RemoteViewsService extends Service {
try {
RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
new RemoteViews.RemoteCollectionItems.Builder();
+ RemoteViews.BitmapCache testBitmapCache = null;
onDataSetChanged();
itemsBuilder.setHasStableIds(hasStableIds());
@@ -150,6 +152,15 @@ public abstract class RemoteViewsService extends Service {
if (capSizeTestParcel.dataSize() > capSize) {
break;
}
+ if (testBitmapCache == null) {
+ testBitmapCache = new RemoteViews.BitmapCache(currentView.getBitmapCache());
+ } else {
+ testBitmapCache.mergeWithCache(currentView.getBitmapCache());
+ }
+ if (testBitmapCache.getBitmapMemory() >= capBitmapSize) {
+ break;
+ }
+
itemsBuilder.addItem(currentItemId, currentView);
}
@@ -266,11 +277,12 @@ public abstract class RemoteViewsService extends Service {
}
@Override
- public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
+ public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
+ int capBitmapSize) {
RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
.Builder().build();
try {
- items = mFactory.getRemoteCollectionItems(capSize);
+ items = mFactory.getRemoteCollectionItems(capSize, capBitmapSize);
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index d5398e6268dc..781a9019d1ae 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.util.SequenceUtils.getInitSeq;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
@@ -53,6 +55,9 @@ public class ClientWindowFrames implements Parcelable {
public float compatScale = 1f;
+ /** To make sure the info update between client and system server is in order. */
+ public int seq = getInitSeq();
+
public ClientWindowFrames() {
}
@@ -74,6 +79,7 @@ public class ClientWindowFrames implements Parcelable {
}
isParentFrameClippedByDisplayCutout = other.isParentFrameClippedByDisplayCutout;
compatScale = other.compatScale;
+ seq = other.seq;
}
/** Needed for AIDL out parameters. */
@@ -84,6 +90,7 @@ public class ClientWindowFrames implements Parcelable {
attachedFrame = in.readTypedObject(Rect.CREATOR);
isParentFrameClippedByDisplayCutout = in.readBoolean();
compatScale = in.readFloat();
+ seq = in.readInt();
}
@Override
@@ -94,6 +101,7 @@ public class ClientWindowFrames implements Parcelable {
dest.writeTypedObject(attachedFrame, flags);
dest.writeBoolean(isParentFrameClippedByDisplayCutout);
dest.writeFloat(compatScale);
+ dest.writeInt(seq);
}
@Override
@@ -116,6 +124,7 @@ public class ClientWindowFrames implements Parcelable {
return false;
}
final ClientWindowFrames other = (ClientWindowFrames) o;
+ // seq is for internal bookkeeping only.
return frame.equals(other.frame)
&& displayFrame.equals(other.displayFrame)
&& parentFrame.equals(other.parentFrame)
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 985dc102b31b..b5bc572724b7 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -53,17 +53,6 @@ flag {
}
flag {
- name: "remove_prepare_surface_in_placement"
- namespace: "windowing_frontend"
- description: "Reduce unnecessary invocation to improve performance"
- bug: "330721336"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "close_to_square_config_includes_status_bar"
namespace: "windowing_frontend"
description: "On close to square display, when necessary, configuration includes status bar"
@@ -146,6 +135,13 @@ flag {
}
flag {
+ name: "process_priority_policy_for_multi_window_mode"
+ namespace: "windowing_frontend"
+ description: "Use higher priority for top-like processes"
+ bug: "200769420"
+}
+
+flag {
name: "insets_decoupled_configuration"
namespace: "windowing_frontend"
description: "Configuration decoupled from insets"
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
index 66faa318666d..ec90dd242c36 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
@@ -36,6 +36,7 @@ import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
+import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Set;
@@ -67,6 +68,10 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS
public AccessibilityTarget(Context context, @UserShortcutType int shortcutType,
@AccessibilityFragmentType int fragmentType, boolean isShortcutSwitched, String id,
int uid, CharSequence label, Drawable icon, String key) {
+ if (!isRecognizedShortcutType(shortcutType)) {
+ throw new IllegalArgumentException(
+ "Unexpected shortcut type " + ShortcutUtils.convertToKey(shortcutType));
+ }
mContext = context;
mShortcutType = shortcutType;
mFragmentType = fragmentType;
@@ -97,20 +102,15 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS
holder.mStatusView.setVisibility(View.GONE);
}
+ @SuppressLint("MissingPermission")
@Override
public void onSelected() {
final AccessibilityManager am =
getContext().getSystemService(AccessibilityManager.class);
- switch (getShortcutType()) {
- case SOFTWARE:
- am.notifyAccessibilityButtonClicked(getContext().getDisplayId(), getId());
- return;
- case HARDWARE:
- am.performAccessibilityShortcut(getId());
- return;
- default:
- throw new IllegalStateException("Unexpected shortcut type");
+ if (am == null) {
+ return;
}
+ am.performAccessibilityShortcut(getContext().getDisplayId(), mShortcutType, getId());
}
@SuppressLint("MissingPermission")
@@ -188,4 +188,15 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS
public String getKey() {
return mKey;
}
+
+ /**
+ * Determines if the provided shortcut type is valid for use with AccessibilityTargets.
+ * @param shortcutType shortcut type to check.
+ * @return {@code true} if the shortcut type can be used, {@code false} otherwise.
+ */
+ @VisibleForTesting
+ public static boolean isRecognizedShortcutType(@UserShortcutType int shortcutType) {
+ int mask = SOFTWARE | HARDWARE;
+ return (shortcutType != 0 && (shortcutType & mask) == shortcutType);
+ }
}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index e523ab066074..4ccdf79da358 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -41,6 +41,7 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.R;
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
@@ -159,21 +160,32 @@ public final class AccessibilityTargetHelper {
final List<AccessibilityTarget> targets = new ArrayList<>(installedServices.size());
for (AccessibilityServiceInfo info : installedServices) {
- final int targetSdk =
- info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion;
- final boolean hasRequestAccessibilityButtonFlag =
- (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
- if ((targetSdk <= Build.VERSION_CODES.Q) && !hasRequestAccessibilityButtonFlag
- && (shortcutType == SOFTWARE)) {
- continue;
+ if (isValidServiceTarget(info, shortcutType)) {
+ targets.add(createAccessibilityServiceTarget(context, shortcutType, info));
}
-
- targets.add(createAccessibilityServiceTarget(context, shortcutType, info));
}
return targets;
}
+ /**
+ * Check for maintaining compatibility on prior versions.
+ * Determines if a given service should be accumulated in a list of installed services.
+ * @param info service info to check.
+ * @param shortcutType type of shortcut to accumulate a list for.
+ * @return {@code true} if the service should be added (always true past version Q),
+ * otherwise {@code false}.
+ */
+ @VisibleForTesting
+ public static boolean isValidServiceTarget(
+ AccessibilityServiceInfo info, @UserShortcutType int shortcutType) {
+ final boolean hasRequestAccessibilityButtonFlag =
+ (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ return (info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
+ > Build.VERSION_CODES.Q) || hasRequestAccessibilityButtonFlag
+ || shortcutType != SOFTWARE;
+ }
+
private static List<AccessibilityTarget> getAccessibilityActivityTargets(Context context,
@UserShortcutType int shortcutType) {
final AccessibilityManager am = (AccessibilityManager) context.getSystemService(
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index a7aef92b2f96..6b0ca9faa2a4 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -181,6 +181,27 @@ public final class ShortcutUtils {
}
/**
+ * Converts {@link Settings.Secure} key to {@link UserShortcutType}.
+ *
+ * @param key The shortcut key in Settings.
+ * @return The mapped type
+ */
+ @UserShortcutType
+ public static int convertToType(String key) {
+ return switch (key) {
+ case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> UserShortcutType.SOFTWARE;
+ case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> UserShortcutType.QUICK_SETTINGS;
+ case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> UserShortcutType.HARDWARE;
+ case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED ->
+ UserShortcutType.TRIPLETAP;
+ case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED ->
+ UserShortcutType.TWOFINGER_DOUBLETAP;
+ default -> throw new IllegalArgumentException(
+ "Unsupported user shortcut key: " + key);
+ };
+ }
+
+ /**
* Updates an accessibility state if the accessibility service is a Always-On a11y service,
* a.k.a. AccessibilityServices that has FLAG_REQUEST_ACCESSIBILITY_BUTTON
* <p>
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index e55cdef82235..b5930e1df340 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -72,6 +72,7 @@ interface IAppWidgetService {
boolean bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent,
IApplicationThread caller, IBinder token, IServiceConnection connection, long flags);
void notifyProviderInheritance(in ComponentName[] componentNames);
+ int getMaxBitmapMemory();
@UnsupportedAppUsage
int[] getAppWidgetIds(in ComponentName providerComponent);
diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
index 507b673ec279..3fd51206e891 100644
--- a/core/java/com/android/internal/view/SurfaceCallbackHelper.java
+++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
@@ -16,11 +16,13 @@
package com.android.internal.view;
-import android.os.RemoteException;
-import android.view.Surface;
+import android.util.EventLog;
import android.view.SurfaceHolder;
public class SurfaceCallbackHelper {
+ private static final int LOGTAG_SURFACEVIEW_CALLBACK = 60006;
+ private final String mTag;
+ private boolean mSurfaceRedrawImplemented;
Runnable mRunnable;
int mFinishDrawingCollected = 0;
@@ -35,12 +37,23 @@ public class SurfaceCallbackHelper {
return;
}
mRunnable.run();
+ if (mSurfaceRedrawImplemented && mTag != null) {
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_CALLBACK, mTag,
+ "surfaceRedrawNeeded implemented");
+ }
}
}
};
public SurfaceCallbackHelper(Runnable callbacksCollected) {
+ // skip logging surfaceRedrawNeeded calls
+ this(callbacksCollected, null);
+ }
+
+ public SurfaceCallbackHelper(Runnable callbacksCollected, String tag) {
mRunnable = callbacksCollected;
+ mTag = tag;
+ mSurfaceRedrawImplemented = false;
}
public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) {
@@ -58,6 +71,7 @@ public class SurfaceCallbackHelper {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2) c).surfaceRedrawNeededAsync(
holder, mFinishDrawingRunnable);
+ mSurfaceRedrawImplemented = true;
} else {
mFinishDrawingRunnable.run();
}
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
index 1d0e97295571..7e8724da6279 100644
--- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -39,6 +39,6 @@ interface IRemoteViewsFactory {
boolean hasStableIds();
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean isCreated();
- RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize);
+ RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize, int capBitmapSize);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index e21d1dfac865..011ef30c7466 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -111,6 +111,7 @@ cc_library_shared_for_libandroid_runtime {
"libminikin",
"libz",
"server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
"android.database.sqlite-aconfig-cc",
"android.media.audiopolicy-aconfig-cc",
],
@@ -195,6 +196,7 @@ cc_library_shared_for_libandroid_runtime {
"android_os_PerformanceHintManager.cpp",
"android_os_SELinux.cpp",
"android_os_ServiceManager.cpp",
+ "android_os_ServiceManagerNative.cpp",
"android_os_SharedMemory.cpp",
"android_os_storage_StorageManager.cpp",
"android_os_UEventObserver.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 71d041c11980..ed59327ff8e9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -148,6 +148,7 @@ extern int register_android_os_HwParcel(JNIEnv *env);
extern int register_android_os_HwRemoteBinder(JNIEnv *env);
extern int register_android_os_NativeHandle(JNIEnv *env);
extern int register_android_os_ServiceManager(JNIEnv *env);
+extern int register_android_os_ServiceManagerNative(JNIEnv* env);
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(JNIEnv* env);
extern int register_android_os_PerformanceHintManager(JNIEnv* env);
@@ -1544,6 +1545,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_HwRemoteBinder),
REG_JNI(register_android_os_NativeHandle),
REG_JNI(register_android_os_ServiceManager),
+ REG_JNI(register_android_os_ServiceManagerNative),
REG_JNI(register_android_os_storage_StorageManager),
REG_JNI(register_android_service_DataLoaderService),
REG_JNI(register_android_view_DisplayEventReceiver),
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index 8f7026859898..7410468199b8 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -134,10 +134,15 @@ static jlong nativeOpen(JNIEnv* env, jclass clazz, jstring pathStr, jint openFla
String8 label(labelChars);
env->ReleaseStringUTFChars(labelStr, labelChars);
+ errno = 0;
sqlite3* db;
int err = sqlite3_open_v2(path.c_str(), &db, sqliteFlags, NULL);
if (err != SQLITE_OK) {
- throw_sqlite3_exception_errcode(env, err, "Could not open database");
+ if (errno == 0) {
+ throw_sqlite3_exception(env, db, "could not open database");
+ } else {
+ throw_sqlite3_exception(env, db, strerror(errno));
+ }
return 0;
}
diff --git a/core/jni/android_os_ServiceManagerNative.cpp b/core/jni/android_os_ServiceManagerNative.cpp
new file mode 100644
index 000000000000..39a3013384ee
--- /dev/null
+++ b/core/jni/android_os_ServiceManagerNative.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <binder/IServiceManagerFFI.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+
+#include "android_util_Binder.h"
+namespace android {
+
+jobject JNICALL Java_android_os_ServiceManagerProxy_getNativeServiceManager(JNIEnv *env,
+ jobject obj) {
+ sp<IBinder> service = IInterface::asBinder(
+ impl::getJavaServicemanagerImplPrivateDoNotUseExceptInTheOnePlaceItIsUsed());
+ return javaObjectForIBinder(env, service);
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod serviceManagerNativeMethods[] = {
+ /* name, signature, funcPtr */
+ {"getNativeServiceManager", "()Landroid/os/IBinder;",
+ (void *)Java_android_os_ServiceManagerProxy_getNativeServiceManager}};
+
+int register_android_os_ServiceManagerNative(JNIEnv *env) {
+ return jniRegisterNativeMethods(env, "android/os/ServiceManagerProxy",
+ serviceManagerNativeMethods,
+ NELEM(serviceManagerNativeMethods));
+}
+} // namespace android \ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c71f9bde6bf1..6c6add9eb22a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8222,6 +8222,19 @@
<permission android:name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD"
android:protectionLevel="internal|appop" />
+ <!-- @hide @SystemApi
+ @FlaggedApi(android.os.Flags.FLAG_ALLOW_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT)
+ Allows system applications to capture bugreport directly without consent dialog when using
+ the bugreporting API, and instead use the applications-owned consent page.
+ <p>The application still needs to hold {@link android.permission.DUMP} permission and be
+ bugreport-whitelisted to be able to capture a bugreport using the bugreporting API in the
+ first place.
+ <p>Protection level: signature|privileged
+ <p>Not for use by third-party applications. -->
+ <permission
+ android:name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows to call APIs that log process lifecycle events
@hide -->
<permission android:name="android.permission.LOG_FOREGROUND_RESOURCE_USE"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9846b710300f..488382783331 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1361,7 +1361,7 @@
<!-- @hide -->
<attr name="customColorThemeAppRing" format="color"/>
<!-- @hide -->
- <attr name="customColorOnThemeAppRing" format="color"/>
+ <attr name="customColorThemeNotif" format="color"/>
<!-- @hide -->
<attr name="customColorBrandA" format="color"/>
<!-- @hide -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 5e039b5e958d..37412a0e12b8 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -590,7 +590,7 @@
<color name="system_theme_app_light">#2F4578</color>
<color name="system_on_theme_app_light">#D6DFFF</color>
<color name="system_theme_app_ring_light">#94AAE4</color>
- <color name="system_on_theme_app_ring_light">#FDD7FA</color>
+ <color name="system_theme_notif_light">#FDD7FA</color>
<color name="system_brand_a_light">#3A5084</color>
<color name="system_brand_b_light">#6E7488</color>
<color name="system_brand_c_light">#6076AC</color>
@@ -611,7 +611,7 @@
<color name="system_theme_app_dark">#D9E2FF</color>
<color name="system_on_theme_app_dark">#304679</color>
<color name="system_theme_app_ring_dark">#94AAE4</color>
- <color name="system_on_theme_app_ring_dark">#E0BBDD</color>
+ <color name="system_theme_notif_dark">#E0BBDD</color>
<color name="system_brand_a_dark">#90A6DF</color>
<color name="system_brand_b_dark">#A4ABC1</color>
<color name="system_brand_c_dark">#7A90C8</color>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fe310b311e1c..fa93e763c45b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6523,4 +6523,15 @@ ul.</string>
<string name="biometric_dangling_notification_action_set_up">Set up</string>
<!-- Biometric dangling notification "Not now" action button -->
<string name="biometric_dangling_notification_action_not_now">Not now</string>
+
+
+ <!-- Background user sound notification related messages -->
+ <!-- Notification title when sound comes from an alarm or timer on background user [CHAR LIMIT=NOTIF_TITLE]-->
+ <string name="bg_user_sound_notification_title_alarm">Alarm for <xliff:g id="user_name" example="John Doe">%s</xliff:g></string>
+ <!-- Notification action button to prompt user switch to the background user [CHAR LIMIT=NONE] -->
+ <string name="bg_user_sound_notification_button_switch_user">Switch user</string>
+ <!-- Notification action button to mute the sound from the background user [CHAR LIMIT=NONE] -->
+ <string name="bg_user_sound_notification_button_mute">Mute</string>
+ <!-- Notification text to mute the sound from the background user [CHAR LIMIT=NOTIF_BODY]-->
+ <string name="bg_user_sound_notification_message">Tap to mute sound</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8823894e18c8..bdcf13c24798 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5307,7 +5307,7 @@
<java-symbol name="customColorThemeApp" type="attr"/>
<java-symbol name="customColorOnThemeApp" type="attr"/>
<java-symbol name="customColorThemeAppRing" type="attr"/>
- <java-symbol name="customColorOnThemeAppRing" type="attr"/>
+ <java-symbol name="customColorThemeNotif" type="attr"/>
<java-symbol name="customColorBrandA" type="attr"/>
<java-symbol name="customColorBrandB" type="attr"/>
<java-symbol name="customColorBrandC" type="attr"/>
@@ -5329,7 +5329,7 @@
<java-symbol name="system_theme_app_light" type="color"/>
<java-symbol name="system_on_theme_app_light" type="color"/>
<java-symbol name="system_theme_app_ring_light" type="color"/>
- <java-symbol name="system_on_theme_app_ring_light" type="color"/>
+ <java-symbol name="system_theme_notif_light" type="color"/>
<java-symbol name="system_brand_a_light" type="color"/>
<java-symbol name="system_brand_b_light" type="color"/>
<java-symbol name="system_brand_c_light" type="color"/>
@@ -5350,7 +5350,7 @@
<java-symbol name="system_theme_app_dark" type="color"/>
<java-symbol name="system_on_theme_app_dark" type="color"/>
<java-symbol name="system_theme_app_ring_dark" type="color"/>
- <java-symbol name="system_on_theme_app_ring_dark" type="color"/>
+ <java-symbol name="system_theme_notif_dark" type="color"/>
<java-symbol name="system_brand_a_dark" type="color"/>
<java-symbol name="system_brand_b_dark" type="color"/>
<java-symbol name="system_brand_c_dark" type="color"/>
@@ -5540,4 +5540,10 @@
<java-symbol type="drawable" name="ic_zen_mode_type_schedule_time" />
<java-symbol type="drawable" name="ic_zen_mode_type_theater" />
<java-symbol type="drawable" name="ic_zen_mode_type_unknown" />
+
+ <!-- System notification for background user sound -->
+ <java-symbol type="string" name="bg_user_sound_notification_title_alarm" />
+ <java-symbol type="string" name="bg_user_sound_notification_button_switch_user" />
+ <java-symbol type="string" name="bg_user_sound_notification_button_mute" />
+ <java-symbol type="string" name="bg_user_sound_notification_message" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 24d493867906..382ff0441fd2 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -292,7 +292,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -410,7 +410,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -527,7 +527,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -646,7 +646,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -764,7 +764,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -890,7 +890,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1007,7 +1007,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1123,7 +1123,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1240,7 +1240,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1373,7 +1373,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1491,7 +1491,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1607,7 +1607,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1725,7 +1725,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1842,7 +1842,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1959,7 +1959,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -2076,7 +2076,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -2193,7 +2193,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -2310,7 +2310,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -2432,7 +2432,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -2547,7 +2547,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -2800,7 +2800,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -2917,7 +2917,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3033,7 +3033,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3150,7 +3150,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3269,7 +3269,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3387,7 +3387,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3511,7 +3511,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3631,7 +3631,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3750,7 +3750,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3870,7 +3870,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3971,7 +3971,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4072,7 +4072,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4192,7 +4192,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4313,7 +4313,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4432,7 +4432,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4550,7 +4550,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4667,7 +4667,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4784,7 +4784,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4899,7 +4899,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5022,7 +5022,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5126,7 +5126,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5222,7 +5222,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5339,7 +5339,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -5440,7 +5440,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5581,7 +5581,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5700,7 +5700,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5845,7 +5845,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -5920,7 +5920,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5999,7 +5999,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -6074,7 +6074,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -6160,7 +6160,7 @@ easier.
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
index f3d4ccfcb4d7..3fb1554fe3a7 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
@@ -15,6 +15,7 @@
*/
package com.android.server.broadcastradio.aidl;
+import android.annotation.Nullable;
import android.hardware.broadcastradio.IdentifierType;
import android.hardware.broadcastradio.Metadata;
import android.hardware.broadcastradio.ProgramIdentifier;
@@ -110,20 +111,25 @@ final class AidlTestUtils {
static ProgramInfo makeHalProgramInfo(
android.hardware.broadcastradio.ProgramSelector hwSel,
- ProgramIdentifier logicallyTunedTo, ProgramIdentifier physicallyTunedTo,
- int hwSignalQuality) {
+ @Nullable ProgramIdentifier logicallyTunedTo,
+ @Nullable ProgramIdentifier physicallyTunedTo, int hwSignalQuality) {
return makeHalProgramInfo(hwSel, logicallyTunedTo, physicallyTunedTo, hwSignalQuality,
new ProgramIdentifier[]{}, new Metadata[]{});
}
static ProgramInfo makeHalProgramInfo(
android.hardware.broadcastradio.ProgramSelector hwSel,
- ProgramIdentifier logicallyTunedTo, ProgramIdentifier physicallyTunedTo,
- int hwSignalQuality, ProgramIdentifier[] relatedContent, Metadata[] metadata) {
+ @Nullable ProgramIdentifier logicallyTunedTo,
+ @Nullable ProgramIdentifier physicallyTunedTo, int hwSignalQuality,
+ ProgramIdentifier[] relatedContent, Metadata[] metadata) {
ProgramInfo hwInfo = new ProgramInfo();
hwInfo.selector = hwSel;
- hwInfo.logicallyTunedTo = logicallyTunedTo;
- hwInfo.physicallyTunedTo = physicallyTunedTo;
+ if (logicallyTunedTo != null) {
+ hwInfo.logicallyTunedTo = logicallyTunedTo;
+ }
+ if (physicallyTunedTo != null) {
+ hwInfo.physicallyTunedTo = physicallyTunedTo;
+ }
hwInfo.signalQuality = hwSignalQuality;
hwInfo.relatedContent = relatedContent;
hwInfo.metadata = metadata;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index 5a08acdf6271..bdc4d2540bae 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -562,10 +562,11 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
@Test
public void programInfoFromHalProgramInfo_withInvalidDabProgramInfo() {
android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
- AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID,
- new ProgramIdentifier[]{TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
+ AidlTestUtils.makeHalSelector(TEST_HAL_DAB_ENSEMBLE_ID,
+ new ProgramIdentifier[]{TEST_HAL_DAB_FREQUENCY_ID});
ProgramInfo halProgramInfo = AidlTestUtils.makeHalProgramInfo(invalidHalDabSelector,
- TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_SIGNAL_QUALITY);
+ /* logicallyTunedTo= */ null, /* physicallyTunedTo= */ null,
+ TEST_SIGNAL_QUALITY);
RadioManager.ProgramInfo programInfo =
ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
@@ -575,6 +576,21 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
}
@Test
+ public void tunedProgramInfoFromHalProgramInfo_withInvalidDabProgramInfo() {
+ android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
+ AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
+ TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
+ ProgramInfo halProgramInfo = AidlTestUtils.makeHalProgramInfo(invalidHalDabSelector,
+ TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_SIGNAL_QUALITY);
+
+ RadioManager.ProgramInfo programInfo =
+ ConversionUtils.tunedProgramInfoFromHalProgramInfo(halProgramInfo);
+
+ expect.withMessage("Invalid DAB program info with incorrect type of physically tuned to id")
+ .that(programInfo).isNull();
+ }
+
+ @Test
public void programSelectorMeetsSdkVersionRequirement_withLowerVersionPrimaryId_returnsFalse() {
expect.withMessage("Selector %s with primary id requiring higher-version SDK version",
TEST_DAB_SELECTOR).that(ConversionUtils
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
index d64fcaf865f2..b18567192657 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
@@ -189,13 +189,16 @@ public class ProgramInfoCacheTest {
@Test
public void updateFromHalProgramListChunk_withInvalidChunk() {
- RadioManager.ProgramInfo invalidDabInfo = AidlTestUtils.makeProgramInfo(TEST_DAB_SELECTOR,
- TEST_DAB_DMB_SID_EXT_ID, TEST_DAB_ENSEMBLE_ID, TEST_SIGNAL_QUALITY);
+ ProgramInfo invalidHalDabInfo = AidlTestUtils.makeHalProgramInfo(
+ AidlTestUtils.makeHalSelector(
+ ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_ENSEMBLE_ID),
+ new ProgramIdentifier[]{}), /* logicallyTunedTo= */ null,
+ /* physicallyTunedTo= */ null, TEST_SIGNAL_QUALITY);
ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
/* complete= */ false);
ProgramListChunk chunk = AidlTestUtils.makeHalChunk(/* purge= */ false,
- /* complete= */ true, new ProgramInfo[]{AidlTestUtils.programInfoToHalProgramInfo(
- invalidDabInfo)}, new ProgramIdentifier[]{});
+ /* complete= */ true, new ProgramInfo[]{invalidHalDabInfo},
+ new ProgramIdentifier[]{});
cache.updateFromHalProgramListChunk(chunk);
@@ -447,10 +450,10 @@ public class ProgramInfoCacheTest {
/* complete= */ false, TEST_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO);
ProgramInfo[] halModified = new android.hardware.broadcastradio.ProgramInfo[1];
halModified[0] = AidlTestUtils.makeHalProgramInfo(
- ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR_ALTERNATIVE),
- ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
- ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
- TEST_SIGNAL_QUALITY);
+ AidlTestUtils.makeHalSelector(
+ ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_ENSEMBLE_ID),
+ new ProgramIdentifier[]{}), /* logicallyTunedTo= */ null,
+ /* physicallyTunedTo= */ null, TEST_SIGNAL_QUALITY);
ProgramIdentifier[] halRemoved = new android.hardware.broadcastradio.ProgramIdentifier[1];
halRemoved[0] = new android.hardware.broadcastradio.ProgramIdentifier();
ProgramListChunk halChunk = AidlTestUtils.makeHalChunk(/* purge= */ false,
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 3104f16bf5a1..0b270d485b97 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -20,8 +20,6 @@ import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -104,8 +102,6 @@ public class ClientTransactionListenerControllerTest {
@Before
public void setup() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
MockitoAnnotations.initMocks(this);
mDisplayManager = new DisplayManagerGlobal(mIDisplayManager);
mHandler = getInstrumentation().getContext().getMainThreadHandler();
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index 527241613a7a..79659cafe5ab 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -16,22 +16,16 @@
package android.app.servertransaction;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
-
-import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
-
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.ClientTransactionHandler;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,24 +43,8 @@ import org.junit.runner.RunWith;
@Presubmit
public class ClientTransactionTests {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
@Test
public void testPreExecute() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testPreExecuteInner();
- }
-
- @Test
- public void testPreExecute_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testPreExecuteInner();
- }
-
- private void testPreExecuteInner() {
final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 935bc7565986..73b7447a31c0 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -25,9 +25,6 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
-
-import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -54,14 +51,12 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@@ -88,9 +83,6 @@ import java.util.stream.Collectors;
@Presubmit
public class TransactionExecutorTests {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
@Mock
private ClientTransactionHandler mTransactionHandler;
@Mock
@@ -248,19 +240,6 @@ public class TransactionExecutorTests {
@Test
public void testTransactionResolution() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testTransactionResolutionInner();
- }
-
- @Test
- public void testTransactionResolution_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testTransactionResolutionInner();
- }
-
- private void testTransactionResolutionInner() {
ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
@@ -284,19 +263,6 @@ public class TransactionExecutorTests {
@Test
public void testDoNotLaunchDestroyedActivity() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testDoNotLaunchDestroyedActivityInner();
- }
-
- @Test
- public void testDoNotLaunchDestroyedActivity_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testDoNotLaunchDestroyedActivityInner();
- }
-
- private void testDoNotLaunchDestroyedActivityInner() {
final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
// Assume launch transaction is still in queue, so there is no client record.
@@ -329,19 +295,6 @@ public class TransactionExecutorTests {
@Test
public void testActivityResultRequiredStateResolution() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityResultRequiredStateResolutionInner();
- }
-
- @Test
- public void testActivityResultRequiredStateResolution_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityResultRequiredStateResolutionInner();
- }
-
- private void testActivityResultRequiredStateResolutionInner() {
when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
PostExecItem postExecItem = new PostExecItem(ON_RESUME);
@@ -495,19 +448,6 @@ public class TransactionExecutorTests {
@Test(expected = IllegalArgumentException.class)
public void testActivityItemNullRecordThrowsException() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityItemNullRecordThrowsExceptionInner();
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testActivityItemNullRecordThrowsException_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityItemNullRecordThrowsExceptionInner();
- }
-
- private void testActivityItemNullRecordThrowsExceptionInner() {
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
final IBinder token = mock(IBinder.class);
@@ -520,19 +460,6 @@ public class TransactionExecutorTests {
@Test
public void testActivityItemExecute() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityItemExecuteInner();
- }
-
- @Test
- public void testActivityItemExecute_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityItemExecuteInner();
- }
-
- private void testActivityItemExecuteInner() {
final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index d451fe58e8e4..a4d76618fecc 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -20,9 +20,6 @@ import static android.app.servertransaction.TestUtils.config;
import static android.app.servertransaction.TestUtils.mergedConfig;
import static android.app.servertransaction.TestUtils.referrerIntentList;
import static android.app.servertransaction.TestUtils.resultInfoList;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
-
-import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.junit.Assert.assertEquals;
@@ -40,14 +37,12 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.window.ActivityWindowInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,9 +62,6 @@ import java.util.ArrayList;
@Presubmit
public class TransactionParcelTests {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
private Parcel mParcel;
private IBinder mActivityToken;
@@ -296,8 +288,6 @@ public class TransactionParcelTests {
@Test
public void testClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
// Write to parcel
NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
@@ -320,49 +310,6 @@ public class TransactionParcelTests {
assertEquals(mActivityToken, result.getActivityToken());
}
- @Test
- public void testClientTransactionCallbacksOnly() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- // Write to parcel
- NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
- ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config(), new ActivityWindowInfo());
-
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addTransactionItem(callback1);
- transaction.addTransactionItem(callback2);
-
- writeAndPrepareForReading(transaction);
-
- // Read from parcel and assert
- ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
-
- assertEquals(transaction.hashCode(), result.hashCode());
- assertEquals(transaction, result);
- assertEquals(mActivityToken, result.getActivityToken());
- }
-
- @Test
- public void testClientTransactionLifecycleOnly() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- // Write to parcel
- StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken);
-
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addTransactionItem(lifecycleRequest);
-
- writeAndPrepareForReading(transaction);
-
- // Read from parcel and assert
- ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
-
- assertEquals(transaction.hashCode(), result.hashCode());
- assertEquals(transaction, result);
- assertEquals(mActivityToken, result.getActivityToken());
- }
-
/** Write to {@link #mParcel} and reset its position to prepare for reading from the start. */
private void writeAndPrepareForReading(Parcelable parcelable) {
parcelable.writeToParcel(mParcel, 0 /* flags */);
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 7b70b412e62b..c8015d43b404 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -87,7 +87,7 @@ public class ContentResolverTest {
bitmap.compress(Bitmap.CompressFormat.PNG, 90, mImage.getOutputStream());
final AssetFileDescriptor afd = new AssetFileDescriptor(
- new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null);
+ ParcelFileDescriptor.dup(mImage.getFileDescriptor()), 0, mSize, null);
when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(
afd);
}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java
new file mode 100644
index 000000000000..09c380a49920
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteCantOpenDatabaseException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.OpenParams;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SQLiteCantOpenDatabaseExceptionTest {
+ private static final String TAG = "SQLiteCantOpenDatabaseExceptionTest";
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+ private File getDatabaseFile(String name) {
+ mContext.deleteDatabase(name);
+
+ // getDatabasePath() doesn't like a filename with a / in it, so we can't use it directly.
+ return new File(mContext.getDatabasePath("a").getParentFile(), name);
+ }
+
+ private void callWithExpectedMessage(File file, String expectedMessagePattern) {
+ try {
+ SQLiteDatabase.openDatabase(file, new OpenParams.Builder().build());
+ fail("SQLiteCantOpenDatabaseException was not thrown");
+ } catch (SQLiteCantOpenDatabaseException e) {
+ Log.i(TAG, "Caught expected exception: " + e.getMessage());
+ assertTrue(e.getMessage().matches(expectedMessagePattern));
+ }
+ }
+
+ /** DB's directory doesn't exist. */
+ @Test
+ public void testDirectoryDoesNotExist() {
+ final File file = getDatabaseFile("nonexisitentdir/mydb.db");
+
+ callWithExpectedMessage(file, ".*: Directory .* doesn't exist");
+ }
+
+ /** File doesn't exist */
+ @Test
+ public void testFileDoesNotExist() {
+ final File file = getDatabaseFile("mydb.db");
+
+ callWithExpectedMessage(file, ".*: File .* doesn't exist");
+ }
+
+ /** File exists, but not readable. */
+ @Test
+ public void testFileNotReadable() {
+ final File file = getDatabaseFile("mydb.db");
+
+ SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(file, null);
+ db.close();
+
+ file.setReadable(false);
+
+ callWithExpectedMessage(file, ".*: File .* not readable");
+ }
+
+ /** Directory with the given name exists already. */
+ @Test
+ public void testPathIsADirectory() {
+ final File file = getDatabaseFile("mydb.db");
+
+ file.mkdirs();
+
+ callWithExpectedMessage(file, ".*: Path .* is a directory");
+ }
+}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
index 8071d3dff619..24d27c484912 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
@@ -27,7 +27,6 @@ import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.os.SystemClock;
-import android.test.AndroidTestCase;
import android.util.Log;
import androidx.test.filters.SmallTest;
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index 851e61259241..8cd6773936ef 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -16,7 +16,6 @@
package android.os;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.MediumTest;
@@ -154,7 +153,6 @@ public class MessageQueueTest {
@Test
@MediumTest
- @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testFieldIntegrity() throws Exception {
TestHandlerThread tester = new TestFieldIntegrityHandler() {
diff --git a/core/tests/coretests/src/android/util/SequenceUtilsTest.java b/core/tests/coretests/src/android/util/SequenceUtilsTest.java
new file mode 100644
index 000000000000..020520dbcf85
--- /dev/null
+++ b/core/tests/coretests/src/android/util/SequenceUtilsTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+
+import static android.util.SequenceUtils.getInitSeq;
+import static android.util.SequenceUtils.getNextSeq;
+import static android.util.SequenceUtils.isIncomingSeqNewer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for subtypes of {@link SequenceUtils}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:SequenceUtilsTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+@DisabledOnRavenwood(blockedBy = SequenceUtils.class)
+public class SequenceUtilsTest {
+
+ // This is needed to disable the test in Ravenwood test, because SequenceUtils hasn't opted in
+ // for Ravenwood, which is still in experiment.
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Test
+ public void testNextSeq() {
+ assertEquals(getInitSeq() + 1, getNextSeq(getInitSeq()));
+ assertEquals(getInitSeq() + 1, getNextSeq(Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void testIsIncomingSeqNewer() {
+ assertTrue(isIncomingSeqNewer(getInitSeq() + 1, getInitSeq() + 10));
+ assertFalse(isIncomingSeqNewer(getInitSeq() + 10, getInitSeq() + 1));
+ assertTrue(isIncomingSeqNewer(-100, 100));
+ assertFalse(isIncomingSeqNewer(100, -100));
+ assertTrue(isIncomingSeqNewer(1, 2));
+ assertFalse(isIncomingSeqNewer(2, 1));
+
+ // Possible incoming seq are all newer than the initial seq.
+ assertTrue(isIncomingSeqNewer(getInitSeq(), getInitSeq() + 1));
+ assertTrue(isIncomingSeqNewer(getInitSeq(), -100));
+ assertTrue(isIncomingSeqNewer(getInitSeq(), 0));
+ assertTrue(isIncomingSeqNewer(getInitSeq(), 100));
+ assertTrue(isIncomingSeqNewer(getInitSeq(), Integer.MAX_VALUE));
+ assertTrue(isIncomingSeqNewer(getInitSeq(), getNextSeq(Integer.MAX_VALUE)));
+
+ // False for the same seq.
+ assertFalse(isIncomingSeqNewer(getInitSeq(), getInitSeq()));
+ assertFalse(isIncomingSeqNewer(100, 100));
+ assertFalse(isIncomingSeqNewer(Integer.MAX_VALUE, Integer.MAX_VALUE));
+
+ // True when there is a large jump (overflow).
+ assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getInitSeq() + 1));
+ assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getInitSeq() + 100));
+ assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getNextSeq(Integer.MAX_VALUE)));
+ }
+}
diff --git a/core/tests/coretests/src/android/util/SparseSetArrayTest.java b/core/tests/coretests/src/android/util/SparseSetArrayTest.java
index 1c72185ea93c..a8dce7032fa3 100644
--- a/core/tests/coretests/src/android/util/SparseSetArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseSetArrayTest.java
@@ -17,7 +17,6 @@ package android.util;
import static com.google.common.truth.Truth.assertThat;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
@@ -37,7 +36,6 @@ public class SparseSetArrayTest {
public final RavenwoodRule mRavenwood = new RavenwoodRule();
@Test
- @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testAddAll() {
final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>();
@@ -59,7 +57,6 @@ public class SparseSetArrayTest {
}
@Test
- @IgnoreUnderRavenwood(reason = "b/315036461")
public void testCopyConstructor() {
final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>();
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 169300a6b81c..da9a6e1bcfe8 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -21,6 +21,7 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
@@ -121,6 +122,52 @@ public class ViewFrameRateTest {
waitForAfterDraw();
}
+ @Test
+ @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
+ public void inputMethodWithContentMoves() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+ // update the window type to TYPE_INPUT_METHOD
+ int windowType = mViewRoot.mWindowAttributes.type;
+ final WindowManager.LayoutParams attrs = mViewRoot.mWindowAttributes;
+ attrs.type = TYPE_INPUT_METHOD;
+ instrumentation.runOnMainSync(() -> {
+ mViewRoot.setLayoutParams(attrs, false);
+ });
+ instrumentation.waitForIdleSync();
+
+ final WindowManager.LayoutParams newAttrs = mViewRoot.mWindowAttributes;
+ assertTrue(newAttrs.type == TYPE_INPUT_METHOD);
+
+ waitForFrameRateCategoryToSettle();
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.offsetLeftAndRight(100);
+ runAfterDraw(() -> {
+ if (toolkitFrameRateVelocityMappingReadOnly()) {
+ float frameRate = mViewRoot.getLastPreferredFrameRate();
+ // frame rate shouldn't be boost with TYPE_INPUT_METHOD window type
+ assertTrue(frameRate == 0);
+ } else {
+ assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+ });
+ });
+ waitForAfterDraw();
+
+ // Reset the window type back to the original one.
+ newAttrs.type = windowType;
+ instrumentation.runOnMainSync(() -> {
+ mViewRoot.setLayoutParams(newAttrs, false);
+ });
+ instrumentation.waitForIdleSync();
+ assertTrue(mViewRoot.mWindowAttributes.type == windowType);
+ }
+
@UiThreadTest
@Test
@RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
@@ -161,7 +208,7 @@ public class ViewFrameRateTest {
@RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API,
FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void lowVelocity60() throws Throwable {
+ public void lowVelocity80() throws Throwable {
if (!ViewProperties.vrr_enabled().orElse(true)) {
return;
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 1013bf50ab94..ce36ee06bb38 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -20,6 +20,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_TILE_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_TILE_COMPONENT_NAME;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.google.common.truth.Truth.assertThat;
@@ -31,6 +32,8 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doThrow;
@@ -48,12 +51,14 @@ import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.view.Display;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
+import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.util.IntPair;
import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -321,7 +326,7 @@ public class AccessibilityManagerTest {
Throwable rethrownException = assertThrows(RuntimeException.class,
() -> manager.enableShortcutsForTargets(
/* enable= */ false,
- UserShortcutType.HARDWARE,
+ HARDWARE,
Set.of(DALTONIZER_COMPONENT_NAME.flattenToString()),
UserHandle.USER_CURRENT
));
@@ -331,7 +336,7 @@ public class AccessibilityManagerTest {
@Test
public void enableShortcutsForTargets_verifyServiceMethodCalled() throws Exception {
AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
- int shortcutTypes = UserShortcutType.HARDWARE | UserShortcutType.TRIPLETAP;
+ int shortcutTypes = HARDWARE | UserShortcutType.TRIPLETAP;
manager.enableShortcutsForTargets(
/* enable= */ false,
@@ -348,6 +353,30 @@ public class AccessibilityManagerTest {
);
}
+ @Test
+ public void performAccessibilityShortcut_callToService_defaultTypeIsHardware()
+ throws Exception {
+ AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+
+ manager.performAccessibilityShortcut();
+
+ verify(mMockService).performAccessibilityShortcut(
+ eq(Display.DEFAULT_DISPLAY), eq(HARDWARE), isNull());
+ }
+
+ @Test
+ public void performAccessibilityShortcut_callToService_typeParameterMatches() throws Exception {
+ AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+ int display = Display.DEFAULT_DISPLAY;
+ String name = LABEL;
+
+ for (int type: ShortcutConstants.USER_SHORTCUT_TYPES) {
+ manager.performAccessibilityShortcut(display, type, name);
+
+ verify(mMockService).performAccessibilityShortcut(display, type, name);
+ }
+ }
+
private class MyAccessibilityProxy extends AccessibilityDisplayProxy {
MyAccessibilityProxy(int displayId,
@NonNull List<AccessibilityServiceInfo> serviceInfos) {
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
index 6ab77dc9d535..534420e6e36e 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
@@ -355,7 +355,8 @@ public class RemoteViewsAdapterTest {
}
@Override
- public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
+ public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
+ int capBitmapSize) {
RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
new RemoteViews.RemoteCollectionItems.Builder();
itemsBuilder.setHasStableIds(hasStableIds())
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 6b9dbbaaf484..f78bc9294357 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -71,6 +71,7 @@ import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.speech.tts.Voice;
import android.test.mock.MockContentResolver;
+import android.view.Display;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -378,7 +379,8 @@ public class AccessibilityShortcutControllerTest {
verify(mAlertDialog).show();
verify(mAccessibilityManagerService, atLeastOnce()).getInstalledAccessibilityServiceList(
anyInt());
- verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
verify(mFrameworkObjectProvider, times(0)).getTextToSpeech(any(), any());
}
@@ -397,7 +399,8 @@ public class AccessibilityShortcutControllerTest {
// assertEquals(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
// mLayoutParams.privateFlags
// & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
- verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -630,7 +633,8 @@ public class AccessibilityShortcutControllerTest {
verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
verify(mToast).show();
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -649,7 +653,8 @@ public class AccessibilityShortcutControllerTest {
verify(mAccessibilityManagerService).enableShortcutsForTargets(
eq(true), eq(HARDWARE), mListCaptor.capture(), anyInt());
assertThat(mListCaptor.getValue()).containsExactly(SERVICE_NAME_STRING);
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -666,7 +671,8 @@ public class AccessibilityShortcutControllerTest {
assertThat(Settings.Secure.getString(mContentResolver,
ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEqualTo(SERVICE_NAME_STRING);
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -726,7 +732,8 @@ public class AccessibilityShortcutControllerTest {
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -739,7 +746,8 @@ public class AccessibilityShortcutControllerTest {
getController().performAccessibilityShortcut();
verifyZeroInteractions(mToast);
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -755,7 +763,8 @@ public class AccessibilityShortcutControllerTest {
getController().performAccessibilityShortcut();
verifyZeroInteractions(mToast);
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetHelperTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetHelperTest.java
new file mode 100644
index 000000000000..64be97d63f1b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetHelperTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
+
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Build;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityTargetHelperTest {
+ @Test
+ public void isValidServiceTarget_legacyService_hardware_isTrue() {
+ assertThat(AccessibilityTargetHelper.isValidServiceTarget(generateServiceInfo(
+ /* flags = */ 0, /* targetSdk = */ 0), HARDWARE)).isTrue();
+ }
+
+ @Test
+ public void isValidServiceTarget_legacyService_software_requestsButton_isTrue() {
+ assertThat(AccessibilityTargetHelper.isValidServiceTarget(generateServiceInfo(
+ FLAG_REQUEST_ACCESSIBILITY_BUTTON, /* targetSdk = */ 0), SOFTWARE)).isTrue();
+ }
+
+ @Test
+ public void isValidServiceTarget_legacyService_software_isFalse() {
+ assertThat(AccessibilityTargetHelper.isValidServiceTarget(generateServiceInfo(
+ /* flags = */ 0, /* targetSdk = */ 0), SOFTWARE)).isFalse();
+ }
+
+ @Test
+ public void isValidServiceTarget_modernService_isTrue() {
+ assertThat(AccessibilityTargetHelper.isValidServiceTarget(generateServiceInfo(
+ /* flags = */ 0, Build.VERSION_CODES.Q + 1), DEFAULT)).isTrue();
+ }
+
+ private AccessibilityServiceInfo generateServiceInfo(int flags, int targetSdk) {
+ AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+ info.flags = flags;
+ info.setResolveInfo(new ResolveInfo());
+ info.getResolveInfo().serviceInfo = new ServiceInfo();
+ info.getResolveInfo().serviceInfo.applicationInfo = new ApplicationInfo();
+ info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = targetSdk;
+ return info;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java
new file mode 100644
index 000000000000..389b677587b0
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.accessibility.common.ShortcutConstants;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.stream.IntStream;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityTargetTest {
+ private static final int[] EXPECTED_TYPES = { HARDWARE, SOFTWARE };
+
+ @Test
+ public void isRecognizedShortcutType_expectedType_isTrue() {
+ for (int type : EXPECTED_TYPES) {
+ assertThat(AccessibilityTarget.isRecognizedShortcutType(type)).isTrue();
+ }
+ }
+
+ @Test
+ public void isRecognizedShortcutType_notExpectedType_isFalse() {
+ for (int type: ShortcutConstants.USER_SHORTCUT_TYPES) {
+ if (IntStream.of(EXPECTED_TYPES).noneMatch(x -> x == type)) {
+ assertThat(AccessibilityTarget.isRecognizedShortcutType(type)).isFalse();
+ }
+ }
+ }
+}
diff --git a/core/tests/utiltests/src/android/util/TimeUtilsTest.java b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
index ac659e1bc593..6c6feaf4ae9c 100644
--- a/core/tests/utiltests/src/android/util/TimeUtilsTest.java
+++ b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
@@ -18,17 +18,19 @@ package android.util;
import static org.junit.Assert.assertEquals;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.TimeZone;
import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
@@ -42,6 +44,22 @@ public class TimeUtilsTest {
public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
+ private TimeZone mOrigTimezone;
+
+ @Before
+ public void setUp() {
+ mOrigTimezone = TimeZone.getDefault();
+
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ }
+
+ @After
+ public void tearDown() {
+ if (mOrigTimezone != null) {
+ TimeZone.setDefault(mOrigTimezone);
+ }
+ }
+
@Test
public void testFormatTime() {
assertEquals("1672556400000 (now)",
@@ -85,32 +103,29 @@ public class TimeUtilsTest {
}
@Test
- @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testDumpTime() {
- assertEquals("2023-01-01 00:00:00.000", runWithPrintWriter((pw) -> {
+ assertEquals("2023-01-01 07:00:00.000", runWithPrintWriter((pw) -> {
TimeUtils.dumpTime(pw, 1672556400000L);
}));
- assertEquals("2023-01-01 00:00:00.000 (now)", runWithPrintWriter((pw) -> {
+ assertEquals("2023-01-01 07:00:00.000 (now)", runWithPrintWriter((pw) -> {
TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L);
}));
- assertEquals("2023-01-01 00:00:00.000 (-10ms)", runWithPrintWriter((pw) -> {
+ assertEquals("2023-01-01 07:00:00.000 (-10ms)", runWithPrintWriter((pw) -> {
TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L + 10);
}));
}
@Test
- @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testFormatForLogging() {
assertEquals("unknown", TimeUtils.formatForLogging(0));
assertEquals("unknown", TimeUtils.formatForLogging(-1));
assertEquals("unknown", TimeUtils.formatForLogging(Long.MIN_VALUE));
- assertEquals("2023-01-01 00:00:00", TimeUtils.formatForLogging(1672556400000L));
+ assertEquals("2023-01-01 07:00:00", TimeUtils.formatForLogging(1672556400000L));
}
@Test
- @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testLogTimeOfDay() {
- assertEquals("01-01 00:00:00.000", TimeUtils.logTimeOfDay(1672556400000L));
+ assertEquals("01-01 07:00:00.000", TimeUtils.logTimeOfDay(1672556400000L));
}
public static String runWithPrintWriter(Consumer<PrintWriter> consumer) {
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 3256f31bdc93..5caedba364be 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -416,7 +416,8 @@ public class SurfaceTexture {
}
/**
- * Retrieve the dataspace associated with the texture image.
+ * Retrieve the dataspace associated with the texture image
+ * set by the most recent call to {@link #updateTexImage}.
*/
@SuppressLint("MethodNameUnits")
public @NamedDataSpace int getDataSpace() {
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index c3aaf983711d..f8cb050fb360 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -494,30 +494,37 @@ public final class Icon implements Parcelable {
BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(),
getDataLength())));
case TYPE_URI:
- InputStream is = getUriInputStream(context);
- if (is != null) {
- final Bitmap bitmap = BitmapFactory.decodeStream(is);
- if (bitmap == null) {
- Log.w(TAG, "Unable to decode image from URI: " + getUriString());
- if (iconLoadDrawableReturnNullWhenUriDecodeFails()) {
- return null;
+ try (InputStream is = getUriInputStream(context)) {
+ if (is != null) {
+ final Bitmap bitmap = BitmapFactory.decodeStream(is);
+ if (bitmap == null) {
+ Log.w(TAG, "Unable to decode image from URI: " + getUriString());
+ if (iconLoadDrawableReturnNullWhenUriDecodeFails()) {
+ return null;
+ }
}
+ return new BitmapDrawable(context.getResources(), fixMaxBitmapSize(bitmap));
}
- return new BitmapDrawable(context.getResources(), fixMaxBitmapSize(bitmap));
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
}
break;
case TYPE_URI_ADAPTIVE_BITMAP:
- is = getUriInputStream(context);
- if (is != null) {
- final Bitmap bitmap = BitmapFactory.decodeStream(is);
- if (bitmap == null) {
- Log.w(TAG, "Unable to decode image from URI: " + getUriString());
- if (iconLoadDrawableReturnNullWhenUriDecodeFails()) {
- return null;
+ try (InputStream is = getUriInputStream(context)) {
+ if (is != null) {
+ final Bitmap bitmap = BitmapFactory.decodeStream(is);
+ if (bitmap == null) {
+ Log.w(TAG, "Unable to decode image from URI: " + getUriString());
+ if (iconLoadDrawableReturnNullWhenUriDecodeFails()) {
+ return null;
+ }
}
+ return new AdaptiveIconDrawable(
+ null, new BitmapDrawable(context.getResources(),
+ fixMaxBitmapSize(bitmap)));
}
- return new AdaptiveIconDrawable(null, new BitmapDrawable(context.getResources(),
- fixMaxBitmapSize(bitmap)));
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
}
break;
}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
new file mode 100644
index 000000000000..c6dbd9b25e7f
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
@@ -0,0 +1,61 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_multitasking_windowing",
+}
+
+android_test {
+ name: "WMShellMultivalentScreenshotTestsOnDevice",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "WindowManager-Shell",
+ "junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "truth",
+ "platform-parametric-runner-lib",
+ "platform-screenshot-diff-core",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ optimize: {
+ enabled: false,
+ },
+ test_suites: ["device-tests"],
+ platform_apis: true,
+ certificate: "platform",
+ aaptflags: [
+ "--extra-packages",
+ "com.android.wm.shell",
+ ],
+ manifest: "AndroidManifest.xml",
+ asset_dirs: ["goldens/onDevice"],
+}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifest.xml
new file mode 100644
index 000000000000..467dc6a5cb81
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.wm.shell.multivalentscreenshot">
+
+ <application android:debuggable="true" android:supportsRtl="true" >
+ <uses-library android:name="android.test.runner" />
+ <activity
+ android:name="platform.test.screenshot.ScreenshotActivity"
+ android:exported="true">
+ </activity>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Multivalent screenshot tests for WindowManager-Shell"
+ android:targetPackage="com.android.wm.shell.multivalentscreenshot">
+ </instrumentation>
+
+ <!-- this permission is required by Tuner Service in screenshot tests -->
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+</manifest>
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml
new file mode 100644
index 000000000000..a7a3f1313a9b
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.wm.shell.multivalentscreenshot">
+ <application android:debuggable="true" android:supportsRtl="true">
+ <uses-library android:name="android.test.runner" />
+ <activity
+ android:name="platform.test.screenshot.ScreenshotActivity"
+ android:exported="true">
+ </activity>
+ </application>
+</manifest>
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidTest.xml b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidTest.xml
new file mode 100644
index 000000000000..75793ae69d27
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Tests for WindowManagerShellLib">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="WMShellMultivalentScreenshotTestsOnDevice.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="WMShellMultivalentScreenshotTestsOnDevice" />
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/data/user/0/com.android.wm.shell.multivalentscreenshot/files/wmshell_screenshots" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.wm.shell.multivalentscreenshot" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
new file mode 100644
index 000000000000..eb2888199ddf
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
new file mode 100644
index 000000000000..eb2888199ddf
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties b/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties
new file mode 100644
index 000000000000..7a0527ccaafb
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties
@@ -0,0 +1,2 @@
+sdk=NEWEST_SDK
+
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
new file mode 100644
index 000000000000..d35f493a8f60
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.bubbles
+
+import android.view.LayoutInflater
+import com.android.wm.shell.common.bubbles.BubblePopupView
+import com.android.wm.shell.testing.goldenpathmanager.WMShellGoldenPathManager
+import com.android.wm.shell.R
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays
+import platform.test.screenshot.ViewScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+@RunWith(ParameterizedAndroidJunit4::class)
+class BubbleEducationViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.forDisplays(Displays.Phone, isLandscape = false)
+ }
+
+ @get:Rule
+ val screenshotRule =
+ ViewScreenshotTestRule(
+ emulationSpec,
+ WMShellGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec))
+ )
+
+ @Test
+ fun bubblesEducation() {
+ screenshotRule.screenshotTest("bubbles_education") { activity ->
+ activity.actionBar?.hide()
+ val view =
+ LayoutInflater.from(activity)
+ .inflate(R.layout.bubble_bar_stack_education, null) as BubblePopupView
+ view.setup()
+ view
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/testing/goldenpathmanager/WMShellGoldenPathManager.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/testing/goldenpathmanager/WMShellGoldenPathManager.kt
new file mode 100644
index 000000000000..901b79b9b1a0
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/testing/goldenpathmanager/WMShellGoldenPathManager.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.testing.goldenpathmanager
+
+import android.os.Build
+import androidx.test.platform.app.InstrumentationRegistry
+import platform.test.screenshot.GoldenPathManager
+import platform.test.screenshot.PathConfig
+
+/** A WM Shell specific implementation of [GoldenPathManager]. */
+class WMShellGoldenPathManager(pathConfig: PathConfig) :
+ GoldenPathManager(
+ appContext = InstrumentationRegistry.getInstrumentation().context,
+ assetsPathRelativeToBuildRoot = assetPath,
+ deviceLocalPath = deviceLocalPath,
+ pathConfig = pathConfig,
+ ) {
+
+ private companion object {
+ private const val ASSETS_PATH =
+ "frameworks/base/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice"
+ private const val ASSETS_PATH_ROBO =
+ "frameworks/base/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/" +
+ "robolectric"
+ private val assetPath: String
+ get() = if (Build.FINGERPRINT.contains("robolectric")) ASSETS_PATH_ROBO else ASSETS_PATH
+ private val deviceLocalPath: String
+ get() =
+ InstrumentationRegistry.getInstrumentation()
+ .targetContext
+ .filesDir
+ .absolutePath
+ .toString() + "/wmshell_screenshots"
+ }
+ override fun toString(): String {
+ // This string is appended to all actual/expected screenshots on the device, so make sure
+ // it is a static value.
+ return "WMShellGoldenPathManager"
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTestsForDevice b/libs/WindowManager/Shell/multivalentScreenshotTestsForDevice
new file mode 120000
index 000000000000..e879efc81ec1
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTestsForDevice
@@ -0,0 +1 @@
+multivalentScreenshotTests \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index a3111b31a2f9..c9d3dbdcae05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -324,6 +324,7 @@ abstract class CrossActivityBackAnimation(
enteringHasSameLetterbox = false
lastPostCommitFlingScale = SPRING_SCALE
gestureProgress = 0f
+ triggerBack = false
}
protected fun applyTransform(
@@ -499,10 +500,12 @@ abstract class CrossActivityBackAnimation(
}
override fun onBackCancelled() {
+ triggerBack = false
progressAnimator.onBackCancelled { finishAnimation() }
}
override fun onBackInvoked() {
+ triggerBack = true
progressAnimator.reset()
onGestureCommitted(progressAnimator.velocity)
}
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 1279fc42c066..2aefc64a3ebb 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
@@ -894,11 +894,22 @@ public class Bubble implements BubbleViewProvider {
}
@Nullable
- Intent getAppBubbleIntent() {
+ @VisibleForTesting
+ public Intent getAppBubbleIntent() {
return mAppIntent;
}
/**
+ * Sets the intent for a bubble that is an app bubble (one for which {@link #mIsAppBubble} is
+ * true).
+ *
+ * @param appIntent The intent to set for the app bubble.
+ */
+ void setAppBubbleIntent(Intent appIntent) {
+ mAppIntent = appIntent;
+ }
+
+ /**
* Returns whether this bubble is from an app versus a notification.
*/
public boolean isAppBubble() {
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 d2c36e6b637c..c853301519e9 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
@@ -1450,6 +1450,8 @@ public class BubbleController implements ConfigurationChangeListener,
if (b != null) {
// It's in the overflow, so remove it & reinflate
mBubbleData.dismissBubbleWithKey(appBubbleKey, Bubbles.DISMISS_NOTIF_CANCEL);
+ // Update the bubble entry in the overflow with the latest intent.
+ b.setAppBubbleIntent(intent);
} else {
// App bubble does not exist, lets add and expand it
b = Bubble.createAppBubble(intent, user, icon, mMainExecutor);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index bfac24b81d2f..2520c25613e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -258,9 +258,15 @@ public class CompatUIController implements OnDisplaysChangedListener,
return;
}
// We're showing the first reachability education so we ignore incoming TaskInfo
- // until the education flow has completed or we double tap.
+ // until the education flow has completed or we double tap. The double-tap
+ // basically cancel all the onboarding flow. We don't have to ignore events in case
+ // the app is in size compat mode.
if (mIsFirstReachabilityEducationRunning) {
- return;
+ if (!taskInfo.appCompatTaskInfo.isFromLetterboxDoubleTap
+ && !taskInfo.appCompatTaskInfo.topActivityInSizeCompat) {
+ return;
+ }
+ mIsFirstReachabilityEducationRunning = false;
}
if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
if (taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled) {
@@ -278,17 +284,24 @@ public class CompatUIController implements OnDisplaysChangedListener,
final boolean isFirstTimeVerticalReachabilityEdu = !topActivityPillarboxed
&& !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(taskInfo);
if (isFirstTimeHorizontalReachabilityEdu || isFirstTimeVerticalReachabilityEdu) {
- mIsFirstReachabilityEducationRunning = true;
mCompatUIConfiguration.setSeenLetterboxEducation(taskInfo.userId);
- createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
- return;
+ // We activate the first reachability education if the double-tap is enabled.
+ // If the double tap is not enabled (e.g. thin letterbox) we just set the value
+ // of the education being seen.
+ if (taskInfo.appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
+ mIsFirstReachabilityEducationRunning = true;
+ createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+ return;
+ }
}
}
}
createOrUpdateCompatLayout(taskInfo, taskListener);
createOrUpdateRestartDialogLayout(taskInfo, taskListener);
if (mCompatUIConfiguration.getHasSeenLetterboxEducation(taskInfo.userId)) {
- createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+ if (taskInfo.appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
+ createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+ }
// The user aspect ratio button should not be handled when a new TaskInfo is
// sent because of a double tap or when in multi-window mode.
if (taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 14ae3a7b9b27..196538248709 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -975,7 +975,7 @@ class DesktopTasksController(
if (!desktopModeTaskRepository.isDesktopModeShowing(task.displayId)) {
KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: switch freeform task to fullscreen oon transition" +
+ "DesktopTasksController: bring desktop tasks to front on transition" +
" taskId=%d",
task.taskId
)
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 9f3c519b441b..ad298dcc253e 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
@@ -65,9 +65,11 @@ 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;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -394,6 +396,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
+ Set<Integer> minimizedFreeformTasks = new HashSet<>();
int mostRecentFreeformTaskIndex = Integer.MAX_VALUE;
@@ -414,6 +417,9 @@ public class RecentTasksController implements TaskStackListenerCallback,
mostRecentFreeformTaskIndex = recentTasks.size();
}
freeformTasks.add(taskInfo);
+ if (mDesktopModeTaskRepository.get().isMinimizedTask(taskInfo.taskId)) {
+ minimizedFreeformTasks.add(taskInfo.taskId);
+ }
continue;
}
@@ -431,8 +437,10 @@ public class RecentTasksController implements TaskStackListenerCallback,
// Add a special entry for freeform tasks
if (!freeformTasks.isEmpty()) {
- recentTasks.add(mostRecentFreeformTaskIndex, GroupedRecentTaskInfo.forFreeformTasks(
- freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0])));
+ recentTasks.add(mostRecentFreeformTaskIndex,
+ GroupedRecentTaskInfo.forFreeformTasks(
+ freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0]),
+ minimizedFreeformTasks));
}
return recentTasks;
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 c045cebdf4e0..a2d2b9aff597 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
@@ -27,6 +27,7 @@ import androidx.annotation.Nullable;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
/**
* Simple container for recent tasks. May contain either a single or pair of tasks.
@@ -50,6 +51,9 @@ public class GroupedRecentTaskInfo implements Parcelable {
private final SplitBounds mSplitBounds;
@GroupType
private final int mType;
+ // TODO(b/348332802): move isMinimized inside each Task object instead once we have a
+ // replacement for RecentTaskInfo
+ private final int[] mMinimizedTaskIds;
/**
* Create new for a single task
@@ -57,7 +61,7 @@ public class GroupedRecentTaskInfo implements Parcelable {
public static GroupedRecentTaskInfo forSingleTask(
@NonNull ActivityManager.RecentTaskInfo task) {
return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task}, null,
- TYPE_SINGLE);
+ TYPE_SINGLE, null /* minimizedFreeformTasks */);
}
/**
@@ -66,28 +70,51 @@ public class GroupedRecentTaskInfo implements Parcelable {
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);
+ splitBounds, TYPE_SPLIT, null /* minimizedFreeformTasks */);
}
/**
* Create new for a group of freeform tasks
*/
public static GroupedRecentTaskInfo forFreeformTasks(
- @NonNull ActivityManager.RecentTaskInfo... tasks) {
- return new GroupedRecentTaskInfo(tasks, null, TYPE_FREEFORM);
+ @NonNull ActivityManager.RecentTaskInfo[] tasks,
+ @NonNull Set<Integer> minimizedFreeformTasks) {
+ return new GroupedRecentTaskInfo(
+ tasks,
+ null /* splitBounds */,
+ TYPE_FREEFORM,
+ minimizedFreeformTasks.stream().mapToInt(i -> i).toArray());
}
- private GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo[] tasks,
- @Nullable SplitBounds splitBounds, @GroupType int type) {
+ private GroupedRecentTaskInfo(
+ @NonNull ActivityManager.RecentTaskInfo[] tasks,
+ @Nullable SplitBounds splitBounds,
+ @GroupType int type,
+ @Nullable int[] minimizedFreeformTaskIds) {
mTasks = tasks;
mSplitBounds = splitBounds;
mType = type;
+ mMinimizedTaskIds = minimizedFreeformTaskIds;
+ ensureAllMinimizedIdsPresent(tasks, minimizedFreeformTaskIds);
+ }
+
+ private static void ensureAllMinimizedIdsPresent(
+ @NonNull ActivityManager.RecentTaskInfo[] tasks,
+ @Nullable int[] minimizedFreeformTaskIds) {
+ if (minimizedFreeformTaskIds == null) {
+ return;
+ }
+ if (!Arrays.stream(minimizedFreeformTaskIds).allMatch(
+ taskId -> Arrays.stream(tasks).anyMatch(task -> task.taskId == taskId))) {
+ throw new IllegalArgumentException("Minimized task IDs contain non-existent Task ID.");
+ }
}
GroupedRecentTaskInfo(Parcel parcel) {
mTasks = parcel.createTypedArray(ActivityManager.RecentTaskInfo.CREATOR);
mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
mType = parcel.readInt();
+ mMinimizedTaskIds = parcel.createIntArray();
}
/**
@@ -135,6 +162,10 @@ public class GroupedRecentTaskInfo implements Parcelable {
return mType;
}
+ public int[] getMinimizedTaskIds() {
+ return mMinimizedTaskIds;
+ }
+
@Override
public String toString() {
StringBuilder taskString = new StringBuilder();
@@ -161,6 +192,8 @@ public class GroupedRecentTaskInfo implements Parcelable {
taskString.append("TYPE_FREEFORM");
break;
}
+ taskString.append(", Minimized Task IDs: ");
+ taskString.append(Arrays.toString(mMinimizedTaskIds));
return taskString.toString();
}
@@ -181,6 +214,7 @@ public class GroupedRecentTaskInfo implements Parcelable {
parcel.writeTypedArray(mTasks, flags);
parcel.writeTypedObject(mSplitBounds, flags);
parcel.writeInt(mType);
+ parcel.writeIntArray(mMinimizedTaskIds);
}
@Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index a1a18a9b7c9d..14fa0f1a338d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -439,14 +439,15 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- fun showDesktopApps_dontReorderMinimizedTask() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_desktopWallpaperDisabled_dontReorderMinimizedTask() {
val homeTask = setUpHomeTask()
val freeformTask = setUpFreeformTask()
val minimizedTask = setUpFreeformTask()
+
markTaskHidden(freeformTask)
markTaskHidden(minimizedTask)
desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
-
controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
@@ -457,6 +458,26 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_desktopWallpaperEnabled_dontReorderMinimizedTask() {
+ setUpHomeTask()
+ val freeformTask = setUpFreeformTask()
+ val minimizedTask = setUpFreeformTask()
+
+ markTaskHidden(freeformTask)
+ markTaskHidden(minimizedTask)
+ desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(2)
+ // Add desktop wallpaper activity
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ // Reorder freeform task to top, don't reorder the minimized task
+ wct.assertReorderAt(index = 1, freeformTask, toTop = true)
+ }
+
+ @Test
fun getVisibleTaskCount_noTasks_returnsZero() {
assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
}
@@ -645,16 +666,33 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- fun moveToDesktop_nonRunningTask_launchesInFreeform() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
+ val task = createTaskInfo(1)
whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
- val task = createTaskInfo(1)
+ controller.moveToDesktop(task.taskId, transitionSource = UNKNOWN)
+ with(getLatestEnterDesktopWct()) {
+ assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
+ val task = createTaskInfo(1)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
controller.moveToDesktop(task.taskId, transitionSource = UNKNOWN)
+
with(getLatestEnterDesktopWct()) {
- assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ // Add desktop wallpaper activity
+ assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ // Launch task
+ assertLaunchTaskAt(index = 1, task.taskId, WINDOWING_MODE_FREEFORM)
}
}
@@ -776,21 +814,44 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- fun moveToDesktop_bringsTasksOverLimit_dontShowBackTask() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveToDesktop_desktopWallpaperDisabled_bringsTasksOver_dontShowBackTask() {
val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
- val homeTask = setUpHomeTask()
val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
val newTask = setUpFullscreenTask()
+ val homeTask = setUpHomeTask()
controller.moveToDesktop(newTask, transitionSource = UNKNOWN)
val wct = getLatestEnterDesktopWct()
assertThat(wct.hierarchyOps.size).isEqualTo(taskLimit + 1) // visible tasks + home
wct.assertReorderAt(0, homeTask)
- for (i in 1..<taskLimit) { // Skipping freeformTasks[0]
- wct.assertReorderAt(index = i, task = freeformTasks[i])
- }
- wct.assertReorderAt(taskLimit, newTask)
+ wct.assertReorderSequenceInRange(
+ range = 1..<(taskLimit + 1),
+ *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
+ newTask
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
+ val newTask = setUpFullscreenTask()
+ setUpHomeTask()
+
+ controller.moveToDesktop(newTask, transitionSource = UNKNOWN)
+
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.hierarchyOps.size).isEqualTo(taskLimit + 1) // visible tasks + wallpaper
+ // Add desktop wallpaper activity
+ wct.assertPendingIntentAt(0, desktopWallpaperIntent)
+ wct.assertReorderSequenceInRange(
+ range = 1..<(taskLimit + 1),
+ *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
+ newTask
+ )
}
@Test
@@ -1109,41 +1170,106 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- fun handleRequest_freeformTask_freeformNotVisible_reorderedToTop() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperDisabled_freeformNotVisible_reorderedToTop() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
val freeformTask1 = setUpFreeformTask()
+ val freeformTask2 = createFreeformTask()
+
markTaskHidden(freeformTask1)
+ val result =
+ controller.handleRequest(Binder(), createTransition(freeformTask2, type = TRANSIT_TO_FRONT))
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(2)
+ result.assertReorderAt(1, freeformTask2, toTop = true)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val freeformTask1 = setUpFreeformTask()
val freeformTask2 = createFreeformTask()
+
+ markTaskHidden(freeformTask1)
val result =
- controller.handleRequest(Binder(), createTransition(freeformTask2, type = TRANSIT_TO_FRONT))
+ controller.handleRequest(Binder(), createTransition(freeformTask2, type = TRANSIT_TO_FRONT))
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(3)
+ // Add desktop wallpaper activity
+ result.assertPendingIntentAt(0, desktopWallpaperIntent)
+ // Bring active desktop tasks to front
+ result.assertReorderAt(1, freeformTask1, toTop = true)
+ // Bring new task to front
+ result.assertReorderAt(2, freeformTask2, toTop = true)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
- assertThat(result?.hierarchyOps?.size).isEqualTo(2)
- result!!.assertReorderAt(1, freeformTask2, toTop = true)
+ val task = createFreeformTask()
+ val result = controller.handleRequest(Binder(), createTransition(task))
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(1)
+ result.assertReorderAt(0, task, toTop = true)
}
@Test
- fun handleRequest_freeformTask_noOtherTasks_reorderedToTop() {
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
val task = createFreeformTask()
val result = controller.handleRequest(Binder(), createTransition(task))
- assertThat(result?.hierarchyOps?.size).isEqualTo(1)
- result!!.assertReorderAt(0, task, toTop = true)
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(2)
+ // Add desktop wallpaper activity
+ result.assertPendingIntentAt(0, desktopWallpaperIntent)
+ // Bring new task to front
+ result.assertReorderAt(1, task, toTop = true)
}
@Test
- fun handleRequest_freeformTask_freeformOnOtherDisplayOnly_reorderedToTop() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_dskWallpaperDisabled_freeformOnOtherDisplayOnly_reorderedToTop() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
- val taskSecondDisplay = createFreeformTask(displayId = SECOND_DISPLAY)
+ // Second display task
+ createFreeformTask(displayId = SECOND_DISPLAY)
val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
- assertThat(result?.hierarchyOps?.size).isEqualTo(1)
- result!!.assertReorderAt(0, taskDefaultDisplay, toTop = true)
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(1)
+ result.assertReorderAt(0, taskDefaultDisplay, toTop = true)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
+ // Second display task
+ createFreeformTask(displayId = SECOND_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(2)
+ // Add desktop wallpaper activity
+ result.assertPendingIntentAt(0, desktopWallpaperIntent)
+ // Bring new task to front
+ result.assertReorderAt(1, taskDefaultDisplay, toTop = true)
}
@Test
@@ -2041,6 +2167,16 @@ private fun WindowContainerTransaction.assertReorderSequence(vararg tasks: Runni
}
}
+/** Checks if the reorder hierarchy operations in [range] correspond to [tasks] list */
+private fun WindowContainerTransaction.assertReorderSequenceInRange(
+ range: IntRange,
+ vararg tasks: RunningTaskInfo
+) {
+ assertThat(hierarchyOps.slice(range).map { it.type to it.container })
+ .containsExactlyElementsIn(tasks.map { HIERARCHY_OP_TYPE_REORDER to it.token.asBinder() })
+ .inOrder()
+}
+
private fun WindowContainerTransaction.assertRemoveAt(index: Int, token: WindowContainerToken) {
assertIndexInBounds(index)
val op = hierarchyOps[index]
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
index 974539f23b80..aa2d6f09508f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
@@ -241,16 +241,16 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_move_expanded_valid() {
- mTvPipBoundsState.setTvPipExpanded(true);
-
// Vertical expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipExpanded(true);
mTvPipBoundsState.setTvPipGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.CENTER_VERTICAL | Gravity.LEFT, true);
moveAndCheckGravity(KEYCODE_DPAD_RIGHT, Gravity.CENTER_VERTICAL | Gravity.RIGHT, true);
// Horizontal expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipExpanded(true);
mTvPipBoundsState.setTvPipGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
moveAndCheckGravity(KEYCODE_DPAD_UP, Gravity.TOP | Gravity.CENTER_HORIZONTAL, true);
moveAndCheckGravity(KEYCODE_DPAD_DOWN, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, true);
@@ -281,10 +281,9 @@ public class TvPipGravityTest extends ShellTestCase {
@Test
public void updateGravity_move_expanded_invalid() {
- mTvPipBoundsState.setTvPipExpanded(true);
-
// Vertical expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipExpanded(true);
mTvPipBoundsState.setTvPipGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
moveAndCheckGravity(KEYCODE_DPAD_RIGHT, Gravity.CENTER_VERTICAL | Gravity.RIGHT, false);
moveAndCheckGravity(KEYCODE_DPAD_UP, Gravity.CENTER_VERTICAL | Gravity.RIGHT, false);
@@ -297,6 +296,7 @@ public class TvPipGravityTest extends ShellTestCase {
// Horizontal expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipExpanded(true);
mTvPipBoundsState.setTvPipGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
moveAndCheckGravity(KEYCODE_DPAD_DOWN, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, false);
moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, false);
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
index bbd65be9abda..15b73c541ed8 100644
--- 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
@@ -17,6 +17,7 @@
package com.android.wm.shell.recents
import android.app.ActivityManager
+import android.app.ActivityManager.RecentTaskInfo
import android.graphics.Rect
import android.os.Parcel
import android.testing.AndroidTestingRunner
@@ -33,6 +34,7 @@ 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.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -86,12 +88,13 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
@Test
fun testFreeformTasks_hasCorrectType() {
- assertThat(freeformTasksGroupInfo().type).isEqualTo(TYPE_FREEFORM)
+ assertThat(freeformTasksGroupInfo(freeformTaskIds = arrayOf(1)).type)
+ .isEqualTo(TYPE_FREEFORM)
}
@Test
- fun testSplitTasks_taskInfoList_hasThreeTasks() {
- val list = freeformTasksGroupInfo().taskInfoList
+ fun testCreateFreeformTasks_hasCorrectNumberOfTasks() {
+ val list = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3)).taskInfoList
assertThat(list).hasSize(3)
assertThat(list[0].taskId).isEqualTo(1)
assertThat(list[1].taskId).isEqualTo(2)
@@ -99,6 +102,16 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
}
@Test
+ fun testCreateFreeformTasks_nonExistentMinimizedTaskId_throwsException() {
+ assertThrows(IllegalArgumentException::class.java) {
+ freeformTasksGroupInfo(
+ freeformTaskIds = arrayOf(1, 2, 3),
+ minimizedTaskIds = arrayOf(1, 4)
+ )
+ }
+ }
+
+ @Test
fun testParcelling_singleTask() {
val recentTaskInfo = singleTaskGroupInfo()
val parcel = Parcel.obtain()
@@ -129,7 +142,7 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
@Test
fun testParcelling_freeformTasks() {
- val recentTaskInfo = freeformTasksGroupInfo()
+ val recentTaskInfo = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3))
val parcel = Parcel.obtain()
recentTaskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
@@ -145,6 +158,21 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
.containsExactly(1, 2, 3)
}
+ @Test
+ fun testParcelling_freeformTasks_minimizedTasks() {
+ val recentTaskInfo = freeformTasksGroupInfo(
+ freeformTaskIds = arrayOf(1, 2, 3), minimizedTaskIds = arrayOf(2))
+
+ 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.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray())
+ }
+
private fun createTaskInfo(id: Int) = ActivityManager.RecentTaskInfo().apply {
taskId = id
token = WindowContainerToken(mock(IWindowContainerToken::class.java))
@@ -162,10 +190,12 @@ class GroupedRecentTaskInfoTest : ShellTestCase() {
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)
+ private fun freeformTasksGroupInfo(
+ freeformTaskIds: Array<Int>,
+ minimizedTaskIds: Array<Int> = emptyArray()
+ ): GroupedRecentTaskInfo {
+ return GroupedRecentTaskInfo.forFreeformTasks(
+ freeformTaskIds.map { createTaskInfo(it) }.toTypedArray(),
+ minimizedTaskIds.toSet())
}
}
diff --git a/location/Android.bp b/location/Android.bp
index e864689bbdee..c0e102acad4d 100644
--- a/location/Android.bp
+++ b/location/Android.bp
@@ -41,6 +41,9 @@ java_sdk_library {
},
lint: {
baseline_filename: "lint-baseline.xml",
+ warning_checks: [
+ "FlaggedApi",
+ ],
},
}
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 5f84862ddf49..acfe473c260c 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -45,13 +45,6 @@ flag {
}
flag {
- name: "gnss_call_stop_before_set_position_mode"
- namespace: "location"
- description: "Flag for calling stop() before setPositionMode()"
- bug: "306874828"
-}
-
-flag {
name: "gnss_api_measurement_request_work_source"
namespace: "location"
description: "Flag for GnssMeasurementRequest WorkSource API"
diff --git a/packages/CtsShim/Android.bp b/packages/CtsShim/Android.bp
index baafe7ba570c..a94c8c56a31f 100644
--- a/packages/CtsShim/Android.bp
+++ b/packages/CtsShim/Android.bp
@@ -61,7 +61,6 @@ android_app_import {
"com.android.apex.cts.shim.v1",
"com.android.apex.cts.shim.v2",
"com.android.apex.cts.shim.v2_legacy",
- "com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_sdk_target_p",
"com.android.apex.cts.shim.v3",
],
@@ -102,7 +101,6 @@ android_app_import {
"com.android.apex.cts.shim.v1",
"com.android.apex.cts.shim.v2",
"com.android.apex.cts.shim.v2_legacy",
- "com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_sdk_target_p",
"com.android.apex.cts.shim.v3",
],
diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp
index d6b7ecf5819d..5b3d47e9f74d 100644
--- a/packages/CtsShim/build/Android.bp
+++ b/packages/CtsShim/build/Android.bp
@@ -93,7 +93,6 @@ android_app {
"com.android.apex.cts.shim.v1",
"com.android.apex.cts.shim.v2",
"com.android.apex.cts.shim.v2_apk_in_apex_upgrades",
- "com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_legacy",
"com.android.apex.cts.shim.v2_sdk_target_p",
"com.android.apex.cts.shim.v2_unsigned_payload",
@@ -200,7 +199,6 @@ android_app {
"com.android.apex.cts.shim.v1",
"com.android.apex.cts.shim.v2",
"com.android.apex.cts.shim.v2_apk_in_apex_upgrades",
- "com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_legacy",
"com.android.apex.cts.shim.v2_sdk_target_p",
"com.android.apex.cts.shim.v2_unsigned_payload",
diff --git a/packages/CtsShim/build/jni/Android.bp b/packages/CtsShim/build/jni/Android.bp
index 2dbf2a212cc3..ac85d2b60327 100644
--- a/packages/CtsShim/build/jni/Android.bp
+++ b/packages/CtsShim/build/jni/Android.bp
@@ -33,7 +33,6 @@ cc_library_shared {
"com.android.apex.cts.shim.v1",
"com.android.apex.cts.shim.v2",
"com.android.apex.cts.shim.v2_apk_in_apex_upgrades",
- "com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_legacy",
"com.android.apex.cts.shim.v2_sdk_target_p",
"com.android.apex.cts.shim.v2_unsigned_payload",
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 493818b2e74f..d60290ed91ef 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -174,7 +174,7 @@ public class FooterPreference extends Preference {
/** Return the content description of footer preference. */
@VisibleForTesting
- CharSequence getContentDescription() {
+ public CharSequence getContentDescription() {
return mContentDescription;
}
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
index 0ab33b71178f..98a72909b276 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -53,6 +53,12 @@ public class LottieColorUtils {
".grey900",
R.color.settingslib_color_grey50);
map.put(
+ ".red100",
+ R.color.settingslib_color_red500);
+ map.put(
+ ".red200",
+ R.color.settingslib_color_red500);
+ map.put(
".red400",
R.color.settingslib_color_red600);
map.put(
@@ -65,14 +71,14 @@ public class LottieColorUtils {
".blue400",
R.color.settingslib_color_blue600);
map.put(
- ".green400",
- R.color.settingslib_color_green600);
+ ".green100",
+ R.color.settingslib_color_green500);
map.put(
".green200",
R.color.settingslib_color_green500);
map.put(
- ".red200",
- R.color.settingslib_color_red500);
+ ".green400",
+ R.color.settingslib_color_green600);
map.put(
".cream",
R.color.settingslib_color_charcoal);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8d02dbd3df4d..d4a4703d5caf 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -134,6 +134,7 @@ public class SecureSettings {
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
+ Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY,
Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 7169cf7708eb..6df1c45bd2ac 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -195,6 +195,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_WAKE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index e03ac3de7e4e..795b39576391 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -8,6 +8,7 @@ achalke@google.com
acul@google.com
adamcohen@google.com
aioana@google.com
+alexchau@google.com
alexflo@google.com
andonian@google.com
amiko@google.com
@@ -89,8 +90,10 @@ rahulbanerjee@google.com
rgl@google.com
roosa@google.com
saff@google.com
+samcackett@google.com
santie@google.com
shanh@google.com
+silvajordan@google.com
snoeberger@google.com
spdonghao@google.com
steell@google.com
@@ -103,6 +106,7 @@ tkachenkoi@google.com
tracyzhou@google.com
tsuji@google.com
twickham@google.com
+uwaisashraf@google.com
vadimt@google.com
valiiftime@google.com
vanjan@google.com
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 0ccaf181a389..90885ab51539 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1021,13 +1021,6 @@ flag {
}
flag {
- name: "glanceable_hub_fullscreen_swipe"
- namespace: "systemui"
- description: "Increase swipe area for gestures to bring in glanceable hub"
- bug: "339665673"
-}
-
-flag {
name: "glanceable_hub_shortcut_button"
namespace: "systemui"
description: "Shows a button over the dream and lock screen to open the glanceable hub"
@@ -1105,3 +1098,23 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "media_lockscreen_launch_animation"
+ namespace : "systemui"
+ description : "Enable the origin launch animation for UMO when opening on top of lockscreen."
+ bug : "346865769"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "expand_heads_up_on_inline_reply"
+ namespace: "systemui"
+ description: "Expands heads up notification when users clicks reply button and activate inline reply"
+ bug: "346976443"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 7c2f7fe51d67..2ed0f6c89ebd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -7,6 +7,7 @@ import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -31,15 +32,12 @@ import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.CommunalSwipeDetector
-import com.android.compose.animation.scene.DefaultSwipeDetector
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.FixedSizeEdgeDetector
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.compose.animation.scene.SceneKey
@@ -51,7 +49,6 @@ import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.Flags
-import com.android.systemui.Flags.glanceableHubFullscreenSwipe
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -60,7 +57,6 @@ import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
-import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.SceneTransitionLayoutDataSource
import kotlin.time.DurationUnit
@@ -137,6 +133,9 @@ val sceneTransitions = transitions {
fade(Communal.Elements.Grid)
}
}
+ // Disable horizontal overscroll. If the scene is overscrolled too soon after showing, this
+ // can lead to inconsistent KeyguardState changes.
+ overscroll(CommunalScenes.Communal, Orientation.Horizontal) {}
}
/**
@@ -187,25 +186,11 @@ fun CommunalContainer(
onDispose { viewModel.setTransitionState(null) }
}
- val swipeSourceDetector =
- if (glanceableHubFullscreenSwipe()) {
- detector
- } else {
- FixedSizeEdgeDetector(dimensionResource(id = R.dimen.communal_gesture_initiation_width))
- }
-
- val swipeDetector =
- if (glanceableHubFullscreenSwipe()) {
- detector
- } else {
- DefaultSwipeDetector
- }
-
SceneTransitionLayout(
state = state,
modifier = modifier.fillMaxSize(),
- swipeSourceDetector = swipeSourceDetector,
- swipeDetector = swipeDetector,
+ swipeSourceDetector = detector,
+ swipeDetector = detector,
) {
scene(
CommunalScenes.Blank,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 7d829208ee89..1ce51afb4fdc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -27,6 +27,7 @@ import com.android.compose.animation.scene.SceneScope
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
+import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
@@ -44,81 +45,86 @@ constructor(
private val lockSection: LockSection,
private val bottomAreaSection: BottomAreaSection,
private val ambientStatusBarSection: AmbientStatusBarSection,
+ private val communalPopupSection: CommunalPopupSection,
) {
@Composable
fun SceneScope.Content(modifier: Modifier = Modifier) {
- Layout(
- modifier = modifier.fillMaxSize(),
- content = {
- Box(modifier = Modifier.fillMaxSize()) {
- with(ambientStatusBarSection) {
- AmbientStatusBar(modifier = Modifier.fillMaxWidth())
+ CommunalTouchableSurface(viewModel = viewModel, modifier = modifier) {
+ Layout(
+ modifier = Modifier.fillMaxSize(),
+ content = {
+ Box(modifier = Modifier.fillMaxSize()) {
+ with(communalPopupSection) { Popup() }
+ with(ambientStatusBarSection) {
+ AmbientStatusBar(modifier = Modifier.fillMaxWidth())
+ }
+ CommunalHub(
+ viewModel = viewModel,
+ interactionHandler = interactionHandler,
+ dialogFactory = dialogFactory,
+ modifier = Modifier.element(Communal.Elements.Grid)
+ )
+ }
+ with(lockSection) {
+ LockIcon(
+ overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer,
+ modifier = Modifier.element(Communal.Elements.LockIcon)
+ )
+ }
+ with(bottomAreaSection) {
+ IndicationArea(
+ Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
+ )
}
- CommunalHub(
- viewModel = viewModel,
- interactionHandler = interactionHandler,
- dialogFactory = dialogFactory,
- modifier = Modifier.element(Communal.Elements.Grid)
- )
- }
- with(lockSection) {
- LockIcon(
- overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer,
- modifier = Modifier.element(Communal.Elements.LockIcon)
- )
- }
- with(bottomAreaSection) {
- IndicationArea(
- Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
- )
}
- }
- ) { measurables, constraints ->
- val communalGridMeasurable = measurables[0]
- val lockIconMeasurable = measurables[1]
- val bottomAreaMeasurable = measurables[2]
+ ) { measurables, constraints ->
+ val communalGridMeasurable = measurables[0]
+ val lockIconMeasurable = measurables[1]
+ val bottomAreaMeasurable = measurables[2]
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
- val bottomAreaPlaceable =
- bottomAreaMeasurable.measure(
- noMinConstraints.copy(
- maxHeight = (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
+ val bottomAreaPlaceable =
+ bottomAreaMeasurable.measure(
+ noMinConstraints.copy(
+ maxHeight =
+ (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
+ )
)
- )
- val communalGridPlaceable =
- communalGridMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
+ val communalGridPlaceable =
+ communalGridMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
- layout(constraints.maxWidth, constraints.maxHeight) {
- communalGridPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- bottomAreaPlaceable.place(
- x = 0,
- y = constraints.maxHeight - bottomAreaPlaceable.height,
- )
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ communalGridPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ bottomAreaPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - bottomAreaPlaceable.height,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 9ea435edbb0f..9c2127c1a790 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -25,7 +25,6 @@ import android.widget.FrameLayout
import android.widget.RemoteViews
import androidx.annotation.VisibleForTesting
import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
@@ -71,7 +70,6 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.outlined.Edit
-import androidx.compose.material.icons.outlined.TouchApp
import androidx.compose.material.icons.outlined.Widgets
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
@@ -99,14 +97,16 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
-import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.input.key.onPreviewKeyEvent
-import androidx.compose.ui.input.pointer.motionEventSpy
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.boundsInWindow
@@ -126,13 +126,11 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.times
import androidx.compose.ui.viewinterop.AndroidView
-import androidx.compose.ui.window.Popup
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.window.layout.WindowMetricsCalculator
import com.android.compose.animation.Easings.Emphasized
@@ -143,7 +141,6 @@ import com.android.internal.R.dimen.system_app_widget_background_radius
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.ui.compose.Dimensions.CardOutlineWidth
import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
@@ -151,7 +148,6 @@ import com.android.systemui.communal.ui.compose.extensions.observeTaps
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.ui.viewmodel.PopupType
import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.res.R
@@ -171,7 +167,6 @@ fun CommunalHub(
) {
val communalContent by
viewModel.communalContent.collectAsStateWithLifecycle(initialValue = emptyList())
- val currentPopup by viewModel.currentPopup.collectAsStateWithLifecycle(initialValue = null)
var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
var toolbarSize: IntSize? by remember { mutableStateOf(null) }
var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
@@ -213,43 +208,29 @@ fun CommunalHub(
}
.thenIf(!viewModel.isEditMode && !isEmptyState) {
Modifier.pointerInput(
- gridState,
- contentOffset,
- communalContent,
- gridCoordinates
- ) {
- detectLongPressGesture { offset ->
- // Deduct both grid offset relative to its container and content
- // offset.
- val adjustedOffset =
- gridCoordinates?.let {
- offset - it.positionInWindow() - contentOffset
- }
- val index =
- adjustedOffset?.let { firstIndexAtOffset(gridState, it) }
- // Display the button only when the gesture initiates from widgets,
- // the CTA tile, or an empty area on the screen. UMO/smartspace have
- // their own long-press handlers. To prevent user confusion, we
- // should
- // not display this button.
- if (
- index == null ||
- communalContent[index].isWidgetContent() ||
- communalContent[index] is
- CommunalContentModel.CtaTileInViewMode
- ) {
- viewModel.onShowCustomizeWidgetButton()
+ gridState,
+ contentOffset,
+ communalContent,
+ gridCoordinates
+ ) {
+ detectLongPressGesture { offset ->
+ // Deduct both grid offset relative to its container and content
+ // offset.
+ val adjustedOffset =
+ gridCoordinates?.let {
+ offset - it.positionInWindow() - contentOffset
}
- val key =
- index?.let { keyAtIndexIfEditable(communalContent, index) }
+ val index = adjustedOffset?.let { firstIndexAtOffset(gridState, it) }
+ val key = index?.let { keyAtIndexIfEditable(communalContent, index) }
+ // Handle long-click on widgets and set the selected index
+ // correctly. We only handle widgets here because long click on
+ // empty spaces is handled by CommunalPopupSection.
+ if (key != null) {
+ viewModel.onLongClick()
viewModel.setSelectedKey(key)
}
}
- .onPreviewKeyEvent {
- onKeyEvent(viewModel)
- false
- }
- .motionEventSpy { onMotionEvent(viewModel) }
+ }
},
) {
AccessibilityContainer(viewModel) {
@@ -342,22 +323,6 @@ fun CommunalHub(
)
}
}
- if (currentPopup == PopupType.CtaTile) {
- PopupOnDismissCtaTile(viewModel::onHidePopup)
- }
-
- AnimatedVisibility(
- visible = currentPopup == PopupType.CustomizeWidgetButton,
- modifier = Modifier.fillMaxSize()
- ) {
- ButtonToEditWidgets(
- onClick = {
- viewModel.onHidePopup()
- viewModel.onOpenWidgetEditor(selectedKey.value)
- },
- onHide = { viewModel.onHidePopup() }
- )
- }
if (viewModel is CommunalViewModel && dialogFactory != null) {
val isEnableWidgetDialogShowing by
@@ -413,14 +378,6 @@ fun CommunalHub(
}
}
-private fun onKeyEvent(viewModel: BaseCommunalViewModel) {
- viewModel.signalUserInteraction()
-}
-
-private fun onMotionEvent(viewModel: BaseCommunalViewModel) {
- viewModel.signalUserInteraction()
-}
-
@Composable
private fun DisclaimerBottomSheetContent(onButtonClicked: () -> Unit) {
val colors = LocalAndroidColorScheme.current
@@ -589,7 +546,7 @@ private fun BoxScope.CommunalHubLazyGrid(
}
} else {
CommunalContent(
- modifier = cardModifier.animateItemPlacement(),
+ modifier = cardModifier.animateItem(),
model = list[index],
viewModel = viewModel,
size = size,
@@ -821,107 +778,6 @@ private fun ToolbarButton(
}
@Composable
-private fun AnimatedVisibilityScope.ButtonToEditWidgets(
- onClick: () -> Unit,
- onHide: () -> Unit,
-) {
- Popup(
- alignment = Alignment.TopCenter,
- offset = IntOffset(0, 40),
- onDismissRequest = onHide,
- ) {
- val colors = LocalAndroidColorScheme.current
- Button(
- modifier =
- Modifier.height(56.dp)
- .graphicsLayer { transformOrigin = TransformOrigin(0f, 0f) }
- .animateEnterExit(
- enter =
- fadeIn(
- initialAlpha = 0f,
- animationSpec = tween(durationMillis = 83, easing = LinearEasing)
- ),
- exit =
- fadeOut(
- animationSpec =
- tween(
- durationMillis = 83,
- delayMillis = 167,
- easing = LinearEasing
- )
- )
- )
- .background(colors.secondary, RoundedCornerShape(50.dp)),
- onClick = onClick,
- ) {
- Row(
- modifier =
- Modifier.animateEnterExit(
- enter =
- fadeIn(
- animationSpec =
- tween(
- durationMillis = 167,
- delayMillis = 83,
- easing = LinearEasing
- )
- ),
- exit =
- fadeOut(
- animationSpec = tween(durationMillis = 167, easing = LinearEasing)
- )
- )
- ) {
- Icon(
- imageVector = Icons.Outlined.Widgets,
- contentDescription = stringResource(R.string.button_to_configure_widgets_text),
- tint = colors.onSecondary,
- modifier = Modifier.size(20.dp)
- )
- Spacer(modifier = Modifier.size(8.dp))
- Text(
- text = stringResource(R.string.button_to_configure_widgets_text),
- style = MaterialTheme.typography.titleSmall,
- color = colors.onSecondary
- )
- }
- }
- }
-}
-
-@Composable
-private fun PopupOnDismissCtaTile(onHidePopup: () -> Unit) {
- Popup(
- alignment = Alignment.TopCenter,
- offset = IntOffset(0, 40),
- onDismissRequest = onHidePopup
- ) {
- val colors = LocalAndroidColorScheme.current
- Row(
- modifier =
- Modifier.height(56.dp)
- .background(colors.secondary, RoundedCornerShape(50.dp))
- .padding(16.dp),
- horizontalArrangement = Arrangement.Center,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- imageVector = Icons.Outlined.TouchApp,
- contentDescription = stringResource(R.string.popup_on_dismiss_cta_tile_text),
- tint = colors.onSecondary,
- modifier = Modifier.size(20.dp)
- )
- Spacer(modifier = Modifier.size(8.dp))
- Text(
- text = stringResource(R.string.popup_on_dismiss_cta_tile_text),
- style = MaterialTheme.typography.titleSmall,
- color = colors.onSecondary,
- )
- }
- }
-}
-
-@Composable
private fun filledButtonColors(): ButtonColors {
val colors = LocalAndroidColorScheme.current
return ButtonDefaults.buttonColors(
@@ -968,13 +824,26 @@ private fun CommunalContent(
/** Creates an empty card used to highlight a particular spot on the grid. */
@Composable
-fun HighlightedItem(modifier: Modifier = Modifier) {
- Card(
- modifier = modifier,
- colors = CardDefaults.cardColors(containerColor = Color.Transparent),
- border = BorderStroke(CardOutlineWidth, LocalAndroidColorScheme.current.tertiaryFixed),
- shape = RoundedCornerShape(16.dp)
- ) {}
+fun HighlightedItem(modifier: Modifier = Modifier, alpha: Float = 1.0f) {
+ val brush = SolidColor(LocalAndroidColorScheme.current.primaryFixed)
+ Box(
+ modifier =
+ // drawBehind lets us draw outside the bounds of the widgets so that we don't need to
+ // resize grid items to account for the border.
+ modifier.drawBehind {
+ // 8dp of padding between the widget and the highlight on every side.
+ val padding = 8.dp.toPx()
+ drawRoundRect(
+ brush,
+ alpha = alpha,
+ topLeft = Offset(-padding, -padding),
+ size =
+ Size(width = size.width + padding * 2, height = size.height + padding * 2),
+ cornerRadius = CornerRadius(37.dp.toPx()),
+ style = Stroke(width = 3.dp.toPx())
+ )
+ }
+ )
}
/** Presents a CTA tile at the end of the grid, to customize the hub. */
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt
new file mode 100644
index 000000000000..3707a87249b9
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.compose
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.onPreviewKeyEvent
+import androidx.compose.ui.input.pointer.motionEventSpy
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+
+@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
+@Composable
+fun CommunalTouchableSurface(
+ viewModel: CommunalViewModel,
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.() -> Unit,
+) {
+
+ val interactionSource = remember { MutableInteractionSource() }
+
+ Box(
+ modifier =
+ modifier
+ .combinedClickable(
+ onLongClick = viewModel::onLongClick,
+ onClick = viewModel::onClick,
+ interactionSource = interactionSource,
+ indication = null,
+ )
+ .onPreviewKeyEvent {
+ viewModel.signalUserInteraction()
+ false
+ }
+ .motionEventSpy { viewModel.signalUserInteraction() }
+ ) {
+ content()
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 33d2cc40cb06..4eae14b355bb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -16,10 +16,9 @@
package com.android.systemui.communal.ui.compose
-import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
+import androidx.compose.animation.core.spring
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
import androidx.compose.foundation.gestures.scrollBy
@@ -34,13 +33,10 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.input.pointer.pointerInteropFilter
-import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.toOffset
import androidx.compose.ui.unit.toSize
@@ -107,8 +103,7 @@ internal constructor(
get() =
draggingItemLayoutInfo?.let { item ->
draggingItemInitialOffset + draggingItemDraggedDelta - item.offset.toOffset()
- }
- ?: Offset.Zero
+ } ?: Offset.Zero
private val draggingItemLayoutInfo: LazyGridItemInfo?
get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex }
@@ -238,7 +233,6 @@ fun Modifier.dragContainer(
}
/** Wrap LazyGrid item with additional modifier needed for drag and drop. */
-@OptIn(ExperimentalComposeUiApi::class)
@ExperimentalFoundationApi
@Composable
fun LazyGridItemScope.DraggableItem(
@@ -267,25 +261,21 @@ fun LazyGridItemScope.DraggableItem(
alpha = itemAlpha
}
} else {
- Modifier.animateItemPlacement()
+ Modifier.animateItem()
}
+ // Animate the highlight alpha manually as alpha modifier (and AnimatedVisibility) clips the
+ // widget to bounds, which cuts off the highlight as we are drawing outside the widget bounds.
+ val alpha by
+ animateFloatAsState(
+ targetValue =
+ if ((dragging || selected) && !dragDropState.isDraggingToRemove) 1f else 0f,
+ animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
+ label = "Widget outline alpha"
+ )
+
Box(modifier) {
+ HighlightedItem(Modifier.matchParentSize(), alpha = alpha)
Box(draggingModifier) { content(dragging) }
- AnimatedVisibility(
- modifier =
- Modifier.matchParentSize()
- // Avoid taking focus away from the content when using explore-by-touch with
- // accessibility tools.
- .clearAndSetSemantics {}
- // Do not consume motion events in the highlighted item and pass them down to
- // the content.
- .pointerInteropFilter { false },
- visible = (dragging || selected) && !dragDropState.isDraggingToRemove,
- enter = fadeIn(),
- exit = fadeOut()
- ) {
- HighlightedItem(Modifier.matchParentSize())
- }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
new file mode 100644
index 000000000000..1ea73e144962
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.compose.section
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.AnimatedVisibilityScope
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.TouchApp
+import androidx.compose.material.icons.outlined.Widgets
+import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Popup
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.PopupType
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class CommunalPopupSection
+@Inject
+constructor(
+ private val viewModel: CommunalViewModel,
+) {
+
+ @Composable
+ fun Popup() {
+ val currentPopup by viewModel.currentPopup.collectAsStateWithLifecycle(initialValue = null)
+
+ if (currentPopup == PopupType.CtaTile) {
+ PopupOnDismissCtaTile(viewModel::onHidePopup)
+ }
+
+ AnimatedVisibility(
+ visible = currentPopup == PopupType.CustomizeWidgetButton,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ ButtonToEditWidgets(
+ onClick = {
+ viewModel.onHidePopup()
+ viewModel.onOpenWidgetEditor()
+ },
+ onDismissRequest = {
+ viewModel.onHidePopup()
+ viewModel.setSelectedKey(null)
+ }
+ )
+ }
+ }
+
+ @Composable
+ private fun AnimatedVisibilityScope.ButtonToEditWidgets(
+ onClick: () -> Unit,
+ onDismissRequest: () -> Unit,
+ ) {
+ Popup(
+ alignment = Alignment.TopCenter,
+ offset = IntOffset(0, 40),
+ onDismissRequest = onDismissRequest,
+ ) {
+ val colors = LocalAndroidColorScheme.current
+ Button(
+ modifier =
+ Modifier.height(56.dp)
+ .graphicsLayer { transformOrigin = TransformOrigin(0f, 0f) }
+ .animateEnterExit(
+ enter =
+ fadeIn(
+ initialAlpha = 0f,
+ animationSpec =
+ tween(durationMillis = 83, easing = LinearEasing)
+ ),
+ exit =
+ fadeOut(
+ animationSpec =
+ tween(
+ durationMillis = 83,
+ delayMillis = 167,
+ easing = LinearEasing
+ )
+ )
+ )
+ .background(colors.secondary, RoundedCornerShape(50.dp)),
+ onClick = onClick,
+ ) {
+ Row(
+ modifier =
+ Modifier.animateEnterExit(
+ enter =
+ fadeIn(
+ animationSpec =
+ tween(
+ durationMillis = 167,
+ delayMillis = 83,
+ easing = LinearEasing
+ )
+ ),
+ exit =
+ fadeOut(
+ animationSpec =
+ tween(durationMillis = 167, easing = LinearEasing)
+ )
+ )
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.Widgets,
+ contentDescription =
+ stringResource(R.string.button_to_configure_widgets_text),
+ tint = colors.onSecondary,
+ modifier = Modifier.size(20.dp)
+ )
+ Spacer(modifier = Modifier.size(8.dp))
+ Text(
+ text = stringResource(R.string.button_to_configure_widgets_text),
+ style = MaterialTheme.typography.titleSmall,
+ color = colors.onSecondary
+ )
+ }
+ }
+ }
+ }
+
+ @Composable
+ private fun PopupOnDismissCtaTile(onDismissRequest: () -> Unit) {
+ Popup(
+ alignment = Alignment.TopCenter,
+ offset = IntOffset(0, 40),
+ onDismissRequest = onDismissRequest
+ ) {
+ val colors = LocalAndroidColorScheme.current
+ Row(
+ modifier =
+ Modifier.height(56.dp)
+ .background(colors.secondary, RoundedCornerShape(50.dp))
+ .padding(16.dp),
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.TouchApp,
+ contentDescription = stringResource(R.string.popup_on_dismiss_cta_tile_text),
+ tint = colors.onSecondary,
+ modifier = Modifier.size(20.dp)
+ )
+ Spacer(modifier = Modifier.size(8.dp))
+ Text(
+ text = stringResource(R.string.popup_on_dismiss_cta_tile_text),
+ style = MaterialTheme.typography.titleSmall,
+ color = colors.onSecondary,
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 69124c1f6dad..377b02bfd86f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -1088,8 +1088,8 @@ private inline fun <T> computeValue(
// range.
val directionSign = if (transition.isUpOrLeft) -1 else 1
val isToScene = overscroll.scene == transition.toScene
- val overscrollProgress = transition.progress.let { if (isToScene) it - 1f else it }
- val progress = directionSign * overscrollProgress
+ val linearProgress = transition.progress.let { if (isToScene) it - 1f else it }
+ val progress = directionSign * overscroll.progressConverter(linearProgress)
val rangeProgress = propertySpec.range?.progress(progress) ?: progress
// Interpolate between the value at rest and the over scrolled value.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index 0f6a1d276578..e30dd356f1be 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -48,7 +48,8 @@ internal constructor(
) {
private val transitionCache =
mutableMapOf<
- SceneKey, MutableMap<SceneKey, MutableMap<TransitionKey?, TransitionSpecImpl>>
+ SceneKey,
+ MutableMap<SceneKey, MutableMap<TransitionKey?, TransitionSpecImpl>>
>()
private val overscrollCache =
@@ -87,8 +88,7 @@ internal constructor(
return transition(from, to, key) {
(it.from == to && it.to == null) || (it.to == from && it.from == null)
}
- ?.reversed()
- ?: defaultTransition(from, to)
+ ?.reversed() ?: defaultTransition(from, to)
}
private fun transition(
@@ -257,12 +257,24 @@ interface OverscrollSpec {
/** The [TransformationSpec] associated to this [OverscrollSpec]. */
val transformationSpec: TransformationSpec
+
+ /**
+ * Function that takes a linear overscroll progress value ranging from 0 to +/- infinity and
+ * outputs the desired **overscroll progress value**.
+ *
+ * When the progress value is:
+ * - 0, the user is not overscrolling.
+ * - 1, the user overscrolled by exactly the [OverscrollBuilder.distance].
+ * - Greater than 1, the user overscrolled more than the [OverscrollBuilder.distance].
+ */
+ val progressConverter: (Float) -> Float
}
internal class OverscrollSpecImpl(
override val scene: SceneKey,
override val orientation: Orientation,
override val transformationSpec: TransformationSpecImpl,
+ override val progressConverter: (Float) -> Float,
) : OverscrollSpec
/**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 465a410a6bdb..89ed8d6d37e2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -185,6 +185,17 @@ interface TransitionBuilder : BaseTransitionBuilder {
@TransitionDsl
interface OverscrollBuilder : BaseTransitionBuilder {
+ /**
+ * Function that takes a linear overscroll progress value ranging from 0 to +/- infinity and
+ * outputs the desired **overscroll progress value**.
+ *
+ * When the progress value is:
+ * - 0, the user is not overscrolling.
+ * - 1, the user overscrolled by exactly the [distance].
+ * - Greater than 1, the user overscrolled more than the [distance].
+ */
+ var progressConverter: (Float) -> Float
+
/** Translate the element(s) matching [matcher] by ([x], [y]) pixels. */
fun translate(
matcher: ElementMatcher,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 802ab1f2eebb..1e67aa947559 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -81,16 +81,20 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
orientation: Orientation,
builder: OverscrollBuilder.() -> Unit
): OverscrollSpec {
- fun transformationSpec(): TransformationSpecImpl {
- val impl = OverscrollBuilderImpl().apply(builder)
- return TransformationSpecImpl(
- progressSpec = snap(),
- swipeSpec = null,
- distance = impl.distance,
- transformations = impl.transformations,
+ val impl = OverscrollBuilderImpl().apply(builder)
+ val spec =
+ OverscrollSpecImpl(
+ scene = scene,
+ orientation = orientation,
+ transformationSpec =
+ TransformationSpecImpl(
+ progressSpec = snap(),
+ swipeSpec = null,
+ distance = impl.distance,
+ transformations = impl.transformations,
+ ),
+ progressConverter = impl.progressConverter
)
- }
- val spec = OverscrollSpecImpl(scene, orientation, transformationSpec())
transitionOverscrollSpecs.add(spec)
return spec
}
@@ -231,6 +235,8 @@ internal class TransitionBuilderImpl : BaseTransitionBuilderImpl(), TransitionBu
}
internal open class OverscrollBuilderImpl : BaseTransitionBuilderImpl(), OverscrollBuilder {
+ override var progressConverter: (Float) -> Float = { it }
+
override fun translate(
matcher: ElementMatcher,
x: OverscrollScope.() -> Float,
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index fcdf76ef8737..2de6faaccca2 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -889,6 +889,63 @@ class ElementTest {
}
@Test
+ fun elementTransitionWithDistanceDuringOverscrollWithProgressConverter() {
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+ var animatedFloat = 0f
+ val state =
+ setupOverscrollScenario(
+ layoutWidth = layoutWidth,
+ layoutHeight = layoutHeight,
+ sceneTransitions = {
+ overscroll(SceneB, Orientation.Vertical) {
+ // Overscroll progress will be halved
+ progressConverter = { it / 2f }
+
+ // On overscroll 100% -> Foo should translate by layoutHeight
+ translate(TestElements.Foo, y = { absoluteDistance })
+ }
+ },
+ firstScroll = 1f, // 100% scroll
+ animatedFloatRange = 0f..100f,
+ onAnimatedFloat = { animatedFloat = it },
+ )
+
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
+ fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+ assertThat(animatedFloat).isEqualTo(100f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ val transition = assertThat(state.transitionState).isTransition()
+ assertThat(animatedFloat).isEqualTo(100f)
+
+ // Scroll 200% (100% scroll + 100% overscroll)
+ assertThat(transition).hasProgress(2f)
+ assertThat(transition).hasOverscrollSpec()
+
+ // Overscroll progress is halved, we are at 50% of the overscroll progress.
+ fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 0.5f)
+ assertThat(animatedFloat).isEqualTo(100f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 300% (100% scroll + 200% overscroll)
+ assertThat(transition).hasProgress(3f)
+ assertThat(transition).hasOverscrollSpec()
+
+ // Overscroll progress is halved, we are at 100% of the overscroll progress.
+ fooElement.assertTopPositionInRootIsEqualTo(layoutHeight)
+ assertThat(animatedFloat).isEqualTo(100f)
+ }
+
+ @Test
fun elementTransitionWithDistanceDuringOverscrollBouncing() {
val layoutWidth = 200.dp
val layoutHeight = 400.dp
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 7a5f81c1ed29..51991dec39ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -75,6 +75,7 @@ import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
+import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -91,6 +92,7 @@ import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -154,13 +156,14 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
context.resources,
kosmos.keyguardTransitionInteractor,
kosmos.keyguardInteractor,
+ mock<KeyguardIndicationController>(),
kosmos.communalSceneInteractor,
kosmos.communalInteractor,
kosmos.communalSettingsInteractor,
kosmos.communalTutorialInteractor,
kosmos.shadeInteractor,
mediaHost,
- logcatLogBuffer("CommunalViewModelTest"),
+ logcatLogBuffer("CommunalViewModelTest")
)
}
@@ -358,7 +361,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
val currentPopup by collectLastValue(underTest.currentPopup)
assertThat(currentPopup).isNull()
- underTest.onShowCustomizeWidgetButton()
+ underTest.onLongClick()
assertThat(currentPopup).isEqualTo(PopupType.CustomizeWidgetButton)
advanceTimeBy(POPUP_AUTO_HIDE_TIMEOUT_MS)
assertThat(currentPopup).isNull()
@@ -370,7 +373,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
val currentPopup by collectLastValue(underTest.currentPopup)
- underTest.onShowCustomizeWidgetButton()
+ underTest.onLongClick()
assertThat(currentPopup).isEqualTo(PopupType.CustomizeWidgetButton)
underTest.onHidePopup()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index f43fa5048298..24672ebe6134 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -183,7 +183,10 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
)
underTest.addSelectedUserMediaEntry(playingData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId))
+ underTest.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(playingInstanceId),
+ false
+ )
verify(smartspaceLogger)
.logSmartspaceCardReceived(
@@ -193,7 +196,10 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
)
underTest.addSelectedUserMediaEntry(remoteData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(remoteInstanceId))
+ underTest.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(remoteInstanceId),
+ false
+ )
verify(smartspaceLogger)
.logSmartspaceCardReceived(
@@ -442,7 +448,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
reset(smartspaceLogger)
underTest.addSelectedUserMediaEntry(data)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId), false)
verify(smartspaceLogger)
.logSmartspaceCardReceived(data.smartspaceId, data.appUid, cardinality = 2)
@@ -451,7 +457,8 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
underTest.addSelectedUserMediaEntry(data)
underTest.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(instanceId, receivedSmartspaceCardLatency = 123)
+ MediaDataLoadingModel.Loaded(instanceId, receivedSmartspaceCardLatency = 123),
+ true
)
verify(smartspaceLogger)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index c62195fafd8c..414974cc2941 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.media.controls.domain.interactor
import android.R
import android.graphics.drawable.Icon
+import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
@@ -38,12 +39,19 @@ import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
+import com.android.systemui.media.controls.util.SmallHash
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -52,7 +60,11 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val mediaFilterRepository: MediaFilterRepository = kosmos.mediaFilterRepository
+ private val mediaFilterRepository: MediaFilterRepository =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaFilterRepository
+ }
private val mediaRecommendationsInteractor: MediaRecommendationsInteractor =
kosmos.mediaRecommendationsInteractor
val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
@@ -63,6 +75,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
packageName = PACKAGE_NAME,
recommendations = MediaTestHelper.getValidRecommendationList(icon),
)
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
private val underTest: MediaCarouselInteractor = kosmos.mediaCarouselInteractor
@@ -153,6 +166,18 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
MediaCommonModel.MediaControl(mediaLoadingModel, true)
)
.inOrder()
+
+ underTest.logSmartspaceSeenCard(0, 1, false)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ SmallHash.hash(mediaRecommendation.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ 2,
+ true
+ )
}
@Test
@@ -239,7 +264,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
.inOrder()
mediaFilterRepository.addSelectedUserMediaEntry(data.copy(isPlaying = true))
- mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
assertThat(currentMedia)
.containsExactly(
@@ -249,9 +274,83 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
.inOrder()
}
+ @Test
+ fun loadMediaAndRecommendation_logSmartspaceSeenCard() {
+ val instanceId = InstanceId.fakeInstanceId(123)
+ val data =
+ MediaData(
+ active = true,
+ instanceId = instanceId,
+ packageName = PACKAGE_NAME,
+ notificationKey = KEY
+ )
+ val smartspaceLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
+ val mediaLoadingModel = MediaDataLoadingModel.Loaded(instanceId)
+
+ mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
+ underTest.logSmartspaceSeenCard(0, 1, false)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ data.smartspaceId,
+ data.appUid,
+ surface = SURFACE,
+ 1
+ )
+
+ reset(smartspaceLogger)
+ mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
+ underTest.logSmartspaceSeenCard(0, 1, true)
+
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ data.smartspaceId,
+ data.appUid,
+ surface = SURFACE,
+ 2
+ )
+
+ reset(smartspaceLogger)
+ mediaFilterRepository.setRecommendation(mediaRecommendation)
+ mediaFilterRepository.setRecommendationsLoadingState(smartspaceLoadingModel)
+ underTest.logSmartspaceSeenCard(1, 1, true)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ SmallHash.hash(mediaRecommendation.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ 2,
+ true,
+ rank = 1
+ )
+
+ reset(smartspaceLogger)
+ mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addMediaDataLoadingState(
+ mediaLoadingModel.copy(receivedSmartspaceCardLatency = 1)
+ )
+ underTest.logSmartspaceSeenCard(0, 1, true)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ data.smartspaceId,
+ data.appUid,
+ surface = SURFACE,
+ 2
+ )
+ }
+
companion object {
private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
private const val PACKAGE_NAME = "com.android.example"
private const val KEY = "key"
+ private const val SURFACE = 4
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
index 856c3fe19d73..d594f3a2f932 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.media.controls.domain.interactor
+import android.R
import android.app.PendingIntent
+import android.graphics.drawable.Icon
import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -30,6 +32,7 @@ import com.android.systemui.bluetooth.mockBroadcastDialogController
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.MediaTestHelper
import com.android.systemui.media.controls.data.repository.mediaDataRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
@@ -38,7 +41,12 @@ import com.android.systemui.media.controls.domain.pipeline.interactor.mediaContr
import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.mediaInstanceId
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.media.mediaOutputDialogManager
import com.android.systemui.mockActivityIntentHelper
import com.android.systemui.plugins.activityStarter
@@ -49,6 +57,8 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
@@ -63,11 +73,23 @@ class MediaControlInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
+ private val mediaDataFilter: MediaDataFilterImpl =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaDataFilter
+ }
private val activityStarter = kosmos.activityStarter
private val keyguardStateController = kosmos.keyguardStateController
private val instanceId: InstanceId = kosmos.mediaInstanceId
private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
+ private val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
+ private val mediaRecommendation =
+ SmartspaceMediaData(
+ targetId = KEY_MEDIA_SMARTSPACE,
+ isActive = true,
+ recommendations = MediaTestHelper.getValidRecommendationList(icon),
+ )
private val underTest: MediaControlInteractor =
with(kosmos) {
@@ -124,13 +146,15 @@ class MediaControlInteractorTest : SysuiTestCase() {
val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
val expandable = mock<Expandable>()
- underTest.startClickIntent(expandable, clickIntent)
+ underTest.startClickIntent(expandable, clickIntent, SMARTSPACE_CARD_CLICK_EVENT, 1)
verify(clickIntent).send(any<Bundle>())
}
@Test
fun startClickIntent_hideOverLockscreen() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
whenever(keyguardStateController.isShowing).thenReturn(false)
val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
@@ -138,8 +162,20 @@ class MediaControlInteractorTest : SysuiTestCase() {
val activityController = mock<ActivityTransitionAnimator.Controller>()
whenever(expandable.activityTransitionController(any())).thenReturn(activityController)
- underTest.startClickIntent(expandable, clickIntent)
-
+ val mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, mediaRecommendation, true)
+ mediaDataFilter.onMediaDataLoaded(KEY, null, mediaData)
+ underTest.startClickIntent(expandable, clickIntent, SMARTSPACE_CARD_CLICK_EVENT, 1)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_CLICK_EVENT,
+ mediaData.smartspaceId,
+ mediaData.appUid,
+ surface = SURFACE,
+ cardinality = 2,
+ rank = 1
+ )
verify(activityStarter)
.postStartActivityDismissingKeyguard(eq(clickIntent), eq(activityController))
}
@@ -217,17 +253,62 @@ class MediaControlInteractorTest : SysuiTestCase() {
}
@Test
- fun removeMediaControl() {
+ fun removeMediaControl_noRecommendation() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+ val listener = mock<MediaDataProcessor.Listener>()
+ kosmos.mediaDataProcessor.addInternalListener(listener)
+
+ val mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+ kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData)
+ kosmos.mediaDataFilter.onMediaDataLoaded(KEY, null, mediaData)
+
+ underTest.removeMediaControl(null, instanceId, 0L, SMARTSPACE_CARD_DISMISS_EVENT, 1)
+ kosmos.fakeExecutor.advanceClockToNext()
+ kosmos.fakeExecutor.runAllReady()
+
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardUIEvent(
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean()
+ )
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
+ }
+
+ @Test
+ fun removeMediaControl_recommendationsExist() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
val listener = mock<MediaDataProcessor.Listener>()
kosmos.mediaDataProcessor.addInternalListener(listener)
- var mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+ val mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, mediaRecommendation, true)
+ mediaDataFilter.onMediaDataLoaded(KEY, null, mediaData)
- underTest.removeMediaControl(null, instanceId, 0L)
+ underTest.removeMediaControl(null, instanceId, 0L, SMARTSPACE_CARD_DISMISS_EVENT, 1)
kosmos.fakeExecutor.advanceClockToNext()
kosmos.fakeExecutor.runAllReady()
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ mediaData.smartspaceId,
+ mediaData.appUid,
+ surface = SURFACE,
+ cardinality = 2,
+ rank = 1
+ )
verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
}
@@ -238,5 +319,7 @@ class MediaControlInteractorTest : SysuiTestCase() {
private const val APP_NAME = "app"
private const val ARTIST = "artist"
private const val ARTIST_2 = "artist2"
+ private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
+ private const val SURFACE = 4
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
index 9656511817dc..8af7e1dbe59b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
@@ -21,6 +21,7 @@ import android.content.ComponentName
import android.content.Intent
import android.content.applicationContext
import android.graphics.drawable.Icon
+import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -40,10 +41,14 @@ import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
import com.android.systemui.media.controls.shared.model.MediaRecModel
import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.util.SmallHash
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.plugins.activityStarter
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -53,6 +58,7 @@ import org.junit.runner.RunWith
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.kotlin.eq
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -62,7 +68,11 @@ class MediaRecommendationsInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos().apply { applicationContext = spyContext }
private val testScope = kosmos.testScope
- private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
+ private val mediaDataFilter: MediaDataFilterImpl =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaDataFilter
+ }
private val activityStarter = kosmos.activityStarter
private val icon: Icon = Icon.createWithResource(context, R.drawable.ic_media_play)
private val smartspaceMediaData: SmartspaceMediaData =
@@ -72,6 +82,7 @@ class MediaRecommendationsInteractorTest : SysuiTestCase() {
packageName = PACKAGE_NAME,
recommendations = MediaTestHelper.getValidRecommendationList(icon),
)
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
private val underTest: MediaRecommendationsInteractor =
with(kosmos) {
@@ -138,8 +149,24 @@ class MediaRecommendationsInteractorTest : SysuiTestCase() {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- underTest.removeMediaRecommendations(KEY_MEDIA_SMARTSPACE, intent, 0)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
+ underTest.removeMediaRecommendations(
+ KEY_MEDIA_SMARTSPACE,
+ intent,
+ 0,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ 1
+ )
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ SmallHash.hash(smartspaceMediaData.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ cardinality = 1,
+ isRecommendationCard = true,
+ )
verify(kosmos.mockBroadcastSender).sendBroadcast(eq(intent))
}
@@ -151,7 +178,13 @@ class MediaRecommendationsInteractorTest : SysuiTestCase() {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.component = ComponentName(PACKAGE_NAME, EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME)
- underTest.removeMediaRecommendations(KEY_MEDIA_SMARTSPACE, intent, 0)
+ underTest.removeMediaRecommendations(
+ KEY_MEDIA_SMARTSPACE,
+ intent,
+ 0,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ 1
+ )
verify(spyContext).startActivity(eq(intent))
}
@@ -171,13 +204,26 @@ class MediaRecommendationsInteractorTest : SysuiTestCase() {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- underTest.startClickIntent(expandable, intent)
-
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
+ underTest.startClickIntent(expandable, intent, SMARTSPACE_CARD_CLICK_EVENT, 1, 2, 3)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_CLICK_EVENT,
+ SmallHash.hash(smartspaceMediaData.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ cardinality = 1,
+ isRecommendationCard = true,
+ interactedSubcardRank = 2,
+ interactedSubcardCardinality = 3
+ )
verify(spyContext).startActivity(eq(intent))
}
companion object {
private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
private const val PACKAGE_NAME = "com.example.app"
+ private const val SURFACE = 4
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
index 71685a4354bf..005424ba599e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
@@ -47,7 +47,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> assertThat(commonViewModel).isEqualTo(mediaControl) },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -66,7 +66,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -85,7 +85,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel -> assertThat(commonViewModel).isNotEqualTo(mediaControl) },
+ { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaControl) },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -104,7 +104,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel -> assertThat(commonViewModel).isNotEqualTo(mediaRecs) },
+ { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaRecs) },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -124,7 +124,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaControl1) },
)
@@ -145,7 +145,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
)
@@ -164,7 +164,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaControl) },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -183,7 +183,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
index cb4e2d377048..16c70901eacc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
@@ -28,12 +28,14 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.ui.viewmodel.notificationsShadeSceneViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -56,7 +58,7 @@ class NotificationsShadeSceneViewModelTest : SysuiTestCase() {
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
- private val underTest = kosmos.notificationsShadeSceneViewModel
+ private val underTest by lazy { kosmos.notificationsShadeSceneViewModel }
@Test
fun upTransitionSceneKey_deviceLocked_lockscreen() =
@@ -65,11 +67,23 @@ class NotificationsShadeSceneViewModelTest : SysuiTestCase() {
lockDevice()
assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Down)).isNull()
assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
.isEqualTo(Scenes.Lockscreen)
}
@Test
+ fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
+ testScope.runTest {
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ lockDevice()
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+ assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun upTransitionSceneKey_deviceUnlocked_gone() =
testScope.runTest {
val destinationScenes by collectLastValue(underTest.destinationScenes)
@@ -77,6 +91,33 @@ class NotificationsShadeSceneViewModelTest : SysuiTestCase() {
unlockDevice()
assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Down)).isNull()
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
+ testScope.runTest {
+ kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ lockDevice()
+
+ assertThat(destinationScenes?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Up)).isNull()
+ assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
+ .isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
+ testScope.runTest {
+ kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ lockDevice()
+ unlockDevice()
+
+ assertThat(destinationScenes?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Up)).isNull()
assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt
index 7c95dfe61a54..8ac5b6c09e73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data
+package com.android.systemui.qs.panels.data.repository
import android.content.Context
import android.content.SharedPreferences
@@ -24,8 +24,6 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.panels.data.repository.QSPreferencesRepository
-import com.android.systemui.qs.panels.data.repository.qsPreferencesRepository
import com.android.systemui.settings.userFileManager
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
index b206f56f95a6..b206f56f95a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
index 7ad904e9b436..7ad904e9b436 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
index 1e2e82ffeca5..1e2e82ffeca5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index d43d50ab0dc4..e01ffa655a1e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -32,6 +32,7 @@ import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
@@ -160,6 +161,37 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
}
@Test
+ fun destinations_whenNotCustomizing_withPreviousSceneLockscreen_butLockscreenDisabled() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ qsFlexiglassAdapter.setCustomizing(false)
+ val destinations by collectLastValue(underTest.destinationScenes)
+
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val backScene by collectLastValue(sceneBackInteractor.backScene)
+ val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+ sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+ sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(backScene).isNull()
+ assertThat(destinations)
+ .isEqualTo(
+ mapOf(
+ Back to UserActionResult(Scenes.Shade),
+ Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
+ Swipe(
+ fromSource = Edge.Bottom,
+ direction = SwipeDirection.Up,
+ ) to UserActionResult(SceneFamilies.Home)
+ )
+ )
+ assertThat(homeScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun destinations_whenNotCustomizing_authMethodSwipe_lockscreenNotDismissed() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
index ac67ac8956be..411a7a4c96f3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
@@ -28,12 +28,14 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.ui.viewmodel.quickSettingsShadeSceneViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -56,7 +58,7 @@ class QuickSettingsShadeSceneViewModelTest : SysuiTestCase() {
private val sceneInteractor = kosmos.sceneInteractor
private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
- private val underTest = kosmos.quickSettingsShadeSceneViewModel
+ private val underTest by lazy { kosmos.quickSettingsShadeSceneViewModel }
@Test
fun upTransitionSceneKey_deviceLocked_lockscreen() =
@@ -66,10 +68,23 @@ class QuickSettingsShadeSceneViewModelTest : SysuiTestCase() {
lockDevice()
assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Down)).isNull()
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
}
@Test
+ fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
+ testScope.runTest {
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+ lockDevice()
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+ assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(homeScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun upTransitionSceneKey_deviceUnlocked_gone() =
testScope.runTest {
val destinationScenes by collectLastValue(underTest.destinationScenes)
@@ -78,6 +93,34 @@ class QuickSettingsShadeSceneViewModelTest : SysuiTestCase() {
unlockDevice()
assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Down)).isNull()
+ assertThat(homeScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
+ testScope.runTest {
+ kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+ lockDevice()
+
+ assertThat(destinationScenes?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Up)).isNull()
+ assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
+ testScope.runTest {
+ kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+ lockDevice()
+ unlockDevice()
+
+ assertThat(destinationScenes?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Up)).isNull()
assertThat(homeScene).isEqualTo(Scenes.Gone)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index ec7150b5af2d..5242fe33a281 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.Idle
@@ -450,4 +451,16 @@ class SceneInteractorTest : SysuiTestCase() {
progress.value = 0.9f
assertThat(transitionValue).isEqualTo(0f)
}
+
+ @Test
+ fun changeScene_toGone_whenKeyguardDisabled_doesNotThrow() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+ underTest.changeScene(Scenes.Gone, "")
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 9edc3af6bb4e..b32d9d6d4c93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -26,22 +26,30 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.internal.logging.uiEventLoggerFake
+import com.android.internal.policy.IKeyguardDismissCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.falsingCollector
import com.android.systemui.classifier.falsingManager
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeTrustRepository
+import com.android.systemui.keyguard.dismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.sysUiState
@@ -483,6 +491,11 @@ class SceneContainerStartableTest : SysuiTestCase() {
QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0L
)
.isFalse()
+ assertThat(
+ sysUiState.flags and
+ QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED != 0L
+ )
+ .isFalse()
kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(false)
runCurrent()
@@ -496,6 +509,11 @@ class SceneContainerStartableTest : SysuiTestCase() {
QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0L
)
.isTrue()
+ assertThat(
+ sysUiState.flags and
+ QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED != 0L
+ )
+ .isTrue()
}
@Test
@@ -1339,6 +1357,152 @@ class SceneContainerStartableTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
}
+ @Test
+ fun switchToGone_whenKeyguardBecomesDisabled() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ fun switchToGone_whenKeyguardBecomesDisabled_whenOnShadeScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState(
+ initialSceneKey = Scenes.Shade,
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ underTest.start()
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ fun doesNotSwitchToGone_whenKeyguardBecomesDisabled_whenInLockdownMode() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+
+ kosmos.fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+ kosmos.fakeBiometricSettingsRepository.setIsUserInLockdown(true)
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun doesNotSwitchToGone_whenKeyguardBecomesDisabled_whenDeviceEntered() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState(
+ isDeviceUnlocked = true,
+ initialSceneKey = Scenes.Gone,
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isTrue()
+ underTest.start()
+ sceneInteractor.changeScene(Scenes.Shade, "")
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isTrue()
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ }
+
+ @Test
+ fun switchToLockscreen_whenKeyguardBecomesEnabled_afterHidingWhenDisabled() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun doesNotSwitchToLockscreen_whenKeyguardBecomesEnabled_ifAuthMethodBecameInsecure() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.None
+ )
+ runCurrent()
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ fun notifyKeyguardDismissCallbacks_whenUnlocking_onDismissSucceeded() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ underTest.start()
+ val dismissCallback: IKeyguardDismissCallback = mock()
+ kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
+
+ // Switch to bouncer and unlock device:
+ sceneInteractor.changeScene(Scenes.Bouncer, "")
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+ kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ kosmos.fakeExecutor.runAllReady()
+
+ verify(dismissCallback).onDismissSucceeded()
+ }
+
+ @Test
+ fun notifyKeyguardDismissCallbacks_whenLeavingBouncer_onDismissCancelled() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ underTest.start()
+ val dismissCallback: IKeyguardDismissCallback = mock()
+ kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
+
+ // Switch to bouncer:
+ sceneInteractor.changeScene(Scenes.Bouncer, "")
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+
+ // Return to lockscreen:
+ sceneInteractor.changeScene(Scenes.Lockscreen, "")
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ runCurrent()
+ kosmos.fakeExecutor.runAllReady()
+
+ verify(dismissCallback).onDismissCancelled()
+ }
+
private fun TestScope.emulateSceneTransition(
transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
toScene: SceneKey,
@@ -1381,15 +1545,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
isDeviceProvisioned: Boolean = true,
isInteractive: Boolean = true,
): MutableStateFlow<ObservableTransitionState> {
- if (authenticationMethod?.isSecure == true) {
- assert(isLockscreenEnabled) {
- "Lockscreen cannot be disabled while having a secure authentication method"
- }
- if (isDeviceUnlocked) {
- kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
- }
+ if (isDeviceUnlocked) {
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
}
check(initialSceneKey != Scenes.Gone || isDeviceUnlocked) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index c53cdf8cc44f..a0295c9f36b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -32,6 +32,7 @@ import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepositor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
@@ -139,6 +140,21 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
}
@Test
+ fun upTransitionSceneKey_keyguardDisabled_gone() =
+ testScope.runTest {
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+ assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
+ .isEqualTo(SceneFamilies.Home)
+ assertThat(homeScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
val destinationScenes by collectLastValue(underTest.destinationScenes)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index f06e04b70809..e73cae257c37 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -40,9 +40,11 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.mock
@@ -105,6 +107,7 @@ class StatusBarStateControllerImplTest(flags: FlagsParameterization) : SysuiTest
{ kosmos.shadeInteractor },
{ kosmos.deviceUnlockedInteractor },
{ kosmos.sceneInteractor },
+ { kosmos.sceneContainerOcclusionInteractor },
{ kosmos.keyguardClockInteractor },
) {
override fun createDarkAnimator(): ObjectAnimator {
@@ -336,6 +339,47 @@ class StatusBarStateControllerImplTest(flags: FlagsParameterization) : SysuiTest
}
@Test
+ @EnableSceneContainer
+ fun start_hydratesStatusBarState_whileOccluded() =
+ testScope.runTest {
+ var statusBarState = underTest.state
+ val listener =
+ object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ statusBarState = newState
+ }
+ }
+ underTest.addCallback(listener)
+
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ val isOccluded by
+ collectLastValue(kosmos.sceneContainerOcclusionInteractor.invisibleDueToOcclusion)
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ showWhenLockedActivityOnTop = true,
+ taskInfo = mock(),
+ )
+ runCurrent()
+ assertThat(isOccluded).isTrue()
+
+ // Call start to begin hydrating based on the scene framework:
+ underTest.start()
+
+ kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "reason")
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+
+ kosmos.sceneInteractor.changeScene(
+ toScene = Scenes.QuickSettings,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+ }
+
+ @Test
fun leaveOpenOnKeyguard_whenGone_isFalse() =
testScope.runTest {
underTest.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
index 1656a2e680da..5887f90036ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
@@ -20,8 +20,11 @@ import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
+import android.os.Handler
import android.os.RemoteException
import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.View
import android.widget.FrameLayout
import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
@@ -29,6 +32,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.LaunchableView
@@ -36,7 +40,6 @@ import com.android.systemui.assist.AssistManager
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.data.repository.FakeShadeRepository
@@ -51,11 +54,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.nullable
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
@@ -64,14 +62,19 @@ import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.nullable
import org.mockito.Mock
-import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
@ExperimentalCoroutinesApi
@SmallTest
@@ -132,22 +135,22 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
mainExecutor = mainExecutor,
communalSceneInteractor = communalSceneInteractor,
)
- whenever(userTracker.userHandle).thenReturn(UserHandle.OWNER)
- whenever(communalSceneInteractor.isIdleOnCommunal).thenReturn(MutableStateFlow(false))
+ `when`(userTracker.userHandle).thenReturn(UserHandle.OWNER)
+ `when`(communalSceneInteractor.isIdleOnCommunal).thenReturn(MutableStateFlow(false))
}
@Test
fun startPendingIntentDismissingKeyguard_keyguardShowing_dismissWithAction() {
val pendingIntent = mock(PendingIntent::class.java)
- whenever(pendingIntent.isActivity).thenReturn(true)
- whenever(keyguardStateController.isShowing).thenReturn(true)
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(pendingIntent.isActivity).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
underTest.startPendingIntentDismissingKeyguard(intent = pendingIntent, dismissShade = true)
mainExecutor.runAllReady()
verify(statusBarKeyguardViewManager)
- .dismissWithAction(any(OnDismissAction::class.java), eq(null), anyBoolean(), eq(null))
+ .dismissWithAction(any(), eq(null), anyBoolean(), eq(null))
}
@Test
@@ -160,10 +163,10 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
}
parent.addView(view)
val controller = ActivityTransitionAnimator.Controller.fromView(view)
- whenever(pendingIntent.isActivity).thenReturn(true)
- whenever(keyguardStateController.isShowing).thenReturn(true)
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
- whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
+ `when`(pendingIntent.isActivity).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
.thenReturn(true)
startPendingIntentMaybeDismissingKeyguard(
@@ -175,9 +178,9 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
verify(activityTransitionAnimator)
.startPendingIntentWithAnimation(
- nullable(),
+ nullable(ActivityTransitionAnimator.Controller::class.java),
eq(true),
- nullable(),
+ nullable(String::class.java),
eq(true),
any(),
)
@@ -193,10 +196,10 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
}
parent.addView(view)
val controller = ActivityTransitionAnimator.Controller.fromView(view)
- whenever(pendingIntent.isActivity).thenReturn(true)
- whenever(keyguardStateController.isShowing).thenReturn(true)
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
- whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
+ `when`(pendingIntent.isActivity).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
.thenReturn(false)
// extra activity options to set on pending intent
@@ -220,12 +223,12 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
eq(context),
eq(0),
eq(fillInIntent),
- nullable(),
- nullable(),
- nullable(),
+ nullable(PendingIntent.OnFinished::class.java),
+ nullable(Handler::class.java),
+ nullable(String::class.java),
bundleCaptor.capture()
)
- val options = ActivityOptions.fromBundle(bundleCaptor.value)
+ val options = ActivityOptions.fromBundle(bundleCaptor.firstValue)
assertThat(options.isPendingIntentBackgroundActivityLaunchAllowedByPermission).isFalse()
assertThat(options.splashScreenStyle).isEqualTo(SPLASH_SCREEN_STYLE_SOLID_COLOR)
}
@@ -245,6 +248,74 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
verify(centralSurfaces).getAnimatorControllerFromNotification(associatedView)
}
+ @EnableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
+ @Test
+ fun startPendingIntentDismissingKeyguard_transitionAnimator_animateOverOcclusion() {
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ val pendingIntent = mock(PendingIntent::class.java)
+ `when`(pendingIntent.isActivity).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardStateController.isOccluded).thenReturn(true)
+
+ underTest.startPendingIntentDismissingKeyguard(
+ intent = pendingIntent,
+ dismissShade = true,
+ animationController = controller,
+ showOverLockscreen = true,
+ skipLockscreenChecks = true
+ )
+ mainExecutor.runAllReady()
+
+ verify(activityTransitionAnimator)
+ .startPendingIntentWithAnimation(
+ nullable(ActivityTransitionAnimator.Controller::class.java),
+ eq(true),
+ nullable(String::class.java),
+ eq(true),
+ any(),
+ )
+ }
+
+ @DisableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
+ @Test
+ fun startPendingIntentDismissingKeyguard_transitionAnimator_doNotAnimateOverOcclusion() {
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ val pendingIntent = mock(PendingIntent::class.java)
+ `when`(pendingIntent.isActivity).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardStateController.isOccluded).thenReturn(true)
+
+ underTest.startPendingIntentDismissingKeyguard(
+ intent = pendingIntent,
+ dismissShade = true,
+ animationController = controller,
+ showOverLockscreen = true,
+ skipLockscreenChecks = true
+ )
+ mainExecutor.runAllReady()
+
+ verify(activityTransitionAnimator)
+ .startPendingIntentWithAnimation(
+ nullable(ActivityTransitionAnimator.Controller::class.java),
+ eq(false),
+ nullable(String::class.java),
+ eq(true),
+ any(),
+ )
+ }
+
@Test
fun startActivity_noUserHandleProvided_getUserHandle() {
val intent = mock(Intent::class.java)
@@ -254,13 +325,66 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
verify(userTracker).userHandle
}
+ @EnableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
+ @Test
+ fun startActivity_transitionAnimator_animateOverOcclusion() {
+ val intent = mock(Intent::class.java)
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardStateController.isOccluded).thenReturn(true)
+
+ mainExecutor.runAllReady()
+ underTest.startActivity(intent, true, controller, true, null)
+
+ verify(activityTransitionAnimator)
+ .startIntentWithAnimation(
+ nullable(ActivityTransitionAnimator.Controller::class.java),
+ eq(true),
+ nullable(String::class.java),
+ eq(true),
+ any(),
+ )
+ }
+
+ @DisableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
+ @Test
+ fun startActivity_transitionAnimator_doNotAnimateOverOcclusion() {
+ val intent = mock(Intent::class.java)
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardStateController.isOccluded).thenReturn(true)
+
+ mainExecutor.runAllReady()
+ underTest.startActivity(intent, true, controller, true, null)
+
+ verify(activityTransitionAnimator)
+ .startIntentWithAnimation(
+ nullable(ActivityTransitionAnimator.Controller::class.java),
+ eq(false),
+ nullable(String::class.java),
+ eq(true),
+ any(),
+ )
+ }
+
@Test
fun dismissKeyguardThenExecute_startWakeAndUnlock() {
- whenever(wakefulnessLifecycle.wakefulness)
- .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
- whenever(keyguardStateController.canDismissLockScreen()).thenReturn(true)
- whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
- whenever(dozeServiceHost.isPulsing).thenReturn(true)
+ `when`(wakefulnessLifecycle.wakefulness).thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
+ `when`(keyguardStateController.canDismissLockScreen()).thenReturn(true)
+ `when`(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
+ `when`(dozeServiceHost.isPulsing).thenReturn(true)
underTest.dismissKeyguardThenExecute({ true }, {}, false)
@@ -271,25 +395,20 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
@Test
fun dismissKeyguardThenExecute_keyguardIsShowing_dismissWithAction() {
val customMessage = "Enter your pin."
- whenever(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
underTest.dismissKeyguardThenExecute({ true }, {}, false, customMessage)
verify(statusBarKeyguardViewManager)
- .dismissWithAction(
- any(OnDismissAction::class.java),
- any(Runnable::class.java),
- eq(false),
- eq(customMessage)
- )
+ .dismissWithAction(any(), any(), eq(false), eq(customMessage))
}
@Test
fun dismissKeyguardThenExecute_awakeDreams() {
val customMessage = "Enter your pin."
var dismissActionExecuted = false
- whenever(keyguardStateController.isShowing).thenReturn(false)
- whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isDreaming).thenReturn(true)
underTest.dismissKeyguardThenExecute(
{
@@ -308,9 +427,9 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
@Test
@Throws(RemoteException::class)
fun executeRunnableDismissingKeyguard_dreaming_notShowing_awakenDreams() {
- whenever(keyguardStateController.isShowing).thenReturn(false)
- whenever(keyguardStateController.isOccluded).thenReturn(false)
- whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(false)
+ `when`(keyguardStateController.isOccluded).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isDreaming).thenReturn(true)
underTest.executeRunnableDismissingKeyguard(
runnable = {},
@@ -326,9 +445,9 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
@Test
@Throws(RemoteException::class)
fun executeRunnableDismissingKeyguard_notDreaming_notShowing_doNotAwakenDreams() {
- whenever(keyguardStateController.isShowing).thenReturn(false)
- whenever(keyguardStateController.isOccluded).thenReturn(false)
- whenever(keyguardUpdateMonitor.isDreaming).thenReturn(false)
+ `when`(keyguardStateController.isShowing).thenReturn(false)
+ `when`(keyguardStateController.isOccluded).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isDreaming).thenReturn(false)
underTest.executeRunnableDismissingKeyguard(
runnable = {},
diff --git a/packages/SystemUI/res/drawable/face_dialog_dark_to_checkmark.xml b/packages/SystemUI/res/drawable/face_dialog_dark_to_checkmark.xml
deleted file mode 100644
index fb30249d1351..000000000000
--- a/packages/SystemUI/res/drawable/face_dialog_dark_to_checkmark.xml
+++ /dev/null
@@ -1,637 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 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
- -->
-
-<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:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_0_G_N_2_T_0"
- android:translateX="30"
- android:translateY="30">
- <group
- android:name="_R_G_L_0_G"
- android:translateX="-30"
- android:translateY="-30">
- <group
- android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
- android:scaleX="0.08"
- android:scaleY="0.08"
- android:translateX="30.1"
- android:translateY="30.083">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:fillAlpha="0"
- android:fillColor="@color/biometric_dialog_face_checkmark"
- android:fillType="nonZero"
- android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
- android:trimPathStart="0"
- android:trimPathEnd="0"
- android:trimPathOffset="0" />
- </group>
- <group
- android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
- android:scaleX="0.08"
- android:scaleY="0.08"
- android:translateX="30.1"
- android:translateY="30.083">
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
- android:strokeWidth="20"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_face_checkmark"
- android:trimPathStart="0"
- android:trimPathEnd="0"
- android:trimPathOffset="0" />
- </group>
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_face_checkmark"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <group
- android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
- android:pivotX="1.05"
- android:pivotY="-9.891"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="29.044"
- android:translateY="41.647">
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:pathData=" M4.71 1.1 C3.71,2.12 2.32,2.75 0.79,2.75 C-2.25,2.75 -4.7,0.29 -4.7,-2.75 "
- android:strokeWidth="2"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- </group>
- <group
- android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="41.1"
- android:translateY="23.8">
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_5_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="18.6"
- android:translateY="23.8">
- <path
- android:name="_R_G_L_0_G_D_5_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_6_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="30.727"
- android:translateY="31.703">
- <path
- android:name="_R_G_L_0_G_D_6_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c " />
- </group>
- </group>
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="33"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="0.08"
- android:valueTo="0.08"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="33"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="0.08"
- android:valueTo="0.08"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:propertyName="scaleX"
- android:startOffset="33"
- android:valueFrom="0.08"
- android:valueTo="0.12789"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:propertyName="scaleY"
- android:startOffset="33"
- android:valueFrom="0.08"
- android:valueTo="0.12789"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="scaleX"
- android:startOffset="200"
- android:valueFrom="0.12789"
- android:valueTo="0.12241"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="scaleY"
- android:startOffset="200"
- android:valueFrom="0.12789"
- android:valueTo="0.12241"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleX"
- android:startOffset="300"
- android:valueFrom="0.12241"
- android:valueTo="0.125"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleY"
- android:startOffset="300"
- android:valueFrom="0.12241"
- android:valueTo="0.125"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="33"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="233"
- android:propertyName="trimPathEnd"
- android:startOffset="33"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="33"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="0.08"
- android:valueTo="0.08"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="33"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="0.08"
- android:valueTo="0.08"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:propertyName="scaleX"
- android:startOffset="33"
- android:valueFrom="0.08"
- android:valueTo="0.12789"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:propertyName="scaleY"
- android:startOffset="33"
- android:valueFrom="0.08"
- android:valueTo="0.12789"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="scaleX"
- android:startOffset="200"
- android:valueFrom="0.12789"
- android:valueTo="0.12241"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="scaleY"
- android:startOffset="200"
- android:valueFrom="0.12789"
- android:valueTo="0.12241"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleX"
- android:startOffset="300"
- android:valueFrom="0.12241"
- android:valueTo="0.125"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleY"
- android:startOffset="300"
- android:valueFrom="0.12241"
- android:valueTo="0.125"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="33"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="233"
- android:propertyName="trimPathEnd"
- android:startOffset="33"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:propertyName="strokeColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_accent"
- android:valueTo="@color/biometric_dialog_face_checkmark"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeColor"
- android:startOffset="67"
- android:valueFrom="@color/biometric_dialog_accent"
- android:valueTo="@color/biometric_dialog_face_checkmark"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.65"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.65"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_5_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_5_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_6_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_6_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="383"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_dark_to_error.xml b/packages/SystemUI/res/drawable/face_dialog_dark_to_error.xml
deleted file mode 100644
index 0c05019bf199..000000000000
--- a/packages/SystemUI/res/drawable/face_dialog_dark_to_error.xml
+++ /dev/null
@@ -1,473 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 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
- -->
-
-<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:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_0_G_N_1_T_0"
- android:translateX="30"
- android:translateY="30">
- <group
- android:name="_R_G_L_0_G"
- android:translateX="-30"
- android:translateY="-30">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_gray"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:strokeWidth="2"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_gray"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <group
- android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="41.1"
- android:translateY="23.8">
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="18.6"
- android:translateY="23.8">
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
- android:translateX="30.3"
- android:translateY="31.45">
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c " />
- </group>
- </group>
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="strokeColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeColor"
- android:startOffset="50"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="strokeColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeColor"
- android:startOffset="50"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="strokeWidth"
- android:startOffset="0"
- android:valueFrom="2"
- android:valueTo="2.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="pathData"
- android:startOffset="0"
- android:valueFrom="M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:valueTo="M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0.34"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5700000000000001"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillAlpha"
- android:startOffset="50"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:pathData="M 41.1,23.8C 40.547981774806985,23.08834635019302 38.34001822519301,20.24165364980698 37.788,19.53"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillAlpha"
- android:startOffset="50"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:pathData="M 18.6,23.8C 19.16757813692093,23.08502601385117 21.43742186307907,20.224973986148832 22.005,19.51"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillColor"
- android:startOffset="50"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:pathData="M 30.3,31.45C 30.3,31.07740886211395 30.3,31.82259113788605 30.3,31.45"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="150"
- android:pathData="M 30.3,31.45C 30.3,31.07740886211395 30.3,29.58759113788605 30.3,29.215"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="67">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:propertyName="pathData"
- android:startOffset="0"
- android:valueFrom="M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c "
- android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="150"
- android:propertyName="pathData"
- android:startOffset="67"
- android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
- android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-13.15 -1.5,-13.15 C-1.5,-13.15 0.9,-13.15 0.9,-13.15 C0.9,-13.15 0.9,3.25 0.9,3.25c "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="pathData"
- android:startOffset="217"
- android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-13.15 -1.5,-13.15 C-1.5,-13.15 0.9,-13.15 0.9,-13.15 C0.9,-13.15 0.9,3.25 0.9,3.25c "
- android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_error_to_idle.xml b/packages/SystemUI/res/drawable/face_dialog_error_to_idle.xml
deleted file mode 100644
index d3cee25a2146..000000000000
--- a/packages/SystemUI/res/drawable/face_dialog_error_to_idle.xml
+++ /dev/null
@@ -1,509 +0,0 @@
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<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:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group android:name="_R_G_L_0_G">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_error"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_error"
- android:trimPathStart="0.34"
- android:trimPathEnd="0.5700000000000001"
- android:trimPathOffset="0" />
- <group
- android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0"
- android:scaleX="0.3"
- android:scaleY="0.3"
- android:translateX="37.788"
- android:translateY="19.53">
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="0"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
- android:scaleX="0.3"
- android:scaleY="0.3"
- android:translateX="22.005"
- android:translateY="19.51">
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="0"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
- android:translateX="30.3"
- android:translateY="29.215">
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_error"
- android:fillType="nonZero"
- android:pathData=" M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c " />
- </group>
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="strokeColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeColor"
- android:startOffset="83"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="strokeColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeColor"
- android:startOffset="83"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="strokeWidth"
- android:startOffset="0"
- android:valueFrom="2.5"
- android:valueTo="2"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="pathData"
- android:startOffset="0"
- android:valueFrom="M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
- android:valueTo="M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0.34"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0.5700000000000001"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillAlpha"
- android:startOffset="83"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:pathData="M 37.788,19.53C 38.3400184636116,20.241653709411622 37.235981536388394,18.81834629058838 37.788,19.53"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:pathData="M 37.788,19.53C 38.3400184636116,20.241653709411622 40.5479815363884,23.08834629058838 41.1,23.8"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="50">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="0.3"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="50"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="0.3"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="117"
- android:propertyName="scaleX"
- android:startOffset="50"
- android:valueFrom="0.3"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="117"
- android:propertyName="scaleY"
- android:startOffset="50"
- android:valueFrom="0.3"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillAlpha"
- android:startOffset="83"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:pathData="M 22.005,19.51C 21.43742198228836,20.224974105358122 22.57257801771164,18.79502589464188 22.005,19.51"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:pathData="M 22.005,19.51C 21.43742198228836,20.224974105358122 19.16757801771164,23.08502589464188 18.6,23.8"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="50">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="0.3"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="50"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="0.3"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="117"
- android:propertyName="scaleX"
- android:startOffset="50"
- android:valueFrom="0.3"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="117"
- android:propertyName="scaleY"
- android:startOffset="50"
- android:valueFrom="0.3"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="fillColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillColor"
- android:startOffset="83"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="150"
- android:pathData="M 30.3,29.215C 30.3,29.58759101867676 30.3,31.077408981323238 30.3,31.45"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="pathData"
- android:startOffset="0"
- android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c "
- android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.321,0 0.67,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="133"
- android:propertyName="pathData"
- android:startOffset="83"
- android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
- android:valueTo="M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.568,0 0.456,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="267"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_idle_static.xml b/packages/SystemUI/res/drawable/face_dialog_idle_static.xml
deleted file mode 100644
index 6ad8e83989d3..000000000000
--- a/packages/SystemUI/res/drawable/face_dialog_idle_static.xml
+++ /dev/null
@@ -1,276 +0,0 @@
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<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:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_0_G"
- android:pivotX="30"
- android:pivotY="30"
- android:scaleX="1.03"
- android:scaleY="1.03">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1.0"
- android:strokeColor="@color/biometric_dialog_gray"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M33.75 42.75 C32.75,43.77 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:strokeWidth="2"
- android:strokeAlpha="1.0"
- android:strokeColor="@color/biometric_dialog_gray"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="1.0"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M39 23.8 C39,25 39.9,25.9 41.1,25.9 C42.2,25.9 43.2,25 43.2,23.8 C43.2,22.6 42.3,21.7 41.1,21.7 C39.9,21.7 39,22.6 39,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="1.0"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M16.5 23.8 C16.5,25 17.4,25.9 18.6,25.9 C19.8,25.9 20.7,25 20.7,23.8 C20.7,22.6 19.8,21.7 18.6,21.7 C17.4,21.7 16.5,22.6 16.5,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="1.0"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M33.33 34.95 C33.33,34.95 28.13,34.95 28.13,34.95 C28.13,34.95 28.13,32.95 28.13,32.95 C28.13,32.95 31.33,32.95 31.33,32.95 C31.33,32.95 31.33,28.45 31.33,28.45 C31.33,28.45 33.33,28.45 33.33,28.45 C33.33,28.45 33.33,34.95 33.33,34.95c " />
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.118,1.266 0.419,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="strokeAlpha"
- android:startOffset="500"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.118,1.266 0.419,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="strokeAlpha"
- android:startOffset="500"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.118,1.266 0.419,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="500"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.118,1.266 0.419,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="500"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.118,1.266 0.419,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="500"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1.03"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1.03"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="scaleX"
- android:startOffset="500"
- android:valueFrom="1"
- android:valueTo="1.03"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.204,0.46 0.568,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="scaleY"
- android:startOffset="500"
- android:valueFrom="1"
- android:valueTo="1.03"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.204,0.46 0.568,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="1017"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_pulse_dark_to_light.xml b/packages/SystemUI/res/drawable/face_dialog_pulse_dark_to_light.xml
deleted file mode 100644
index 427be1447679..000000000000
--- a/packages/SystemUI/res/drawable/face_dialog_pulse_dark_to_light.xml
+++ /dev/null
@@ -1,183 +0,0 @@
-<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:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_0_G"
- android:pivotX="30"
- android:pivotY="30"
- android:scaleX="1"
- android:scaleY="1">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M33.75 42.75 C32.75,43.77 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:strokeWidth="2"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M39 23.8 C39,25 39.9,25.9 41.1,25.9 C42.2,25.9 43.2,25 43.2,23.8 C43.2,22.6 42.3,21.7 41.1,21.7 C39.9,21.7 39,22.6 39,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M16.5 23.8 C16.5,25 17.4,25.9 18.6,25.9 C19.8,25.9 20.7,25 20.7,23.8 C20.7,22.6 19.8,21.7 18.6,21.7 C17.4,21.7 16.5,22.6 16.5,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M33.33 34.95 C33.33,34.95 28.13,34.95 28.13,34.95 C28.13,34.95 28.13,32.95 28.13,32.95 C28.13,32.95 31.33,32.95 31.33,32.95 C31.33,32.95 31.33,28.45 31.33,28.45 C31.33,28.45 33.33,28.45 33.33,28.45 C33.33,28.45 33.33,34.95 33.33,34.95c " />
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1.03"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1.03"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="350"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_pulse_light_to_dark.xml b/packages/SystemUI/res/drawable/face_dialog_pulse_light_to_dark.xml
deleted file mode 100644
index ab26408a2ed6..000000000000
--- a/packages/SystemUI/res/drawable/face_dialog_pulse_light_to_dark.xml
+++ /dev/null
@@ -1,183 +0,0 @@
-<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:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_0_G"
- android:pivotX="30"
- android:pivotY="30"
- android:scaleX="1.03"
- android:scaleY="1.03">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="0.5"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M33.75 42.75 C32.75,43.77 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:strokeWidth="2"
- android:strokeAlpha="0.5"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="0.5"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M39 23.8 C39,25 39.9,25.9 41.1,25.9 C42.2,25.9 43.2,25 43.2,23.8 C43.2,22.6 42.3,21.7 41.1,21.7 C39.9,21.7 39,22.6 39,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="0.5"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M16.5 23.8 C16.5,25 17.4,25.9 18.6,25.9 C19.8,25.9 20.7,25 20.7,23.8 C20.7,22.6 19.8,21.7 18.6,21.7 C17.4,21.7 16.5,22.6 16.5,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="0.5"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M33.33 34.95 C33.33,34.95 28.13,34.95 28.13,34.95 C28.13,34.95 28.13,32.95 28.13,32.95 C28.13,32.95 31.33,32.95 31.33,32.95 C31.33,32.95 31.33,28.45 31.33,28.45 C31.33,28.45 33.33,28.45 33.33,28.45 C33.33,28.45 33.33,34.95 33.33,34.95c " />
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="0.5"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="0.5"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="0.5"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="0.5"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="0.5"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1.03"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1.03"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="350"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_wink_from_dark.xml b/packages/SystemUI/res/drawable/face_dialog_wink_from_dark.xml
deleted file mode 100644
index 0cd542d51b51..000000000000
--- a/packages/SystemUI/res/drawable/face_dialog_wink_from_dark.xml
+++ /dev/null
@@ -1,208 +0,0 @@
-<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:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group android:name="_R_G_L_1_G">
- <path
- android:name="_R_G_L_1_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- </group>
- <group
- android:name="_R_G_L_0_G"
- android:pivotX="30"
- android:pivotY="30"
- android:rotation="0"
- android:scaleX="1"
- android:scaleY="1">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M33.75 42.75 C32.75,43.77 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:strokeWidth="2"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M39 23.8 C39,25 39.9,25.9 41.1,25.9 C42.2,25.9 43.2,25 43.2,23.8 C43.2,22.6 42.3,21.7 41.1,21.7 C39.9,21.7 39,22.6 39,23.8c " />
- <group
- android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="18.6"
- android:translateY="23.8">
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M33.33 34.95 C33.33,34.95 28.13,34.95 28.13,34.95 C28.13,34.95 28.13,32.95 28.13,32.95 C28.13,32.95 31.33,32.95 31.33,32.95 C31.33,32.95 31.33,28.45 31.33,28.45 C31.33,28.45 33.33,28.45 33.33,28.45 C33.33,28.45 33.33,34.95 33.33,34.95c " />
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="433"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="433"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.26"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleX"
- android:startOffset="433"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleY"
- android:startOffset="433"
- android:valueFrom="0.26"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="433"
- android:propertyName="rotation"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="-14"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="rotation"
- android:startOffset="433"
- android:valueFrom="-14"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.305,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="433"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1.06"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="433"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1.06"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleX"
- android:startOffset="433"
- android:valueFrom="1.06"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.305,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleY"
- android:startOffset="433"
- android:valueFrom="1.06"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.305,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="1017"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index beb16b38e6e0..154397d2b4a1 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -34,13 +34,13 @@
android:background="@drawable/ongoing_activity_chip_bg"
android:paddingStart="@dimen/ongoing_activity_chip_side_padding"
android:paddingEnd="@dimen/ongoing_activity_chip_side_padding"
- android:contentDescription="@string/ongoing_phone_call_content_description"
android:minWidth="@dimen/min_clickable_item_size"
>
<ImageView
android:src="@*android:drawable/ic_phone"
android:id="@+id/ongoing_activity_chip_icon"
+ android:contentDescription="@string/ongoing_phone_call_content_description"
android:layout_width="@dimen/ongoing_activity_chip_icon_size"
android:layout_height="@dimen/ongoing_activity_chip_icon_size"
android:tint="?android:attr/colorPrimary"
diff --git a/packages/SystemUI/res/raw/face_dialog_authenticating.json b/packages/SystemUI/res/raw/face_dialog_authenticating.json
index 4e25e6d933c4..896816ec2009 100644
--- a/packages/SystemUI/res/raw/face_dialog_authenticating.json
+++ b/packages/SystemUI/res/raw/face_dialog_authenticating.json
@@ -1 +1 @@
-{"v":"5.7.13","fr":60,"ip":0,"op":61,"w":64,"h":64,"nm":"face_scanning 3","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[32,32,0],"ix":2,"l":2},"a":{"a":0,"k":[27.25,27.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[95,95,100]},{"t":60,"s":[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,-1.243],[-1.244,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.244,0]],"v":[[-2.249,0.001],[0.001,2.251],[2.249,0.001],[0.001,-2.251]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[15.1,20.495],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.242,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.242,0]],"v":[[-2.249,0],[0.001,2.25],[2.249,0],[0.001,-2.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[39.4,20.495],"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":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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":[[2.814,3.523],[-2.814,3.523],[-2.814,1.363],[0.652,1.363],[0.652,-3.523],[2.814,-3.523]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.791,28.479],"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 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.154,0.15],[0,0],[0.117,-0.095],[0,0],[0.228,-0.121],[0.358,-0.103],[0.922,0.261],[0.3,0.16],[0.24,0.185],[0.14,0.139],[0.178,0.261],[0.143,0.451],[0,0],[0,0.494],[0,0],[-0.214,-0.676],[-0.392,-0.572],[-0.323,-0.317],[-0.228,-0.177],[-0.333,-0.179],[-0.503,-0.145],[-0.662,0],[-0.653,0.184],[-0.437,0.233],[-0.336,0.258],[0,0],[0,0]],"o":[[0,0],[-0.107,0.106],[0,0],[-0.24,0.185],[-0.301,0.16],[-0.92,0.261],[-0.357,-0.103],[-0.228,-0.121],[-0.158,-0.122],[-0.225,-0.221],[-0.272,-0.393],[0,0],[-0.147,-0.466],[0,0],[0,0.716],[0.206,0.656],[0.256,0.372],[0.204,0.201],[0.336,0.258],[0.436,0.233],[0.655,0.184],[0.662,0],[0.503,-0.145],[0.332,-0.179],[0,0],[0,0],[0.165,-0.136]],"v":[[6.094,1.465],[4.579,-0.076],[4.242,0.225],[4.124,0.315],[3.43,0.771],[2.439,1.165],[-0.342,1.165],[-1.331,0.771],[-2.027,0.315],[-2.48,-0.075],[-3.087,-0.801],[-3.712,-2.075],[-3.712,-2.075],[-3.934,-3.523],[-6.094,-3.523],[-5.771,-1.424],[-4.868,0.424],[-3.995,1.465],[-3.344,2.027],[-2.35,2.676],[-0.934,3.243],[1.049,3.523],[3.031,3.243],[4.449,2.676],[5.441,2.027],[5.482,1.997],[5.615,1.895]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[26.201,40.411],"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":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.398,0],[0,-13.4],[13.398,0],[0,13.4]],"o":[[13.398,0],[0,13.4],[-13.398,0],[0,-13.4]],"v":[[0,-24.3],[24.3,0],[0,24.3],[-24.3,0]],"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":[[14.904,0],[0,-14.904],[-14.904,0],[0,14.904]],"o":[[-14.904,0],[0,14.904],[14.904,0],[0,-14.904]],"v":[[0,-27],[-27,0],[0,27],[27,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.25,27.25],"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 5","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1200,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
+{"v":"5.7.13","fr":60,"ip":0,"op":61,"w":68,"h":68,"nm":"face_dialog_authenticating","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[27.25,27.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[95,95,100]},{"t":60,"s":[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,-1.243],[-1.244,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.244,0]],"v":[[-2.249,0.001],[0.001,2.251],[2.249,0.001],[0.001,-2.251]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[15.1,20.495],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.242,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.242,0]],"v":[[-2.249,0],[0.001,2.25],[2.249,0],[0.001,-2.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[39.4,20.495],"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":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[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":[[2.814,3.523],[-2.814,3.523],[-2.814,1.363],[0.652,1.363],[0.652,-3.523],[2.814,-3.523]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.791,28.479],"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 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.154,0.15],[0,0],[0.117,-0.095],[0,0],[0.228,-0.121],[0.358,-0.103],[0.922,0.261],[0.3,0.16],[0.24,0.185],[0.14,0.139],[0.178,0.261],[0.143,0.451],[0,0],[0,0.494],[0,0],[-0.214,-0.676],[-0.392,-0.572],[-0.323,-0.317],[-0.228,-0.177],[-0.333,-0.179],[-0.503,-0.145],[-0.662,0],[-0.653,0.184],[-0.437,0.233],[-0.336,0.258],[0,0],[0,0]],"o":[[0,0],[-0.107,0.106],[0,0],[-0.24,0.185],[-0.301,0.16],[-0.92,0.261],[-0.357,-0.103],[-0.228,-0.121],[-0.158,-0.122],[-0.225,-0.221],[-0.272,-0.393],[0,0],[-0.147,-0.466],[0,0],[0,0.716],[0.206,0.656],[0.256,0.372],[0.204,0.201],[0.336,0.258],[0.436,0.233],[0.655,0.184],[0.662,0],[0.503,-0.145],[0.332,-0.179],[0,0],[0,0],[0.165,-0.136]],"v":[[6.094,1.465],[4.579,-0.076],[4.242,0.225],[4.124,0.315],[3.43,0.771],[2.439,1.165],[-0.342,1.165],[-1.331,0.771],[-2.027,0.315],[-2.48,-0.075],[-3.087,-0.801],[-3.712,-2.075],[-3.712,-2.075],[-3.934,-3.523],[-6.094,-3.523],[-5.771,-1.424],[-4.868,0.424],[-3.995,1.465],[-3.344,2.027],[-2.35,2.676],[-0.934,3.243],[1.049,3.523],[3.031,3.243],[4.449,2.676],[5.441,2.027],[5.482,1.997],[5.615,1.895]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[26.201,40.411],"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":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.398,0],[0,-13.4],[13.398,0],[0,13.4]],"o":[[13.398,0],[0,13.4],[-13.398,0],[0,-13.4]],"v":[[0,-24.3],[24.3,0],[0,24.3],[-24.3,0]],"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":[[14.904,0],[0,-14.904],[-14.904,0],[0,14.904]],"o":[[-14.904,0],[0,14.904],[14.904,0],[0,-14.904]],"v":[[0,-27],[-27,0],[0,27],[27,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.25,27.25],"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 5","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1200,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_dark_to_checkmark.json b/packages/SystemUI/res/raw/face_dialog_dark_to_checkmark.json
new file mode 100644
index 000000000000..512ca30cf40d
--- /dev/null
+++ b/packages/SystemUI/res/raw/face_dialog_dark_to_checkmark.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":23,"w":68,"h":68,"nm":"face_dialog_dark_to_checkmark","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".green100","cl":"green100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,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":[[-116,-16.5],[-31.25,68.5],[108.75,-71.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.155],"y":[1]},"o":{"x":[0.292],"y":[0]},"t":2,"s":[0]},{"t":16,"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},{"ty":"st","c":{"a":0,"k":[0.76862745098,0.933333333333,0.81568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"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":[30.1,30.083],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.537,0.537],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":2,"s":[8,8]},{"i":{"x":[0.533,0.533],"y":[1,1]},"o":{"x":[0.441,0.441],"y":[0,0]},"t":12,"s":[12.789,12.789]},{"i":{"x":[0.486,0.486],"y":[1,1]},"o":{"x":[0.424,0.424],"y":[0,0]},"t":18,"s":[12.241,12.241]},{"t":22,"s":[12.5,12.5]}],"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":"Checkmark","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":23,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".green100","cl":"green100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - 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},{"ty":"st","c":{"a":0,"k":[0.768627464771,0.933333337307,0.815686285496,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":2,"s":[0]},{"t":7,"s":[100]}],"ix":4},"w":{"a":0,"k":2.5,"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":[30.05,30],"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":"Outline","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":23,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - 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},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.780392156863,0.980392156863,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":2,"s":[100]},{"t":7,"s":[0]}],"ix":4},"w":{"a":0,"k":2.5,"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":[30.05,30],"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":"Outline","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":23,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,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],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":3,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.727,31.703],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.287,0.287],"y":[0.12,0.12]},"t":0,"s":[100,100]},{"t":5,"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":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":3,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.6,23.8],"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.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":5,"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":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":3,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.1,23.8],"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.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":5,"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":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[0]},{"t":5,"s":[50]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[100]},{"t":5,"s":[50]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":3,"s":[0]}],"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.094,31.756],"ix":2},"a":{"a":0,"k":[1.05,-9.891],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":4,"s":[65,65]}],"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":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":23,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_dark_to_error.json b/packages/SystemUI/res/raw/face_dialog_dark_to_error.json
new file mode 100644
index 000000000000..9578cb40ac71
--- /dev/null
+++ b/packages/SystemUI/res/raw/face_dialog_dark_to_error.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":20,"w":68,"h":68,"nm":"face_dialog_dark_to_error","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".red100","cl":"red100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,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.4,"y":0},"t":0,"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":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":4,"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":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-3.25],[0.9,-3.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":13,"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":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-13.15],[0.9,-13.15]],"c":true}]},{"t":19,"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":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-11.712],[0.9,-11.712]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":4,"s":[30.3,31.45],"to":[0,-0.373],"ti":[0,0.373]},{"t":13,"s":[30.3,29.215]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[100]},{"t":4,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[18.6,23.8],"to":[0.568,-0.715],"ti":[-0.568,0.715]},{"t":4,"s":[22.005,19.51]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.999,0.999],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":4,"s":[30,30]}],"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":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[100]},{"t":4,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[41.1,23.8],"to":[-0.552,-0.712],"ti":[0.552,0.712]},{"t":4,"s":[37.788,19.53]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.999,0.999],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":4,"s":[30,30]}],"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":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false}]},{"t":13,"s":[{"i":[[0,0],[1.534,0],[1.832,-0.006]],"o":[[-0.948,-0.005],[-3.033,0],[0,0]],"v":[[5.734,-2.889],[0.961,-2.894],[-4.74,-2.887]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[0]},{"t":13,"s":[34]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[100]},{"t":13,"s":[57]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[2]},{"t":13,"s":[2.5]}],"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":[29.044,41.646],"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":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - 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},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"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":[30.05,30],"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":"Outline","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":4,"op":20,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,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.4,"y":0},"t":0,"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":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":4,"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":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-3.25],[0.9,-3.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":13,"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":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-13.15],[0.9,-13.15]],"c":true}]},{"t":19,"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":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-11.712],[0.9,-11.712]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":4,"s":[30.3,31.45],"to":[0,-0.373],"ti":[0,0.373]},{"t":13,"s":[30.3,29.215]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[100]},{"t":4,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[18.6,23.8],"to":[0.568,-0.715],"ti":[-0.568,0.715]},{"t":4,"s":[22.005,19.51]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.999,0.999],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":4,"s":[30,30]}],"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":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[100]},{"t":4,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[41.1,23.8],"to":[-0.552,-0.712],"ti":[0.552,0.712]},{"t":4,"s":[37.788,19.53]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.999,0.999],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":4,"s":[30,30]}],"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":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false}]},{"t":13,"s":[{"i":[[0,0],[1.534,0],[1.832,-0.006]],"o":[[-0.948,-0.005],[-3.033,0],[0,0]],"v":[[5.734,-2.889],[0.961,-2.894],[-4.74,-2.887]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[0]},{"t":13,"s":[34]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[100]},{"t":13,"s":[57]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[2]},{"t":13,"s":[2.5]}],"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":[29.044,41.646],"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":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - 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},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"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":[30.05,30],"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":"Outline","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":4,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_error_to_idle.json b/packages/SystemUI/res/raw/face_dialog_error_to_idle.json
new file mode 100644
index 000000000000..f94431492565
--- /dev/null
+++ b/packages/SystemUI/res/raw/face_dialog_error_to_idle.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":14,"w":68,"h":68,"nm":"face_error_to_idle","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.329,106.329,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.67,"y":1},"o":{"x":0.321,"y":0},"t":0,"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":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-11.712],[0.9,-11.712]],"c":true}]},{"i":{"x":0.456,"y":1},"o":{"x":0.568,"y":0},"t":5,"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":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-3.25],[0.9,-3.25]],"c":true}]},{"t":13,"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":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[30.3,29.215],"to":[0,0.373],"ti":[0,-0.373]},{"t":9,"s":[30.3,31.45]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":6,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":3,"s":[22.005,19.51],"to":[-0.568,0.715],"ti":[0.568,-0.715]},{"t":13,"s":[18.6,23.8]}],"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.4,0.4],"y":[0,0]},"t":3,"s":[30,30]},{"t":10,"s":[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":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":6,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":3,"s":[37.788,19.53],"to":[0.552,0.712],"ti":[-0.552,-0.712]},{"t":13,"s":[41.1,23.8]}],"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.4,0.4],"y":[0,0]},"t":3,"s":[30,30]},{"t":10,"s":[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":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[1.534,0],[1.832,-0.006]],"o":[[-0.948,-0.005],[-3.033,0],[0,0]],"v":[[5.734,-2.889],[0.961,-2.894],[-4.74,-2.887]],"c":false}]},{"t":13,"s":[{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[34]},{"t":13,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[57]},{"t":13,"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},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0.949019610882,0.721568644047,0.709803938866,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6,"s":[0.741176486015,0.75686275959,0.776470601559,1]},{"t":7,"s":[0.658823549747,0.780392169952,0.980392158031,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[2.5]},{"t":13,"s":[2]}],"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":[29.044,41.646],"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":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - 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},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0.949019610882,0.721568644047,0.709803938866,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6,"s":[0.741176486015,0.75686275959,0.776470601559,1]},{"t":7,"s":[0.658823549747,0.780392169952,0.980392158031,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"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":[30.05,30],"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":"Outline","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":6,"op":14,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".red100","cl":"red100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.329,106.329,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.67,"y":1},"o":{"x":0.321,"y":0},"t":0,"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":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-11.712],[0.9,-11.712]],"c":true}]},{"i":{"x":0.456,"y":1},"o":{"x":0.568,"y":0},"t":5,"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":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-3.25],[0.9,-3.25]],"c":true}]},{"t":13,"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":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0.949019610882,0.721568644047,0.709803938866,1]},{"t":6,"s":[0.741176486015,0.75686275959,0.776470601559,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[30.3,29.215],"to":[0,0.373],"ti":[0,-0.373]},{"t":9,"s":[30.3,31.45]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":6,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":3,"s":[22.005,19.51],"to":[-0.568,0.715],"ti":[0.568,-0.715]},{"t":13,"s":[18.6,23.8]}],"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.4,0.4],"y":[0,0]},"t":3,"s":[30,30]},{"t":10,"s":[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":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":6,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":3,"s":[37.788,19.53],"to":[0.552,0.712],"ti":[-0.552,-0.712]},{"t":13,"s":[41.1,23.8]}],"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.4,0.4],"y":[0,0]},"t":3,"s":[30,30]},{"t":10,"s":[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":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[1.534,0],[1.832,-0.006]],"o":[[-0.948,-0.005],[-3.033,0],[0,0]],"v":[[5.734,-2.889],[0.961,-2.894],[-4.74,-2.887]],"c":false}]},{"t":13,"s":[{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[34]},{"t":13,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[57]},{"t":13,"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},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0.949019610882,0.721568644047,0.709803938866,1]},{"t":6,"s":[0.741176486015,0.75686275959,0.776470601559,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[2.5]},{"t":13,"s":[2]}],"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":[29.044,41.646],"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":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - 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},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0.949019610882,0.721568644047,0.709803938866,1]},{"t":6,"s":[0.741176486015,0.75686275959,0.776470601559,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"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":[30.05,30],"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":"Outline","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":6,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_idle_static.json b/packages/SystemUI/res/raw/face_dialog_idle_static.json
new file mode 100644
index 000000000000..2d3a7a46e8f2
--- /dev/null
+++ b/packages/SystemUI/res/raw/face_dialog_idle_static.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":14,"w":68,"h":68,"nm":"face_dialog_idle_static","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.329,106.329,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],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.3,31.45],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.6,23.8],"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":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.1,23.8],"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":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - 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},{"ty":"st","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"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":[29.044,41.646],"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":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - 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},{"ty":"st","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"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":[30.05,30],"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":"Outline","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":14,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_wink_from_dark.json b/packages/SystemUI/res/raw/face_dialog_wink_from_dark.json
new file mode 100644
index 000000000000..05b55c99edc6
--- /dev/null
+++ b/packages/SystemUI/res/raw/face_dialog_wink_from_dark.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":61,"w":68,"h":68,"nm":"face_dialog_wink_from_dark","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.305],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":26,"s":[-14]},{"t":45,"s":[0]}],"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.4],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.305,0.305,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":26,"s":[106,106,100]},{"t":45,"s":[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],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.727,31.703],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.6,23.8],"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.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":26,"s":[100,26]},{"t":45,"s":[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":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.1,23.8],"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":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - 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},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.780392156863,0.980392156863,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.094,31.756],"ix":2},"a":{"a":0,"k":[1.05,-9.891],"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":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":80,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.329,106.329,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - 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},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.780392156863,0.980392156863,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"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":[30.05,30],"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":"Outline","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":80,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index abafb01cc646..31a999bf845e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -330,6 +330,8 @@
<!-- Button to stop a screen recording [CHAR LIMIT=35] -->
<string name="screenrecord_stop_dialog_button">Stop recording</string>
+ <!-- Content description for the status bar chip shown to the user when they're sharing their screen to another app on the device [CHAR LIMIT=NONE] -->
+ <string name="share_to_app_chip_accessibility_label">Sharing screen</string>
<!-- Title for a dialog shown to the user that will let them stop sharing their screen to another app on the device [CHAR LIMIT=50] -->
<string name="share_to_app_stop_dialog_title">Stop sharing screen?</string>
<!-- Text telling a user that they will stop sharing their screen if they click the "Stop sharing" button [CHAR LIMIT=100] -->
@@ -339,6 +341,8 @@
<!-- Button to stop screen sharing [CHAR LIMIT=35] -->
<string name="share_to_app_stop_dialog_button">Stop sharing</string>
+ <!-- Content description for the status bar chip shown to the user when they're casting their screen to a different device [CHAR LIMIT=NONE] -->
+ <string name="cast_to_other_device_chip_accessibility_label">Casting screen</string>
<!-- Title for a dialog shown to the user that will let them stop casting their screen to a different device [CHAR LIMIT=50] -->
<string name="cast_to_other_device_stop_dialog_title">Stop casting screen?</string>
<!-- Text telling a user that they will stop casting their screen to a different device if they click the "Stop casting" button [CHAR LIMIT=100] -->
@@ -3543,10 +3547,26 @@
shows the user which keyboard shortcuts they can use. The "System" shortcuts are for
example "Take a screenshot" or "Go back". [CHAR LIMIT=NONE] -->
<string name="shortcut_helper_category_system">System</string>
+ <!-- Title of the keyboard shortcut helper category "System controls". The helper is a component
+ that shows the user which keyboard shortcuts they can use. The "System controls" shortcuts
+ are for example "Go to home screen" or "App apps search". [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_category_system_controls">System controls</string>
+ <!-- Title of the keyboard shortcut helper category "System apps". The helper is a component
+ that shows the user which keyboard shortcuts they can use. The "System apps" shortcuts
+ are for example "Settings" or "Take a note". [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_category_system_apps">System apps</string>
<!-- Title of the keyboard shortcut helper category "Multitasking". The helper is a component
that shows the user which keyboard shortcuts they can use. The "Multitasking" shortcuts are
for example "Enter split screen". [CHAR LIMIT=NONE] -->
<string name="shortcut_helper_category_multitasking">Multitasking</string>
+ <!-- Title of the keyboard shortcut helper category "Recent apps". The helper is a component
+ that shows the user which keyboard shortcuts they can use. The "Recent apps" shortcuts are
+ for example "Cycle through recent apps". [CHAR LIMIT=NONE] -->
+ <string name="shortcutHelper_category_recent_apps">Recent apps</string>
+ <!-- Title of the keyboard shortcut helper category "Split screen". The helper is a component
+ that shows the user which keyboard shortcuts they can use. The "Split screen" shortcuts are
+ for example "Move current app to left split". [CHAR LIMIT=NONE] -->
+ <string name="shortcutHelper_category_split_screen">Split screen</string>
<!-- Title of the keyboard shortcut helper category "Input". The helper is a component
that shows the user which keyboard shortcuts they can use. The "Input" shortcuts are
the ones provided by the keyboard. Examples are "Access emoji" or "Switch to next language"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 78d4fc8d4c04..edf855fe5576 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -249,6 +249,9 @@ public class Task {
@ViewDebug.ExportedProperty(category="recents")
public boolean isVisible;
+ @ViewDebug.ExportedProperty(category = "recents")
+ public boolean isMinimized;
+
public Task() {
// Do nothing
}
@@ -283,6 +286,7 @@ public class Task {
positionInParent = other.positionInParent;
appBounds = other.appBounds;
isVisible = other.isVisible;
+ isMinimized = other.isMinimized;
}
/**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index 3cb5bc745ec7..ea7321627322 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -10,7 +10,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.wm.shell.util.SplitBounds;
/**
* Utility class to position the thumbnail in the TaskView
@@ -35,8 +34,6 @@ public class PreviewPositionHelper {
private final Matrix mMatrix = new Matrix();
private boolean mIsOrientationChanged;
- private SplitBounds mSplitBounds;
- private int mDesiredStagePosition;
public Matrix getMatrix() {
return mMatrix;
@@ -50,11 +47,6 @@ public class PreviewPositionHelper {
return mIsOrientationChanged;
}
- public void setSplitBounds(SplitBounds splitBounds, int desiredStagePosition) {
- mSplitBounds = splitBounds;
- mDesiredStagePosition = desiredStagePosition;
- }
-
/**
* Updates the matrix based on the provided parameters
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 5e76801aaca3..bf1f93f4693a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -28,6 +28,7 @@ import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
@@ -86,6 +87,12 @@ class ActiveUnlockConfig @Inject constructor(
* Trigger ActiveUnlock when the assistant is triggered.
*/
ASSISTANT,
+ /**
+ * Trigger ActiveUnlock on legacy unlock intents. This includes tapping on the empty space
+ * of the notification shadse when face auth is enrolled and re-trying face auth on the
+ * primary bouncer.
+ */
+ UNLOCK_INTENT_LEGACY,
}
/**
@@ -99,6 +106,7 @@ class ActiveUnlockConfig @Inject constructor(
}
private var requestActiveUnlockOnWakeup = false
+ private var requestActiveUnlockOnUnlockIntentLegacy = false
private var requestActiveUnlockOnUnlockIntent = false
private var requestActiveUnlockOnBioFail = false
@@ -110,6 +118,8 @@ class ActiveUnlockConfig @Inject constructor(
private val settingsObserver = object : ContentObserver(handler) {
private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
+ private val unlockIntentLegacyUri =
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY)
private val unlockIntentUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)
private val bioFailUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)
private val faceErrorsUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS)
@@ -164,6 +174,15 @@ class ActiveUnlockConfig @Inject constructor(
ACTIVE_UNLOCK_ON_WAKE, 0, selectedUserInteractor.getSelectedUserId()) == 1
}
+ if (selfChange || uris.contains(unlockIntentLegacyUri)) {
+ requestActiveUnlockOnUnlockIntentLegacy =
+ secureSettings.getIntForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY,
+ 0,
+ selectedUserInteractor.getSelectedUserId()
+ ) == 1
+ }
+
if (selfChange || uris.contains(unlockIntentUri)) {
requestActiveUnlockOnUnlockIntent = secureSettings.getIntForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0,
@@ -257,7 +276,7 @@ class ActiveUnlockConfig @Inject constructor(
*/
fun isActiveUnlockEnabled(): Boolean {
return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent ||
- requestActiveUnlockOnBioFail
+ requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntentLegacy
}
/**
@@ -299,15 +318,18 @@ class ActiveUnlockConfig @Inject constructor(
fun shouldAllowActiveUnlockFromOrigin(requestOrigin: ActiveUnlockRequestOrigin): Boolean {
return when (requestOrigin) {
ActiveUnlockRequestOrigin.WAKE -> requestActiveUnlockOnWakeup
-
+ ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY ->
+ requestActiveUnlockOnUnlockIntentLegacy
ActiveUnlockRequestOrigin.UNLOCK_INTENT ->
- requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup ||
- (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
-
+ requestActiveUnlockOnUnlockIntent ||
+ requestActiveUnlockOnUnlockIntentLegacy ||
+ requestActiveUnlockOnWakeup ||
+ (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
ActiveUnlockRequestOrigin.BIOMETRIC_FAIL ->
- requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent ||
- requestActiveUnlockOnWakeup
-
+ requestActiveUnlockOnBioFail ||
+ requestActiveUnlockOnUnlockIntentLegacy ||
+ requestActiveUnlockOnUnlockIntent ||
+ requestActiveUnlockOnWakeup
ActiveUnlockRequestOrigin.ASSISTANT -> isActiveUnlockEnabled()
}
}
@@ -345,6 +367,9 @@ class ActiveUnlockConfig @Inject constructor(
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("Settings:")
pw.println(" requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
+ pw.println(
+ " requestActiveUnlockOnUnlockIntentLegacy=$requestActiveUnlockOnUnlockIntentLegacy"
+ )
pw.println(" requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
pw.println(" requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 42838aeddd6b..428cd0e7da0a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -351,7 +351,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mDeviceEntryFaceAuthInteractor.onSwipeUpOnBouncer();
if (mDeviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) {
mUpdateMonitor.requestActiveUnlock(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY,
"swipeUpOnBouncer");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
index 004d5dba64c7..c2c5277ece81 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
@@ -42,4 +42,7 @@ interface AccessibilityModule {
fun accessibilityQsShortcutsRepository(
impl: AccessibilityQsShortcutsRepositoryImpl
): AccessibilityQsShortcutsRepository
+
+ @Binds
+ fun magnification(impl: MagnificationImpl): Magnification
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 3c4c0034b7b9..3c0ac9a172f9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -56,7 +56,7 @@ import com.android.systemui.util.leak.RotationUtils;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
-class FullscreenMagnificationController implements ComponentCallbacks {
+public class FullscreenMagnificationController implements ComponentCallbacks {
private static final String TAG = "FullscreenMagnificationController";
private final Context mContext;
@@ -87,7 +87,7 @@ class FullscreenMagnificationController implements ComponentCallbacks {
};
private final long mLongAnimationTimeMs;
- FullscreenMagnificationController(
+ public FullscreenMagnificationController(
@UiContext Context context,
@Main Handler handler,
@Main Executor executor,
@@ -157,7 +157,7 @@ class FullscreenMagnificationController implements ComponentCallbacks {
* there is an activation change.
*/
@UiThread
- void onFullscreenMagnificationActivationChanged(boolean activated) {
+ public void onFullscreenMagnificationActivationChanged(boolean activated) {
final boolean changed = (mFullscreenMagnificationActivated != activated);
if (changed) {
mFullscreenMagnificationActivated = activated;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index e22a4e46b513..48e5f0b1f858 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -16,618 +16,54 @@
package com.android.systemui.accessibility;
-import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
-import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-
-import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
-
import android.annotation.MainThread;
import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.window.InputTransferToken;
-
-import androidx.annotation.NonNull;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.CoreStartable;
-import com.android.systemui.Flags;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.io.PrintWriter;
-import java.util.concurrent.Executor;
-import java.util.function.Supplier;
-
-import javax.inject.Inject;
-
-/**
- * Class to handle the interaction with
- * {@link com.android.server.accessibility.AccessibilityManagerService}. It invokes
- * {@link AccessibilityManager#setMagnificationConnection(IMagnificationConnection)}
- * when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
- */
-@SysUISingleton
-public class Magnification implements CoreStartable, CommandQueue.Callbacks {
- private static final String TAG = "Magnification";
-
- @VisibleForTesting static final int DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS = 300;
- private static final int MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL = 1;
-
- private final ModeSwitchesController mModeSwitchesController;
- private final Context mContext;
- private final Handler mHandler;
- private final Executor mExecutor;
- private final AccessibilityManager mAccessibilityManager;
- private final CommandQueue mCommandQueue;
- private final OverviewProxyService mOverviewProxyService;
- private final DisplayTracker mDisplayTracker;
- private final AccessibilityLogger mA11yLogger;
-
- private MagnificationConnectionImpl mMagnificationConnectionImpl;
- private SysUiState mSysUiState;
-
- @VisibleForTesting
- SparseArray<SparseArray<Float>> mUsersScales = new SparseArray();
-
- private static class WindowMagnificationControllerSupplier extends
- DisplayIdIndexSupplier<WindowMagnificationController> {
-
- private final Context mContext;
- private final Handler mHandler;
- private final WindowMagnifierCallback mWindowMagnifierCallback;
- private final SysUiState mSysUiState;
- private final SecureSettings mSecureSettings;
-
- WindowMagnificationControllerSupplier(Context context, Handler handler,
- WindowMagnifierCallback windowMagnifierCallback,
- DisplayManager displayManager, SysUiState sysUiState,
- SecureSettings secureSettings) {
- super(displayManager);
- mContext = context;
- mHandler = handler;
- mWindowMagnifierCallback = windowMagnifierCallback;
- mSysUiState = sysUiState;
- mSecureSettings = secureSettings;
- }
-
- @Override
- protected WindowMagnificationController createInstance(Display display) {
- final Context windowContext = mContext.createWindowContext(display,
- Flags.createWindowlessWindowMagnifier()
- ? TYPE_ACCESSIBILITY_OVERLAY
- : TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
- /* options */ null);
- windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
-
- Supplier<SurfaceControlViewHost> scvhSupplier = () ->
- Flags.createWindowlessWindowMagnifier() ? new SurfaceControlViewHost(mContext,
- mContext.getDisplay(), new InputTransferToken(), TAG) : null;
-
- return new WindowMagnificationController(
- windowContext,
- mHandler,
- new WindowMagnificationAnimationController(windowContext),
- /* mirrorWindowControl= */ null,
- new SurfaceControl.Transaction(),
- mWindowMagnifierCallback,
- mSysUiState,
- mSecureSettings,
- scvhSupplier,
- new SfVsyncFrameCallbackProvider(),
- WindowManagerGlobal::getWindowSession);
- }
- }
- @VisibleForTesting
- DisplayIdIndexSupplier<WindowMagnificationController> mWindowMagnificationControllerSupplier;
-
- private static class FullscreenMagnificationControllerSupplier extends
- DisplayIdIndexSupplier<FullscreenMagnificationController> {
-
- private final Context mContext;
- private final Handler mHandler;
- private final Executor mExecutor;
- private final IWindowManager mIWindowManager;
-
- FullscreenMagnificationControllerSupplier(Context context,
- DisplayManager displayManager,
- Handler handler,
- Executor executor, IWindowManager iWindowManager) {
- super(displayManager);
- mContext = context;
- mHandler = handler;
- mExecutor = executor;
- mIWindowManager = iWindowManager;
- }
-
- @Override
- protected FullscreenMagnificationController createInstance(Display display) {
- final Context windowContext = mContext.createWindowContext(display,
- TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
- Supplier<SurfaceControlViewHost> scvhSupplier = () -> new SurfaceControlViewHost(
- mContext, mContext.getDisplay(), new InputTransferToken(), TAG);
- windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
- return new FullscreenMagnificationController(
- windowContext,
- mHandler,
- mExecutor,
- windowContext.getSystemService(AccessibilityManager.class),
- windowContext.getSystemService(WindowManager.class),
- mIWindowManager,
- scvhSupplier);
- }
- }
-
- @VisibleForTesting
- DisplayIdIndexSupplier<FullscreenMagnificationController>
- mFullscreenMagnificationControllerSupplier;
-
- private static class SettingsSupplier extends
- DisplayIdIndexSupplier<MagnificationSettingsController> {
-
- private final Context mContext;
- private final MagnificationSettingsController.Callback mSettingsControllerCallback;
- private final SecureSettings mSecureSettings;
-
- SettingsSupplier(Context context,
- MagnificationSettingsController.Callback settingsControllerCallback,
- DisplayManager displayManager,
- SecureSettings secureSettings) {
- super(displayManager);
- mContext = context;
- mSettingsControllerCallback = settingsControllerCallback;
- mSecureSettings = secureSettings;
- }
-
- @Override
- protected MagnificationSettingsController createInstance(Display display) {
- final Context windowContext = mContext.createWindowContext(display,
- TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
- windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
- return new MagnificationSettingsController(
- windowContext,
- new SfVsyncFrameCallbackProvider(),
- mSettingsControllerCallback,
- mSecureSettings);
- }
- }
-
- @VisibleForTesting
- DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier;
-
- @Inject
- public Magnification(Context context,
- @Main Handler mainHandler, @Main Executor executor,
- CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
- SysUiState sysUiState, OverviewProxyService overviewProxyService,
- SecureSettings secureSettings, DisplayTracker displayTracker,
- DisplayManager displayManager, AccessibilityLogger a11yLogger,
- IWindowManager iWindowManager) {
- this(context, mainHandler.getLooper(), executor, commandQueue,
- modeSwitchesController, sysUiState, overviewProxyService, secureSettings,
- displayTracker, displayManager, a11yLogger, iWindowManager);
- }
-
- @VisibleForTesting
- public Magnification(Context context, Looper looper, @Main Executor executor,
- CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
- SysUiState sysUiState, OverviewProxyService overviewProxyService,
- SecureSettings secureSettings, DisplayTracker displayTracker,
- DisplayManager displayManager, AccessibilityLogger a11yLogger,
- IWindowManager iWindowManager) {
- mContext = context;
- mHandler = new Handler(looper) {
- @Override
- public void handleMessage(@NonNull Message msg) {
- if (msg.what == MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL) {
- showMagnificationButtonInternal(msg.arg1, msg.arg2);
- }
- }
- };
- mExecutor = executor;
- mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
- mCommandQueue = commandQueue;
- mModeSwitchesController = modeSwitchesController;
- mSysUiState = sysUiState;
- mOverviewProxyService = overviewProxyService;
- mDisplayTracker = displayTracker;
- mA11yLogger = a11yLogger;
- mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
- mHandler, mWindowMagnifierCallback,
- displayManager, sysUiState, secureSettings);
- mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
- context, displayManager, mHandler, mExecutor, iWindowManager);
- mMagnificationSettingsSupplier = new SettingsSupplier(context,
- mMagnificationSettingsControllerCallback, displayManager, secureSettings);
-
- mModeSwitchesController.setClickListenerDelegate(
- displayId -> mHandler.post(() -> {
- toggleSettingsPanelVisibility(displayId);
- }));
- }
-
- @Override
- public void start() {
- mCommandQueue.addCallback(this);
- mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
- @Override
- public void onConnectionChanged(boolean isConnected) {
- if (isConnected) {
- updateSysUiStateFlag();
- }
- }
- });
- }
-
- private void updateSysUiStateFlag() {
- //TODO(b/187510533): support multi-display once SysuiState supports it.
- final WindowMagnificationController controller =
- mWindowMagnificationControllerSupplier.valueAt(
- mDisplayTracker.getDefaultDisplayId());
- if (controller != null) {
- controller.updateSysUIStateFlag();
- } else {
- // The instance is initialized when there is an IPC request. Considering
- // self-crash cases, we need to reset the flag in such situation.
- mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
- .commitUpdate(mDisplayTracker.getDefaultDisplayId());
- }
- }
+/** Interface for managing magnification connection calls from system process. */
+public interface Magnification extends CoreStartable {
@MainThread
- void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
- @Nullable IRemoteMagnificationAnimationCallback callback) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.enableWindowMagnification(scale, centerX, centerY,
- magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, callback);
- }
- }
+ void enableWindowMagnification(
+ int displayId,
+ float scale,
+ float centerX,
+ float centerY,
+ float magnificationFrameOffsetRatioX,
+ float magnificationFrameOffsetRatioY,
+ @Nullable IRemoteMagnificationAnimationCallback callback);
@MainThread
- void setScaleForWindowMagnification(int displayId, float scale) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.setScale(scale);
- }
- }
+ void setScaleForWindowMagnification(int displayId, float scale);
@MainThread
- void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
- final WindowMagnificationController windowMagnificationcontroller =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationcontroller != null) {
- windowMagnificationcontroller.moveWindowMagnifier(offsetX, offsetY);
- }
- }
+ void moveWindowMagnifier(int displayId, float offsetX, float offsetY);
@MainThread
- void moveWindowMagnifierToPositionInternal(int displayId, float positionX, float positionY,
- IRemoteMagnificationAnimationCallback callback) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.moveWindowMagnifierToPosition(positionX, positionY,
- callback);
- }
- }
+ void disableWindowMagnification(
+ int displayId, @Nullable IRemoteMagnificationAnimationCallback callback);
@MainThread
- void disableWindowMagnification(int displayId,
- @Nullable IRemoteMagnificationAnimationCallback callback) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.deleteWindowMagnification(callback);
- }
- }
+ void onFullscreenMagnificationActivationChanged(int displayId, boolean activated);
@MainThread
- void onFullscreenMagnificationActivationChanged(int displayId, boolean activated) {
- final FullscreenMagnificationController fullscreenMagnificationController =
- mFullscreenMagnificationControllerSupplier.get(displayId);
- if (fullscreenMagnificationController != null) {
- fullscreenMagnificationController.onFullscreenMagnificationActivationChanged(activated);
- }
- }
+ void showMagnificationButton(int displayId, int magnificationMode);
@MainThread
- void toggleSettingsPanelVisibility(int displayId) {
- final MagnificationSettingsController magnificationSettingsController =
- mMagnificationSettingsSupplier.get(displayId);
- if (magnificationSettingsController != null) {
- magnificationSettingsController.toggleSettingsPanelVisibility();
- }
- }
+ void removeMagnificationButton(int displayId);
@MainThread
- void hideMagnificationSettingsPanel(int displayId) {
- final MagnificationSettingsController magnificationSettingsController =
- mMagnificationSettingsSupplier.get(displayId);
- if (magnificationSettingsController != null) {
- magnificationSettingsController.closeMagnificationSettings();
- }
- }
-
- boolean isMagnificationSettingsPanelShowing(int displayId) {
- final MagnificationSettingsController magnificationSettingsController =
- mMagnificationSettingsSupplier.get(displayId);
- if (magnificationSettingsController != null) {
- return magnificationSettingsController.isMagnificationSettingsShowing();
- }
- return false;
- }
+ void setUserMagnificationScale(int userId, int displayId, float scale);
@MainThread
- void showMagnificationButton(int displayId, int magnificationMode) {
- if (Flags.delayShowMagnificationButton()) {
- if (mHandler.hasMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL)) {
- return;
- }
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(
- MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL, displayId, magnificationMode),
- DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS);
- } else {
- showMagnificationButtonInternal(displayId, magnificationMode);
- }
- }
+ void hideMagnificationSettingsPanel(int displayId);
@MainThread
- private void showMagnificationButtonInternal(int displayId, int magnificationMode) {
- // not to show mode switch button if settings panel is already showing to
- // prevent settings panel be covered by the button.
- if (isMagnificationSettingsPanelShowing(displayId)) {
- return;
- }
- mModeSwitchesController.showButton(displayId, magnificationMode);
- }
-
- @MainThread
- void removeMagnificationButton(int displayId) {
- if (Flags.delayShowMagnificationButton()) {
- mHandler.removeMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL);
- }
- mModeSwitchesController.removeButton(displayId);
- }
-
- @MainThread
- void setUserMagnificationScale(int userId, int displayId, float scale) {
- SparseArray<Float> scales = mUsersScales.get(userId);
- if (scales == null) {
- scales = new SparseArray<>();
- mUsersScales.put(userId, scales);
- }
- if (scales.contains(displayId) && scales.get(displayId) == scale) {
- return;
- }
- scales.put(displayId, scale);
-
- final MagnificationSettingsController magnificationSettingsController =
- mMagnificationSettingsSupplier.get(displayId);
- magnificationSettingsController.setMagnificationScale(scale);
- }
-
- @VisibleForTesting
- final WindowMagnifierCallback mWindowMagnifierCallback = new WindowMagnifierCallback() {
- @Override
- public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
- }
- }
-
- @Override
- public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
- }
- }
-
- @Override
- public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onPerformScaleAction(
- displayId, scale, updatePersistence);
- }
- }
-
- @Override
- public void onAccessibilityActionPerformed(int displayId) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
- }
- }
-
- @Override
- public void onMove(int displayId) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onMove(displayId);
- }
- }
-
- @Override
- public void onClickSettingsButton(int displayId) {
- mHandler.post(() -> {
- toggleSettingsPanelVisibility(displayId);
- });
- }
- };
-
- @VisibleForTesting
- final MagnificationSettingsController.Callback mMagnificationSettingsControllerCallback =
- new MagnificationSettingsController.Callback() {
- @Override
- public void onSetMagnifierSize(int displayId, int index) {
- mHandler.post(() -> onSetMagnifierSizeInternal(displayId, index));
- mA11yLogger.logWithPosition(
- MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_WINDOW_SIZE_SELECTED,
- index
- );
- }
-
- @Override
- public void onSetDiagonalScrolling(int displayId, boolean enable) {
- mHandler.post(() -> onSetDiagonalScrollingInternal(displayId, enable));
- }
-
- @Override
- public void onEditMagnifierSizeMode(int displayId, boolean enable) {
- mHandler.post(() -> onEditMagnifierSizeModeInternal(displayId, enable));
- mA11yLogger.log(enable
- ?
- MagnificationSettingsEvent
- .MAGNIFICATION_SETTINGS_SIZE_EDITING_ACTIVATED
- : MagnificationSettingsEvent
- .MAGNIFICATION_SETTINGS_SIZE_EDITING_DEACTIVATED);
- }
-
- @Override
- public void onMagnifierScale(int displayId, float scale,
- boolean updatePersistence) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onPerformScaleAction(
- displayId, scale, updatePersistence);
- }
- mA11yLogger.logThrottled(
- MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_ZOOM_SLIDER_CHANGED
- );
- }
-
- @Override
- public void onModeSwitch(int displayId, int newMode) {
- mHandler.post(() -> onModeSwitchInternal(displayId, newMode));
- }
-
- @Override
- public void onSettingsPanelVisibilityChanged(int displayId, boolean shown) {
- mHandler.post(() -> onSettingsPanelVisibilityChangedInternal(displayId, shown));
- }
- };
-
- @MainThread
- private void onSetMagnifierSizeInternal(int displayId, int index) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.changeMagnificationSize(index);
- }
- }
-
- @MainThread
- private void onSetDiagonalScrollingInternal(int displayId, boolean enable) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.setDiagonalScrolling(enable);
- }
- }
-
- @MainThread
- private void onEditMagnifierSizeModeInternal(int displayId, boolean enable) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null && windowMagnificationController.isActivated()) {
- windowMagnificationController.setEditMagnifierSizeMode(enable);
- }
- }
-
- @MainThread
- private void onModeSwitchInternal(int displayId, int newMode) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- final boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
- final boolean isSwitchToWindowMode = (newMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- final boolean changed = isSwitchToWindowMode ^ isWindowMagnifierActivated;
- if (changed) {
- final MagnificationSettingsController magnificationSettingsController =
- mMagnificationSettingsSupplier.get(displayId);
- if (magnificationSettingsController != null) {
- magnificationSettingsController.closeMagnificationSettings();
- }
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
- }
- }
- }
-
- @MainThread
- private void onSettingsPanelVisibilityChangedInternal(int displayId, boolean shown) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
- if (isWindowMagnifierActivated) {
- windowMagnificationController.updateDragHandleResourcesIfNeeded(shown);
- }
-
- if (shown) {
- mA11yLogger.logWithPosition(
- MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_OPENED,
- isWindowMagnifierActivated
- ? ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
- : ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
- );
- } else {
- mA11yLogger.log(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_CLOSED);
- }
- }
- }
-
- @Override
- public void requestMagnificationConnection(boolean connect) {
- if (connect) {
- setMagnificationConnection();
- } else {
- clearMagnificationConnection();
- }
- }
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- pw.println(TAG);
- mWindowMagnificationControllerSupplier.forEach(
- magnificationController -> magnificationController.dump(pw));
- }
-
- private void setMagnificationConnection() {
- if (mMagnificationConnectionImpl == null) {
- mMagnificationConnectionImpl = new MagnificationConnectionImpl(this,
- mHandler);
- }
- mAccessibilityManager.setMagnificationConnection(
- mMagnificationConnectionImpl);
- }
-
- private void clearMagnificationConnection() {
- mAccessibilityManager.setMagnificationConnection(null);
- //TODO: destroy controllers.
- }
+ void moveWindowMagnifierToPosition(
+ int displayId,
+ float positionX,
+ float positionY,
+ IRemoteMagnificationAnimationCallback callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
index b5f3aef88dc4..c8b53ee2e891 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
@@ -32,7 +32,7 @@ import com.android.systemui.dagger.qualifiers.Main;
*
* @see IMagnificationConnection
*/
-class MagnificationConnectionImpl extends IMagnificationConnection.Stub {
+public class MagnificationConnectionImpl extends IMagnificationConnection.Stub {
private static final String TAG = "WindowMagnificationConnectionImpl";
@@ -40,7 +40,7 @@ class MagnificationConnectionImpl extends IMagnificationConnection.Stub {
private final Magnification mMagnification;
private final Handler mHandler;
- MagnificationConnectionImpl(@NonNull Magnification magnification,
+ public MagnificationConnectionImpl(@NonNull Magnification magnification,
@Main Handler mainHandler) {
mMagnification = magnification;
mHandler = mainHandler;
@@ -83,7 +83,7 @@ class MagnificationConnectionImpl extends IMagnificationConnection.Stub {
@Override
public void moveWindowMagnifierToPosition(int displayId, float positionX, float positionY,
IRemoteMagnificationAnimationCallback callback) {
- mHandler.post(() -> mMagnification.moveWindowMagnifierToPositionInternal(
+ mHandler.post(() -> mMagnification.moveWindowMagnifierToPosition(
displayId, positionX, positionY, callback));
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
new file mode 100644
index 000000000000..6e5e44e45548
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+
+import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
+import android.annotation.MainThread;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IMagnificationConnection;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.window.InputTransferToken;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.systemui.Flags;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+import javax.inject.Inject;
+
+/**
+ * Class to handle the interaction with
+ * {@link com.android.server.accessibility.AccessibilityManagerService}. It invokes
+ * {@link AccessibilityManager#setMagnificationConnection(IMagnificationConnection)}
+ * when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
+ */
+@SysUISingleton
+public class MagnificationImpl implements Magnification, CommandQueue.Callbacks {
+ private static final String TAG = "Magnification";
+
+ @VisibleForTesting static final int DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS = 300;
+ private static final int MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL = 1;
+
+ private final ModeSwitchesController mModeSwitchesController;
+ private final Handler mHandler;
+ private final Executor mExecutor;
+ private final AccessibilityManager mAccessibilityManager;
+ private final CommandQueue mCommandQueue;
+ private final OverviewProxyService mOverviewProxyService;
+ private final DisplayTracker mDisplayTracker;
+ private final AccessibilityLogger mA11yLogger;
+
+ private MagnificationConnectionImpl mMagnificationConnectionImpl;
+ private SysUiState mSysUiState;
+
+ @VisibleForTesting
+ SparseArray<SparseArray<Float>> mUsersScales = new SparseArray();
+
+ private static class WindowMagnificationControllerSupplier extends
+ DisplayIdIndexSupplier<WindowMagnificationController> {
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final WindowMagnifierCallback mWindowMagnifierCallback;
+ private final SysUiState mSysUiState;
+ private final SecureSettings mSecureSettings;
+
+ WindowMagnificationControllerSupplier(Context context, Handler handler,
+ WindowMagnifierCallback windowMagnifierCallback,
+ DisplayManager displayManager, SysUiState sysUiState,
+ SecureSettings secureSettings) {
+ super(displayManager);
+ mContext = context;
+ mHandler = handler;
+ mWindowMagnifierCallback = windowMagnifierCallback;
+ mSysUiState = sysUiState;
+ mSecureSettings = secureSettings;
+ }
+
+ @Override
+ protected WindowMagnificationController createInstance(Display display) {
+ final Context windowContext = mContext.createWindowContext(display,
+ Flags.createWindowlessWindowMagnifier()
+ ? TYPE_ACCESSIBILITY_OVERLAY
+ : TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+ /* options */ null);
+ windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
+
+ Supplier<SurfaceControlViewHost> scvhSupplier = () ->
+ Flags.createWindowlessWindowMagnifier() ? new SurfaceControlViewHost(mContext,
+ mContext.getDisplay(), new InputTransferToken(), TAG) : null;
+
+ return new WindowMagnificationController(
+ windowContext,
+ mHandler,
+ new WindowMagnificationAnimationController(windowContext),
+ /* mirrorWindowControl= */ null,
+ new SurfaceControl.Transaction(),
+ mWindowMagnifierCallback,
+ mSysUiState,
+ mSecureSettings,
+ scvhSupplier,
+ new SfVsyncFrameCallbackProvider(),
+ WindowManagerGlobal::getWindowSession);
+ }
+ }
+
+ @VisibleForTesting
+ DisplayIdIndexSupplier<WindowMagnificationController> mWindowMagnificationControllerSupplier;
+
+ private static class FullscreenMagnificationControllerSupplier extends
+ DisplayIdIndexSupplier<FullscreenMagnificationController> {
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final Executor mExecutor;
+ private final IWindowManager mIWindowManager;
+
+ FullscreenMagnificationControllerSupplier(Context context,
+ DisplayManager displayManager,
+ Handler handler,
+ Executor executor, IWindowManager iWindowManager) {
+ super(displayManager);
+ mContext = context;
+ mHandler = handler;
+ mExecutor = executor;
+ mIWindowManager = iWindowManager;
+ }
+
+ @Override
+ protected FullscreenMagnificationController createInstance(Display display) {
+ final Context windowContext = mContext.createWindowContext(display,
+ TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
+ Supplier<SurfaceControlViewHost> scvhSupplier = () -> new SurfaceControlViewHost(
+ mContext, mContext.getDisplay(), new InputTransferToken(), TAG);
+ windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
+ return new FullscreenMagnificationController(
+ windowContext,
+ mHandler,
+ mExecutor,
+ windowContext.getSystemService(AccessibilityManager.class),
+ windowContext.getSystemService(WindowManager.class),
+ mIWindowManager,
+ scvhSupplier);
+ }
+ }
+
+ @VisibleForTesting
+ DisplayIdIndexSupplier<FullscreenMagnificationController>
+ mFullscreenMagnificationControllerSupplier;
+
+ private static class SettingsSupplier extends
+ DisplayIdIndexSupplier<MagnificationSettingsController> {
+
+ private final Context mContext;
+ private final MagnificationSettingsController.Callback mSettingsControllerCallback;
+ private final SecureSettings mSecureSettings;
+
+ SettingsSupplier(Context context,
+ MagnificationSettingsController.Callback settingsControllerCallback,
+ DisplayManager displayManager,
+ SecureSettings secureSettings) {
+ super(displayManager);
+ mContext = context;
+ mSettingsControllerCallback = settingsControllerCallback;
+ mSecureSettings = secureSettings;
+ }
+
+ @Override
+ protected MagnificationSettingsController createInstance(Display display) {
+ final Context windowContext = mContext.createWindowContext(display,
+ TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
+ windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
+ return new MagnificationSettingsController(
+ windowContext,
+ new SfVsyncFrameCallbackProvider(),
+ mSettingsControllerCallback,
+ mSecureSettings);
+ }
+ }
+
+ @VisibleForTesting
+ DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier;
+
+ @Inject
+ public MagnificationImpl(Context context,
+ @Main Handler mainHandler, @Main Executor executor,
+ CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
+ SysUiState sysUiState, OverviewProxyService overviewProxyService,
+ SecureSettings secureSettings, DisplayTracker displayTracker,
+ DisplayManager displayManager, AccessibilityLogger a11yLogger,
+ IWindowManager iWindowManager, AccessibilityManager accessibilityManager) {
+ this(context, mainHandler.getLooper(), executor, commandQueue,
+ modeSwitchesController, sysUiState, overviewProxyService, secureSettings,
+ displayTracker, displayManager, a11yLogger, iWindowManager, accessibilityManager);
+ }
+
+ @VisibleForTesting
+ public MagnificationImpl(Context context, Looper looper, @Main Executor executor,
+ CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
+ SysUiState sysUiState, OverviewProxyService overviewProxyService,
+ SecureSettings secureSettings, DisplayTracker displayTracker,
+ DisplayManager displayManager, AccessibilityLogger a11yLogger,
+ IWindowManager iWindowManager,
+ AccessibilityManager accessibilityManager) {
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ if (msg.what == MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL) {
+ showMagnificationButtonInternal(msg.arg1, msg.arg2);
+ }
+ }
+ };
+ mExecutor = executor;
+ mAccessibilityManager = accessibilityManager;
+ mCommandQueue = commandQueue;
+ mModeSwitchesController = modeSwitchesController;
+ mSysUiState = sysUiState;
+ mOverviewProxyService = overviewProxyService;
+ mDisplayTracker = displayTracker;
+ mA11yLogger = a11yLogger;
+ mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
+ mHandler, mWindowMagnifierCallback,
+ displayManager, sysUiState, secureSettings);
+ mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
+ context, displayManager, mHandler, mExecutor, iWindowManager);
+ mMagnificationSettingsSupplier = new SettingsSupplier(context,
+ mMagnificationSettingsControllerCallback, displayManager, secureSettings);
+
+ mModeSwitchesController.setClickListenerDelegate(
+ displayId -> mHandler.post(() -> {
+ toggleSettingsPanelVisibility(displayId);
+ }));
+ }
+
+ @Override
+ public void start() {
+ mCommandQueue.addCallback(this);
+ mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ if (isConnected) {
+ updateSysUiStateFlag();
+ }
+ }
+ });
+ }
+
+ private void updateSysUiStateFlag() {
+ //TODO(b/187510533): support multi-display once SysuiState supports it.
+ final WindowMagnificationController controller =
+ mWindowMagnificationControllerSupplier.valueAt(
+ mDisplayTracker.getDefaultDisplayId());
+ if (controller != null) {
+ controller.updateSysUIStateFlag();
+ } else {
+ // The instance is initialized when there is an IPC request. Considering
+ // self-crash cases, we need to reset the flag in such situation.
+ mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
+ .commitUpdate(mDisplayTracker.getDefaultDisplayId());
+ }
+ }
+
+ @Override
+ @MainThread
+ public void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
+ float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.enableWindowMagnification(scale, centerX, centerY,
+ magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, callback);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void setScaleForWindowMagnification(int displayId, float scale) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.setScale(scale);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+ final WindowMagnificationController windowMagnificationcontroller =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationcontroller != null) {
+ windowMagnificationcontroller.moveWindowMagnifier(offsetX, offsetY);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void moveWindowMagnifierToPosition(int displayId, float positionX, float positionY,
+ IRemoteMagnificationAnimationCallback callback) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.moveWindowMagnifierToPosition(positionX, positionY,
+ callback);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void disableWindowMagnification(int displayId,
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.deleteWindowMagnification(callback);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void onFullscreenMagnificationActivationChanged(int displayId, boolean activated) {
+ final FullscreenMagnificationController fullscreenMagnificationController =
+ mFullscreenMagnificationControllerSupplier.get(displayId);
+ if (fullscreenMagnificationController != null) {
+ fullscreenMagnificationController.onFullscreenMagnificationActivationChanged(activated);
+ }
+ }
+
+ @MainThread
+ void toggleSettingsPanelVisibility(int displayId) {
+ final MagnificationSettingsController magnificationSettingsController =
+ mMagnificationSettingsSupplier.get(displayId);
+ if (magnificationSettingsController != null) {
+ magnificationSettingsController.toggleSettingsPanelVisibility();
+ }
+ }
+
+ @Override
+ @MainThread
+ public void hideMagnificationSettingsPanel(int displayId) {
+ final MagnificationSettingsController magnificationSettingsController =
+ mMagnificationSettingsSupplier.get(displayId);
+ if (magnificationSettingsController != null) {
+ magnificationSettingsController.closeMagnificationSettings();
+ }
+ }
+
+ boolean isMagnificationSettingsPanelShowing(int displayId) {
+ final MagnificationSettingsController magnificationSettingsController =
+ mMagnificationSettingsSupplier.get(displayId);
+ if (magnificationSettingsController != null) {
+ return magnificationSettingsController.isMagnificationSettingsShowing();
+ }
+ return false;
+ }
+
+ @Override
+ @MainThread
+ public void showMagnificationButton(int displayId, int magnificationMode) {
+ if (Flags.delayShowMagnificationButton()) {
+ if (mHandler.hasMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL)) {
+ return;
+ }
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(
+ MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL, displayId, magnificationMode),
+ DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS);
+ } else {
+ showMagnificationButtonInternal(displayId, magnificationMode);
+ }
+ }
+
+ @MainThread
+ private void showMagnificationButtonInternal(int displayId, int magnificationMode) {
+ // not to show mode switch button if settings panel is already showing to
+ // prevent settings panel be covered by the button.
+ if (isMagnificationSettingsPanelShowing(displayId)) {
+ return;
+ }
+ mModeSwitchesController.showButton(displayId, magnificationMode);
+ }
+
+ @Override
+ @MainThread
+ public void removeMagnificationButton(int displayId) {
+ if (Flags.delayShowMagnificationButton()) {
+ mHandler.removeMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL);
+ }
+ mModeSwitchesController.removeButton(displayId);
+ }
+
+ @Override
+ @MainThread
+ public void setUserMagnificationScale(int userId, int displayId, float scale) {
+ SparseArray<Float> scales = mUsersScales.get(userId);
+ if (scales == null) {
+ scales = new SparseArray<>();
+ mUsersScales.put(userId, scales);
+ }
+ if (scales.contains(displayId) && scales.get(displayId) == scale) {
+ return;
+ }
+ scales.put(displayId, scale);
+
+ final MagnificationSettingsController magnificationSettingsController =
+ mMagnificationSettingsSupplier.get(displayId);
+ magnificationSettingsController.setMagnificationScale(scale);
+ }
+
+ @VisibleForTesting
+ final WindowMagnifierCallback mWindowMagnifierCallback = new WindowMagnifierCallback() {
+ @Override
+ public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
+ }
+ }
+
+ @Override
+ public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
+ }
+ }
+
+ @Override
+ public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onPerformScaleAction(
+ displayId, scale, updatePersistence);
+ }
+ }
+
+ @Override
+ public void onAccessibilityActionPerformed(int displayId) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
+ }
+ }
+
+ @Override
+ public void onMove(int displayId) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onMove(displayId);
+ }
+ }
+
+ @Override
+ public void onClickSettingsButton(int displayId) {
+ mHandler.post(() -> {
+ toggleSettingsPanelVisibility(displayId);
+ });
+ }
+ };
+
+ @VisibleForTesting
+ final MagnificationSettingsController.Callback mMagnificationSettingsControllerCallback =
+ new MagnificationSettingsController.Callback() {
+ @Override
+ public void onSetMagnifierSize(int displayId, int index) {
+ mHandler.post(() -> onSetMagnifierSizeInternal(displayId, index));
+ mA11yLogger.logWithPosition(
+ MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_WINDOW_SIZE_SELECTED,
+ index
+ );
+ }
+
+ @Override
+ public void onSetDiagonalScrolling(int displayId, boolean enable) {
+ mHandler.post(() -> onSetDiagonalScrollingInternal(displayId, enable));
+ }
+
+ @Override
+ public void onEditMagnifierSizeMode(int displayId, boolean enable) {
+ mHandler.post(() -> onEditMagnifierSizeModeInternal(displayId, enable));
+ mA11yLogger.log(enable
+ ?
+ MagnificationSettingsEvent
+ .MAGNIFICATION_SETTINGS_SIZE_EDITING_ACTIVATED
+ : MagnificationSettingsEvent
+ .MAGNIFICATION_SETTINGS_SIZE_EDITING_DEACTIVATED);
+ }
+
+ @Override
+ public void onMagnifierScale(int displayId, float scale,
+ boolean updatePersistence) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onPerformScaleAction(
+ displayId, scale, updatePersistence);
+ }
+ mA11yLogger.logThrottled(
+ MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_ZOOM_SLIDER_CHANGED
+ );
+ }
+
+ @Override
+ public void onModeSwitch(int displayId, int newMode) {
+ mHandler.post(() -> onModeSwitchInternal(displayId, newMode));
+ }
+
+ @Override
+ public void onSettingsPanelVisibilityChanged(int displayId, boolean shown) {
+ mHandler.post(() -> onSettingsPanelVisibilityChangedInternal(displayId, shown));
+ }
+ };
+
+ @MainThread
+ private void onSetMagnifierSizeInternal(int displayId, int index) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.changeMagnificationSize(index);
+ }
+ }
+
+ @MainThread
+ private void onSetDiagonalScrollingInternal(int displayId, boolean enable) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.setDiagonalScrolling(enable);
+ }
+ }
+
+ @MainThread
+ private void onEditMagnifierSizeModeInternal(int displayId, boolean enable) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null && windowMagnificationController.isActivated()) {
+ windowMagnificationController.setEditMagnifierSizeMode(enable);
+ }
+ }
+
+ @MainThread
+ private void onModeSwitchInternal(int displayId, int newMode) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ final boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
+ final boolean isSwitchToWindowMode = (newMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+ final boolean changed = isSwitchToWindowMode ^ isWindowMagnifierActivated;
+ if (changed) {
+ final MagnificationSettingsController magnificationSettingsController =
+ mMagnificationSettingsSupplier.get(displayId);
+ if (magnificationSettingsController != null) {
+ magnificationSettingsController.closeMagnificationSettings();
+ }
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
+ }
+ }
+ }
+
+ @MainThread
+ private void onSettingsPanelVisibilityChangedInternal(int displayId, boolean shown) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
+ if (isWindowMagnifierActivated) {
+ windowMagnificationController.updateDragHandleResourcesIfNeeded(shown);
+ }
+
+ if (shown) {
+ mA11yLogger.logWithPosition(
+ MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_OPENED,
+ isWindowMagnifierActivated
+ ? ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
+ : ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
+ );
+ } else {
+ mA11yLogger.log(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_CLOSED);
+ }
+ }
+ }
+
+ @Override
+ public void requestMagnificationConnection(boolean connect) {
+ if (connect) {
+ setMagnificationConnection();
+ } else {
+ clearMagnificationConnection();
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
+ pw.println(TAG);
+ mWindowMagnificationControllerSupplier.forEach(
+ magnificationController -> magnificationController.dump(pw));
+ }
+
+ private void setMagnificationConnection() {
+ if (mMagnificationConnectionImpl == null) {
+ mMagnificationConnectionImpl = new MagnificationConnectionImpl(this,
+ mHandler);
+ }
+ mAccessibilityManager.setMagnificationConnection(
+ mMagnificationConnectionImpl);
+ }
+
+ private void clearMagnificationConnection() {
+ mAccessibilityManager.setMagnificationConnection(null);
+ //TODO: destroy controllers.
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
index 02fa003a3628..f81124eeeb7f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
@@ -220,7 +220,8 @@ public class HearingDevicesPresetsController implements
if (mActiveHearingDevice == null) {
return emptyList();
}
- return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice());
+ return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice()).stream().filter(
+ BluetoothHapPresetInfo::isAvailable).toList();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index 61b4401d6b22..4035e957a2db 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -323,12 +323,15 @@ public class TouchMonitor {
// When we stop monitoring touches, we must ensure that all active touch sessions and
// descendants informed of the removal so any cleanup for active tracking can proceed.
- mMainExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
- while (touchSession != null) {
- touchSession.onRemoved();
- touchSession = touchSession.getPredecessor();
- }
- }));
+ mMainExecutor.execute(() -> {
+ mActiveTouchSessions.forEach(touchSession -> {
+ while (touchSession != null) {
+ touchSession.onRemoved();
+ touchSession = touchSession.getPredecessor();
+ }
+ });
+ mActiveTouchSessions.clear();
+ });
mCurrentInputSession.dispose();
mCurrentInputSession = null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index 900d7cc791f2..5e0e2bb9556e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -17,15 +17,11 @@
package com.android.systemui.biometrics.ui.binder
-import android.graphics.drawable.AnimatedVectorDrawable
import android.util.Log
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieOnCompositionLoadedListener
-import com.airbnb.lottie.LottieListener
import com.android.settingslib.widget.LottieColorUtils
import com.android.systemui.Flags.constraintBp
import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
@@ -66,85 +62,35 @@ object PromptIconViewBinder {
iconOverlayView.layoutParams.height = iconViewLayoutParamSizeOverride.second
}
- var faceIcon: AnimatedVectorDrawable? = null
-
- fun updateXmlIconAsset(
- iconAsset: Int,
- shouldAnimateIconView: Boolean,
- activeAuthType: AuthType
- ) {
- faceIcon?.stop()
- faceIcon = iconView.context.getDrawable(iconAsset) as AnimatedVectorDrawable
- faceIcon?.apply {
- iconView.setIconFailureListener(iconAsset, activeAuthType)
- iconView.setImageDrawable(this)
- if (shouldAnimateIconView) {
- forceAnimationOnUI()
- start()
- }
- }
- }
-
- fun updateJsonIconAsset(
- iconAsset: Int,
- shouldAnimateIconView: Boolean,
- activeAuthType: AuthType
- ) {
- iconView.setIconFailureListener(iconAsset, activeAuthType)
- iconView.setAnimation(iconAsset)
- iconView.frame = 0
-
- if (shouldAnimateIconView) {
- iconView.playAnimation()
- }
- }
-
if (!constraintBp()) {
launch {
var lottieOnCompositionLoadedListener: LottieOnCompositionLoadedListener? =
null
- combine(viewModel.activeAuthType, viewModel.iconSize, ::Pair).collect {
- (activeAuthType, iconSize) ->
- // Every time after bp shows, [isIconViewLoaded] is set to false in
- // [BiometricViewSizeBinder]. Then when biometric prompt view is redrew
- // (when size or activeAuthType changes), we need to update
- // [isIconViewLoaded] here to keep it correct.
- when (activeAuthType) {
- AuthType.Fingerprint,
- AuthType.Coex -> {
- /**
- * View is only set visible in BiometricViewSizeBinder once
- * PromptSize is determined that accounts for iconView size, to
- * prevent prompt resizing being visible to the user.
- *
- * TODO(b/288175072): May be able to remove this once constraint
- * layout is implemented
- */
- if (lottieOnCompositionLoadedListener != null) {
- iconView.removeLottieOnCompositionLoadedListener(
- lottieOnCompositionLoadedListener!!
- )
- }
- lottieOnCompositionLoadedListener =
- LottieOnCompositionLoadedListener {
- promptViewModel.setIsIconViewLoaded(true)
- }
- iconView.addLottieOnCompositionLoadedListener(
- lottieOnCompositionLoadedListener!!
- )
- }
- AuthType.Face -> {
- /**
- * Set to true by default since face icon is a drawable, which
- * doesn't have a LottieOnCompositionLoadedListener equivalent.
- *
- * TODO(b/318569643): To be updated once face assets are updated
- * from drawables
- */
- promptViewModel.setIsIconViewLoaded(true)
- }
+ viewModel.iconSize.collect { iconSize ->
+ /**
+ * When we bind the BiometricPrompt View and ViewModel in
+ * [BiometricViewBinder], the view is set invisible and
+ * [isIconViewLoaded] is set to false. We configure the iconView with a
+ * LottieOnCompositionLoadedListener that sets [isIconViewLoaded] to
+ * true, in order to wait for the iconView to load before determining
+ * the prompt size, and prevent any prompt resizing from being visible
+ * to the user.
+ *
+ * TODO(b/288175072): May be able to remove this once constraint layout
+ * is unflagged
+ */
+ if (lottieOnCompositionLoadedListener != null) {
+ iconView.removeLottieOnCompositionLoadedListener(
+ lottieOnCompositionLoadedListener!!
+ )
+ }
+ lottieOnCompositionLoadedListener = LottieOnCompositionLoadedListener {
+ promptViewModel.setIsIconViewLoaded(true)
}
+ iconView.addLottieOnCompositionLoadedListener(
+ lottieOnCompositionLoadedListener!!
+ )
if (iconViewLayoutParamSizeOverride == null) {
iconView.layoutParams.width = iconSize.first
@@ -171,51 +117,12 @@ object PromptIconViewBinder {
.collect { (iconAsset, activeAuthType, shouldAnimateIconView, showingError)
->
if (iconAsset != -1) {
- when (activeAuthType) {
- AuthType.Fingerprint,
- AuthType.Coex -> {
- // TODO(b/318569643): Until assets unified to one type, this
- // check
- // is needed in face-auth-error-triggered implicit ->
- // explicit
- // coex auth transition, in case iconAsset updates to
- // face_dialog_dark_to_error (XML) after activeAuthType
- // updates
- // from AuthType.Face (which expects XML)
- // to AuthType.Coex (which expects JSON)
- if (iconAsset == R.drawable.face_dialog_dark_to_error) {
- updateXmlIconAsset(
- iconAsset,
- shouldAnimateIconView,
- activeAuthType
- )
- } else {
- updateJsonIconAsset(
- iconAsset,
- shouldAnimateIconView,
- activeAuthType
- )
- }
- }
- AuthType.Face -> {
- // TODO(b/318569643): Consolidate logic once all face auth
- // assets are migrated from drawable to json
- if (iconAsset == R.raw.face_dialog_authenticating) {
- updateJsonIconAsset(
- iconAsset,
- shouldAnimateIconView,
- activeAuthType
- )
- } else {
- updateXmlIconAsset(
- iconAsset,
- shouldAnimateIconView,
- activeAuthType
- )
- }
- }
- }
- LottieColorUtils.applyDynamicColors(iconView.context, iconView)
+ iconView.updateAsset(
+ "iconAsset",
+ iconAsset,
+ shouldAnimateIconView,
+ activeAuthType
+ )
viewModel.setPreviousIconWasError(showingError)
}
}
@@ -233,17 +140,12 @@ object PromptIconViewBinder {
)
.collect { (iconOverlayAsset, shouldAnimateIconOverlay, showingError) ->
if (iconOverlayAsset != -1) {
- iconOverlayView.setIconOverlayFailureListener(iconOverlayAsset)
- iconOverlayView.setAnimation(iconOverlayAsset)
- iconOverlayView.frame = 0
- LottieColorUtils.applyDynamicColors(
- iconOverlayView.context,
- iconOverlayView
+ iconOverlayView.updateAsset(
+ "iconOverlayAsset",
+ iconOverlayAsset,
+ shouldAnimateIconOverlay,
+ AuthType.Fingerprint
)
-
- if (shouldAnimateIconOverlay) {
- iconOverlayView.playAnimation()
- }
viewModel.setPreviousIconOverlayWasError(showingError)
}
}
@@ -313,11 +215,11 @@ private val assetIdToString: Map<Int, String> =
R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft to
"biometricprompt_symbol_fingerprint_to_success_portrait_topleft",
// Face assets
- R.drawable.face_dialog_wink_from_dark to "face_dialog_wink_from_dark",
- R.drawable.face_dialog_dark_to_checkmark to "face_dialog_dark_to_checkmark",
- R.drawable.face_dialog_dark_to_error to "face_dialog_dark_to_error",
- R.drawable.face_dialog_error_to_idle to "face_dialog_error_to_idle",
- R.drawable.face_dialog_idle_static to "face_dialog_idle_static",
+ R.raw.face_dialog_wink_from_dark to "face_dialog_wink_from_dark",
+ R.raw.face_dialog_dark_to_checkmark to "face_dialog_dark_to_checkmark",
+ R.raw.face_dialog_dark_to_error to "face_dialog_dark_to_error",
+ R.raw.face_dialog_error_to_idle to "face_dialog_error_to_idle",
+ R.raw.face_dialog_idle_static to "face_dialog_idle_static",
R.raw.face_dialog_authenticating to "face_dialog_authenticating",
// Co-ex assets
R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie to
@@ -332,31 +234,35 @@ private fun getAssetNameFromId(id: Int): String {
return assetIdToString.getOrDefault(id, "Asset $id not found")
}
-private fun LottieAnimationView.setIconFailureListener(iconAsset: Int, activeAuthType: AuthType) {
- setFailureListener(
- LottieListener<Throwable> { result: Throwable? ->
- Log.d(
- TAG,
- "Collecting iconAsset | " +
- "activeAuthType = $activeAuthType | " +
- "Invalid resource id: $iconAsset, " +
- "name ${getAssetNameFromId(iconAsset)}",
- result
- )
+fun LottieAnimationView.updateAsset(
+ type: String,
+ asset: Int,
+ shouldAnimateIconView: Boolean,
+ activeAuthType: AuthType
+) {
+ setFailureListener(type, asset, activeAuthType)
+ setAnimation(asset)
+ frame = 0
+ if (shouldAnimateIconView) {
+ if (asset == R.raw.face_dialog_authenticating) {
+ loop(true)
+ } else {
+ loop(false)
}
- )
+ playAnimation()
+ }
+ LottieColorUtils.applyDynamicColors(context, this)
}
-private fun LottieAnimationView.setIconOverlayFailureListener(iconOverlayAsset: Int) {
- setFailureListener(
- LottieListener<Throwable> { result: Throwable? ->
- Log.d(
- TAG,
- "Collecting iconOverlayAsset | " +
- "Invalid resource id: $iconOverlayAsset, " +
- "name ${getAssetNameFromId(iconOverlayAsset)}",
- result
- )
- }
- )
+private fun LottieAnimationView.setFailureListener(type: String, asset: Int, authType: AuthType) {
+ setFailureListener { result: Throwable? ->
+ Log.d(
+ TAG,
+ "Collecting $type | " +
+ "activeAuthType = $authType | " +
+ "Invalid resource id: $asset, " +
+ "name ${getAssetNameFromId(asset)}",
+ result
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 6c6ef5ac175e..6c83dac148b2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.biometrics.ui.viewmodel
-import android.annotation.DrawableRes
import android.annotation.RawRes
import android.content.res.Configuration
import android.graphics.Rect
@@ -267,7 +266,7 @@ constructor(
}
}
- @DrawableRes
+ @RawRes
private fun getFaceIconViewAsset(
authState: PromptAuthState,
isAuthenticating: Boolean,
@@ -275,17 +274,17 @@ constructor(
showingError: Boolean
): Int =
if (authState.isAuthenticated && isPendingConfirmation) {
- R.drawable.face_dialog_wink_from_dark
+ R.raw.face_dialog_wink_from_dark
} else if (authState.isAuthenticated) {
- R.drawable.face_dialog_dark_to_checkmark
+ R.raw.face_dialog_dark_to_checkmark
} else if (isAuthenticating) {
R.raw.face_dialog_authenticating
} else if (showingError) {
- R.drawable.face_dialog_dark_to_error
+ R.raw.face_dialog_dark_to_error
} else if (_previousIconWasError.value) {
- R.drawable.face_dialog_error_to_idle
+ R.raw.face_dialog_error_to_idle
} else {
- R.drawable.face_dialog_idle_static
+ R.raw.face_dialog_idle_static
}
@RawRes
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index ac8807dd1b7e..3904ee144b05 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -50,6 +50,7 @@ import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.PromptKind
+import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.res.R
@@ -132,11 +133,11 @@ constructor(
R.dimen.biometric_prompt_landscape_medium_horizontal_padding
)
+ val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
+ udfpsOverlayInteractor.udfpsOverlayParams
+
private val udfpsSensorBounds: Flow<Rect> =
- combine(
- udfpsOverlayInteractor.udfpsOverlayParams,
- displayStateInteractor.currentRotation
- ) { params, rotation ->
+ combine(udfpsOverlayParams, displayStateInteractor.currentRotation) { params, rotation ->
val rotatedBounds = Rect(params.sensorBounds)
RotationUtils.rotateBounds(
rotatedBounds,
@@ -153,32 +154,27 @@ constructor(
}
.distinctUntilChanged()
+ private val udfpsSensorWidth: Flow<Int> = udfpsOverlayParams.map { it.sensorBounds.width() }
+ private val udfpsSensorHeight: Flow<Int> = udfpsOverlayParams.map { it.sensorBounds.height() }
+
val legacyFingerprintSensorWidth: Flow<Int> =
- combine(modalities, udfpsOverlayInteractor.udfpsOverlayParams) { modalities, overlayParams
- ->
+ combine(modalities, udfpsSensorWidth) { modalities, udfpsSensorWidth ->
if (modalities.hasUdfps) {
- overlayParams.sensorBounds.width()
+ udfpsSensorWidth
} else {
fingerprintIconWidth
}
}
val legacyFingerprintSensorHeight: Flow<Int> =
- combine(modalities, udfpsOverlayInteractor.udfpsOverlayParams) { modalities, overlayParams
- ->
+ combine(modalities, udfpsSensorHeight) { modalities, udfpsSensorHeight ->
if (modalities.hasUdfps) {
- overlayParams.sensorBounds.height()
+ udfpsSensorHeight
} else {
fingerprintIconHeight
}
}
- val fingerprintSensorWidth: Int =
- udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds.width()
-
- val fingerprintSensorHeight: Int =
- udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds.height()
-
private val _accessibilityHint = MutableSharedFlow<String>()
/** Hint for talkback directional guidance */
@@ -442,12 +438,16 @@ constructor(
/** The size of the biometric icon */
val iconSize: Flow<Pair<Int, Int>> =
- combine(iconViewModel.activeAuthType, modalities) { activeAuthType, modalities ->
+ combine(iconViewModel.activeAuthType, modalities, udfpsSensorWidth, udfpsSensorHeight) {
+ activeAuthType,
+ modalities,
+ udfpsSensorWidth,
+ udfpsSensorHeight ->
if (activeAuthType == PromptIconViewModel.AuthType.Face) {
Pair(faceIconWidth, faceIconHeight)
} else {
if (modalities.hasUdfps) {
- Pair(fingerprintSensorWidth, fingerprintSensorHeight)
+ Pair(udfpsSensorWidth, udfpsSensorHeight)
} else {
Pair(fingerprintIconWidth, fingerprintIconHeight)
}
@@ -921,13 +921,13 @@ constructor(
udfpsUtils.getTouchInNativeCoordinates(
event.getPointerId(0),
event,
- udfpsOverlayInteractor.udfpsOverlayParams.value
+ udfpsOverlayParams.value
)
if (
!udfpsUtils.isWithinSensorArea(
event.getPointerId(0),
event,
- udfpsOverlayInteractor.udfpsOverlayParams.value
+ udfpsOverlayParams.value
)
) {
_accessibilityHint.emit(
@@ -936,7 +936,7 @@ constructor(
context,
scaledTouch.x,
scaledTouch.y,
- udfpsOverlayInteractor.udfpsOverlayParams.value
+ udfpsOverlayParams.value
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index cc9073040c96..6ec6ec1113a0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -129,7 +129,6 @@ abstract class BaseCommunalViewModel(
/** Called as the UI requests opening the widget editor with an optional preselected widget. */
open fun onOpenWidgetEditor(
- preselectedKey: String? = null,
shouldOpenWidgetPickerOnStart: Boolean = false,
) {}
@@ -146,7 +145,7 @@ abstract class BaseCommunalViewModel(
open fun onReorderWidgetCancel() {}
/** Called as the user request to show the customize widget button. */
- open fun onShowCustomizeWidgetButton() {}
+ open fun onLongClick() {}
/** Set the key of the currently selected item */
fun setSelectedKey(key: String?) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 11247df5e2fd..2043dd1c557c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -40,6 +40,7 @@ import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
@@ -76,9 +77,10 @@ constructor(
@Main private val resources: Resources,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
keyguardInteractor: KeyguardInteractor,
+ private val keyguardIndicationController: KeyguardIndicationController,
communalSceneInteractor: CommunalSceneInteractor,
private val communalInteractor: CommunalInteractor,
- private val communalSettingsInteractor: CommunalSettingsInteractor,
+ communalSettingsInteractor: CommunalSettingsInteractor,
tutorialInteractor: CommunalTutorialInteractor,
private val shadeInteractor: ShadeInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
@@ -218,9 +220,8 @@ constructor(
}
override fun onOpenWidgetEditor(
- preselectedKey: String?,
shouldOpenWidgetPickerOnStart: Boolean,
- ) = communalInteractor.showWidgetEditor(preselectedKey, shouldOpenWidgetPickerOnStart)
+ ) = communalInteractor.showWidgetEditor(selectedKey.value, shouldOpenWidgetPickerOnStart)
override fun onDismissCtaTile() {
scope.launch {
@@ -229,7 +230,11 @@ constructor(
}
}
- override fun onShowCustomizeWidgetButton() {
+ fun onClick() {
+ keyguardIndicationController.showActionToUnlock()
+ }
+
+ override fun onLongClick() {
setCurrentPopupType(PopupType.CustomizeWidgetButton)
}
@@ -319,7 +324,7 @@ constructor(
}
sealed class PopupType {
- object CtaTile : PopupType()
+ data object CtaTile : PopupType()
- object CustomizeWidgetButton : PopupType()
+ data object CustomizeWidgetButton : PopupType()
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 9f0fc51abdd2..9ae63a19473a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -25,7 +25,9 @@ import android.hardware.SensorPrivacyManager;
import com.android.keyguard.KeyguardViewController;
import com.android.systemui.CoreStartable;
import com.android.systemui.ScreenDecorationsModule;
+import com.android.systemui.accessibility.AccessibilityModule;
import com.android.systemui.accessibility.SystemActionsModule;
+import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
import com.android.systemui.battery.BatterySaverModule;
import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dock.DockManager;
@@ -107,6 +109,8 @@ import javax.inject.Named;
* SystemUI code that variants of SystemUI _must_ include to function correctly.
*/
@Module(includes = {
+ AccessibilityModule.class,
+ AccessibilityRepositoryModule.class,
AospPolicyModule.class,
BatterySaverModule.class,
CollapsedStatusBarFragmentStartableModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index a7ff3c36a641..4b2fb44de310 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -32,8 +32,6 @@ import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.CameraProtectionModule;
import com.android.systemui.CoreStartable;
import com.android.systemui.SystemUISecondaryUserService;
-import com.android.systemui.accessibility.AccessibilityModule;
-import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
import com.android.systemui.ambient.dagger.AmbientModule;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
@@ -187,8 +185,6 @@ import javax.inject.Named;
* may not appreciate that.
*/
@Module(includes = {
- AccessibilityModule.class,
- AccessibilityRepositoryModule.class,
AmbientModule.class,
AppOpsModule.class,
AssistModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 10d6881e4f93..4c7142b7aeb0 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -211,6 +211,9 @@ constructor(
}
}
+ /** Whether the device is in lockdown mode, where bouncer input is required to unlock. */
+ val isInLockdown: Flow<Boolean> = deviceEntryRestrictionReason.map { it.isInLockdown() }
+
/**
* Attempt to enter the device and dismiss the lockscreen. If authentication is required to
* unlock the device it will transition to bouncer.
@@ -259,6 +262,27 @@ constructor(
return repository.isLockscreenEnabled()
}
+ fun DeviceEntryRestrictionReason?.isInLockdown(): Boolean {
+ return when (this) {
+ DeviceEntryRestrictionReason.UserLockdown -> true
+ DeviceEntryRestrictionReason.PolicyLockdown -> true
+
+ // Add individual enum value instead of using "else" so new reasons are guaranteed
+ // to be added here at compile-time.
+ null -> false
+ DeviceEntryRestrictionReason.DeviceNotUnlockedSinceReboot -> false
+ DeviceEntryRestrictionReason.BouncerLockedOut -> false
+ DeviceEntryRestrictionReason.AdaptiveAuthRequest -> false
+ DeviceEntryRestrictionReason.NonStrongBiometricsSecurityTimeout -> false
+ DeviceEntryRestrictionReason.TrustAgentDisabled -> false
+ DeviceEntryRestrictionReason.StrongBiometricsLockedOut -> false
+ DeviceEntryRestrictionReason.SecurityTimeout -> false
+ DeviceEntryRestrictionReason.DeviceNotUnlockedSinceMainlineUpdate -> false
+ DeviceEntryRestrictionReason.UnattendedUpdate -> false
+ DeviceEntryRestrictionReason.NonStrongFaceLockedOut -> false
+ }
+ }
+
/**
* Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
* dismissed once the authentication challenge is completed. For example, completing a biometric
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
index 5635f8056b9c..8a40c0326523 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
@@ -19,7 +19,7 @@ package com.android.systemui.keyboard.shortcut
import android.app.Activity
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.keyboardShortcutHelperRewrite
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.ui.ShortcutHelperActivityStarter
import com.android.systemui.keyboard.shortcut.ui.view.ShortcutHelperActivity
import dagger.Binds
@@ -52,8 +52,8 @@ interface ShortcutHelperModule {
@Provides
@IntoMap
- @ClassKey(ShortcutHelperRepository::class)
- fun repo(implLazy: Lazy<ShortcutHelperRepository>): CoreStartable {
+ @ClassKey(ShortcutHelperStateRepository::class)
+ fun repo(implLazy: Lazy<ShortcutHelperStateRepository>): CoreStartable {
return if (keyboardShortcutHelperRewrite()) {
implLazy.get()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
new file mode 100644
index 000000000000..04bde26fdd88
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.shortcut.data.source.MultitaskingShortcutsSource
+import com.android.systemui.keyboard.shortcut.data.source.SystemShortcutsSource
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import javax.inject.Inject
+
+@SysUISingleton
+class ShortcutHelperCategoriesRepository
+@Inject
+constructor(
+ private val systemShortcutsSource: SystemShortcutsSource,
+ private val multitaskingShortcutsSource: MultitaskingShortcutsSource,
+) {
+
+ fun systemShortcutsCategory(): ShortcutCategory =
+ systemShortcutsSource.systemShortcutsCategory()
+
+ fun multitaskingShortcutsCategory(): ShortcutCategory =
+ multitaskingShortcutsSource.multitaskingShortcutCategory()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
index ec92ca9c4b5b..82df95d482c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
@@ -20,23 +20,34 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.hardware.input.InputManager
import android.os.UserHandle
+import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD
import com.android.systemui.CoreStartable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Inactive
+import com.android.systemui.shared.hardware.findInputDevice
import com.android.systemui.statusbar.CommandQueue
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
@SysUISingleton
-class ShortcutHelperRepository
+class ShortcutHelperStateRepository
@Inject
constructor(
private val commandQueue: CommandQueue,
private val broadcastDispatcher: BroadcastDispatcher,
+ private val inputManager: InputManager,
+ @Background private val backgroundScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
) : CoreStartable {
val state = MutableStateFlow<ShortcutHelperState>(Inactive)
@@ -44,7 +55,9 @@ constructor(
override fun start() {
registerBroadcastReceiver(
action = Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS,
- onReceive = { state.value = Active() }
+ onReceive = {
+ backgroundScope.launch { state.value = Active(findPhysicalKeyboardId()) }
+ }
)
registerBroadcastReceiver(
action = Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS,
@@ -72,6 +85,13 @@ constructor(
)
}
+ private suspend fun findPhysicalKeyboardId() =
+ withContext(backgroundDispatcher) {
+ val firstEnabledPhysicalKeyboard =
+ inputManager.findInputDevice { it.isEnabled && it.isFullKeyboard && !it.isVirtual }
+ return@withContext firstEnabledPhysicalKeyboard?.id ?: VIRTUAL_KEYBOARD
+ }
+
fun hide() {
state.value = Inactive
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
index 34b10c727cd9..7b64f872b064 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
@@ -25,13 +25,28 @@ import android.view.KeyEvent.META_ALT_ON
import android.view.KeyEvent.META_CTRL_ON
import android.view.KeyEvent.META_META_ON
import android.view.KeyEvent.META_SHIFT_ON
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MULTI_TASKING
import com.android.systemui.keyboard.shortcut.shared.model.shortcut
+import com.android.systemui.keyboard.shortcut.shared.model.shortcutCategory
import com.android.systemui.res.R
import javax.inject.Inject
-class MultitaskingShortcutsSource @Inject constructor(private val resources: Resources) {
+class MultitaskingShortcutsSource @Inject constructor(@Main private val resources: Resources) {
- fun splitScreenShortcuts() =
+ fun multitaskingShortcutCategory() =
+ shortcutCategory(MULTI_TASKING) {
+ subCategory(
+ resources.getString(R.string.shortcutHelper_category_recent_apps),
+ recentsShortcuts()
+ )
+ subCategory(
+ resources.getString(R.string.shortcutHelper_category_split_screen),
+ splitScreenShortcuts()
+ )
+ }
+
+ private fun splitScreenShortcuts() =
listOf(
// Enter Split screen with current app to RHS:
// - Meta + Ctrl + Right arrow
@@ -60,7 +75,7 @@ class MultitaskingShortcutsSource @Inject constructor(private val resources: Res
},
)
- fun recentsShortcuts() =
+ private fun recentsShortcuts() =
listOf(
// Cycle through recent apps (forward):
// - Alt + Tab
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
index a4304e5d1d33..51c67dfdcb4d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
@@ -31,13 +31,28 @@ import android.view.KeyEvent.KEYCODE_SLASH
import android.view.KeyEvent.KEYCODE_TAB
import android.view.KeyEvent.META_CTRL_ON
import android.view.KeyEvent.META_META_ON
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.SYSTEM
import com.android.systemui.keyboard.shortcut.shared.model.shortcut
+import com.android.systemui.keyboard.shortcut.shared.model.shortcutCategory
import com.android.systemui.res.R
import javax.inject.Inject
-class SystemShortcutsSource @Inject constructor(private val resources: Resources) {
+class SystemShortcutsSource @Inject constructor(@Main private val resources: Resources) {
- fun generalShortcuts() =
+ fun systemShortcutsCategory() =
+ shortcutCategory(SYSTEM) {
+ subCategory(
+ resources.getString(R.string.shortcut_helper_category_system_controls),
+ systemControlsShortcuts()
+ )
+ subCategory(
+ resources.getString(R.string.shortcut_helper_category_system_apps),
+ systemAppsShortcuts()
+ )
+ }
+
+ private fun systemControlsShortcuts() =
listOf(
// Access list of all apps and search (i.e. Search/Launcher):
// - Meta
@@ -87,7 +102,7 @@ class SystemShortcutsSource @Inject constructor(private val resources: Resources
},
)
- fun systemAppsShortcuts() =
+ private fun systemAppsShortcuts() =
listOf(
// Pull up Notes app for quick memo:
// - Meta + Ctrl + N
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
new file mode 100644
index 000000000000..883407c5f6a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class ShortcutHelperCategoriesInteractor
+@Inject
+constructor(
+ stateRepository: ShortcutHelperStateRepository,
+ categoriesRepository: ShortcutHelperCategoriesRepository,
+) {
+
+ val shortcutCategories: Flow<List<ShortcutCategory>> =
+ stateRepository.state.map { state ->
+ when (state) {
+ is ShortcutHelperState.Active ->
+ listOf(
+ categoriesRepository.systemShortcutsCategory(),
+ categoriesRepository.multitaskingShortcutsCategory()
+ )
+ is ShortcutHelperState.Inactive -> emptyList()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
index 44f1c1e8305f..3d707f70538e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
@@ -18,7 +18,7 @@ package com.android.systemui.keyboard.shortcut.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
import com.android.systemui.model.SysUiState
import com.android.systemui.settings.DisplayTracker
@@ -29,13 +29,13 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
@SysUISingleton
-class ShortcutHelperInteractor
+class ShortcutHelperStateInteractor
@Inject
constructor(
private val displayTracker: DisplayTracker,
@Background private val backgroundScope: CoroutineScope,
private val sysUiState: SysUiState,
- private val repository: ShortcutHelperRepository
+ private val repository: ShortcutHelperStateRepository
) {
val state: Flow<ShortcutHelperState> = repository.state
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
index ea90b18c06b5..e5b870a983c6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
@@ -16,19 +16,17 @@
package com.android.systemui.keyboard.shortcut.shared.model
-import android.graphics.drawable.Icon
+data class Shortcut(val label: String, val commands: List<ShortcutCommand>)
-data class Shortcut(val label: String, val icon: Icon? = null, val commands: List<ShortcutCommand>)
-
-class ShortcutBuilder(private val label: String, private val icon: Icon? = null) {
+class ShortcutBuilder(private val label: String) {
val commands = mutableListOf<ShortcutCommand>()
fun command(vararg keyCodes: Int) {
commands += ShortcutCommand(keyCodes.toList())
}
- fun build() = Shortcut(label, icon, commands)
+ fun build() = Shortcut(label, commands)
}
-fun shortcut(label: String, icon: Icon? = null, block: ShortcutBuilder.() -> Unit): Shortcut =
+fun shortcut(label: String, block: ShortcutBuilder.() -> Unit): Shortcut =
ShortcutBuilder(label).apply(block).build()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
new file mode 100644
index 000000000000..c5e8d2c12fda
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.shared.model
+
+enum class ShortcutCategoryType {
+ SYSTEM,
+ MULTI_TASKING,
+}
+
+data class ShortcutCategory(
+ val type: ShortcutCategoryType,
+ val subCategories: List<ShortcutSubCategory>
+)
+
+class ShortcutCategoryBuilder(val type: ShortcutCategoryType) {
+ private val subCategories = mutableListOf<ShortcutSubCategory>()
+
+ fun subCategory(label: String, shortcuts: List<Shortcut>) {
+ subCategories += ShortcutSubCategory(label, shortcuts)
+ }
+
+ fun build() = ShortcutCategory(type, subCategories)
+}
+
+fun shortcutCategory(type: ShortcutCategoryType, block: ShortcutCategoryBuilder.() -> Unit) =
+ ShortcutCategoryBuilder(type).apply(block).build()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
index 747efbaf4838..a98a8ffb7004 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
@@ -16,4 +16,4 @@
package com.android.systemui.keyboard.shortcut.shared.model
-class ShortcutCommand(val keyCodes: List<Int>)
+data class ShortcutCommand(val keyCodes: List<Int>)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperState.kt
index d22d6c88ccc8..1f22141a655c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperState.kt
@@ -19,5 +19,5 @@ package com.android.systemui.keyboard.shortcut.shared.model
sealed interface ShortcutHelperState {
data object Inactive : ShortcutHelperState
- data class Active(val deviceId: Int? = null) : ShortcutHelperState
+ data class Active(val deviceId: Int) : ShortcutHelperState
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
new file mode 100644
index 000000000000..4545b4c5dd28
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.shared.model
+
+data class ShortcutSubCategory(val label: String, val shortcuts: List<Shortcut>)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index c623f5c23fd9..510e55262224 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyboard.shortcut.ui.viewmodel
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperInteractor
+import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -29,20 +29,20 @@ class ShortcutHelperViewModel
@Inject
constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
- private val interactor: ShortcutHelperInteractor
+ private val stateInteractor: ShortcutHelperStateInteractor,
) {
val shouldShow =
- interactor.state
+ stateInteractor.state
.map { it is ShortcutHelperState.Active }
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
fun onViewClosed() {
- interactor.onViewClosed()
+ stateInteractor.onViewClosed()
}
fun onViewOpened() {
- interactor.onViewOpened()
+ stateInteractor.onViewOpened()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index b3d9a7670c8a..56b520ed6206 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -59,7 +59,7 @@ constructor(
override val key: String = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
- override fun pickerName(): String = context.getString(R.string.accessibility_wallet_button)
+ override fun pickerName(): String = context.getString(R.string.wallet_title)
override val pickerIconResourceId = R.drawable.ic_wallet_lockscreen
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 cdf3b06d41b8..888d047d88ce 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
@@ -39,6 +39,7 @@ import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.time.SystemClock
@@ -293,6 +294,15 @@ interface KeyguardRepository {
/** Sets whether the keyguard is enabled (see [isKeyguardEnabled]). */
fun setKeyguardEnabled(enabled: Boolean)
+
+ /** @see isShowKeyguardWhenReenabled */
+ fun setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled: Boolean)
+
+ /**
+ * Returns `true` if the keyguard should be re-shown once it becomes re-enabled again; `false`
+ * otherwise.
+ */
+ fun isShowKeyguardWhenReenabled(): Boolean
}
/** Encapsulates application state for the keyguard. */
@@ -474,6 +484,8 @@ constructor(
private val _isDozing = MutableStateFlow(statusBarStateController.isDozing)
override val isDozing: StateFlow<Boolean> = _isDozing.asStateFlow()
+ private var isShowKeyguardWhenReenabled: Boolean = false
+
override fun setIsDozing(isDozing: Boolean) {
_isDozing.value = isDozing
}
@@ -692,6 +704,16 @@ constructor(
_isKeyguardEnabled.value = enabled
}
+ override fun setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled: Boolean) {
+ SceneContainerFlag.assertInNewMode()
+ this.isShowKeyguardWhenReenabled = isShowKeyguardWhenReenabled
+ }
+
+ override fun isShowKeyguardWhenReenabled(): Boolean {
+ SceneContainerFlag.assertInNewMode()
+ return isShowKeyguardWhenReenabled
+ }
+
private fun statusBarStateIntToObject(value: Int): StatusBarState {
return when (value) {
0 -> StatusBarState.SHADE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 2f40c99c69f9..506a18d39750 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -232,5 +232,6 @@ constructor(
val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
val TO_GONE_DURATION = DEFAULT_DURATION
val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
+ val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index f5b12a2ecfcf..07a2b0424aba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -99,19 +99,21 @@ constructor(
}
}
- scope.launch {
- keyguardRepository.isKeyguardEnabled
- .filterRelevantKeyguardStateAnd { enabled -> enabled }
- .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
- .filter { reshow -> reshow }
- .collect {
- startTransitionTo(
- KeyguardState.LOCKSCREEN,
- ownerReason =
- "Keyguard was re-enabled, and we weren't GONE when it " +
- "was originally disabled"
- )
- }
+ if (!SceneContainerFlag.isEnabled) {
+ scope.launch {
+ keyguardRepository.isKeyguardEnabled
+ .filterRelevantKeyguardStateAnd { enabled -> enabled }
+ .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
+ .filter { reshow -> reshow }
+ .collect {
+ startTransitionTo(
+ KeyguardState.LOCKSCREEN,
+ ownerReason =
+ "Keyguard was re-enabled, and we weren't GONE when it " +
+ "was originally disabled"
+ )
+ }
+ }
}
} else {
scope.launch("$TAG#listenForGoneToLockscreenOrHub") {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index f8208b37ce4a..f23b12f35977 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -233,6 +233,7 @@ constructor(
KeyguardState.DOZING -> TO_DOZING_DURATION
KeyguardState.GONE -> TO_GONE_DURATION
KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+ KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
@@ -245,6 +246,7 @@ constructor(
val TO_GONE_DURATION = 500.milliseconds
val TO_GONE_SHORT_DURATION = 200.milliseconds
val TO_LOCKSCREEN_DURATION = 450.milliseconds
+ val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.5f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 8dede01cd20b..8d683f0d4375 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -21,12 +21,15 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
/**
@@ -48,6 +51,38 @@ constructor(
transitionInteractor: KeyguardTransitionInteractor,
) {
+ /**
+ * Whether the keyguard is enabled, per [KeyguardService]. If the keyguard is not enabled, the
+ * lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE.
+ *
+ * Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold
+ * permission to do so (such as Phone).
+ *
+ * If the keyguard is disabled while we're locked, we will transition to GONE unless we're in
+ * lockdown mode. If the keyguard is re-enabled, we'll transition back to LOCKSCREEN if we were
+ * locked when it was disabled.
+ */
+ val isKeyguardEnabled: StateFlow<Boolean> = repository.isKeyguardEnabled
+
+ /**
+ * Whether we need to show the keyguard when the keyguard is re-enabled, since we hid it when it
+ * became disabled.
+ */
+ val showKeyguardWhenReenabled: Flow<Boolean> =
+ repository.isKeyguardEnabled
+ .onEach { SceneContainerFlag.assertInLegacyMode() }
+ // Whenever the keyguard is disabled...
+ .filter { enabled -> !enabled }
+ .sampleCombine(
+ transitionInteractor.currentTransitionInfoInternal,
+ biometricSettingsRepository.isCurrentUserInLockdown
+ )
+ .map { (_, transitionInfo, inLockdown) ->
+ // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case,
+ // we want to remember that and re-show it when keyguard is enabled again.
+ transitionInfo.to != KeyguardState.GONE && !inLockdown
+ }
+
init {
/**
* Whenever keyguard is disabled, transition to GONE unless we're in lockdown or already
@@ -68,25 +103,15 @@ constructor(
}
}
- /**
- * Whether we need to show the keyguard when the keyguard is re-enabled, since we hid it when it
- * became disabled.
- */
- val showKeyguardWhenReenabled: Flow<Boolean> =
- repository.isKeyguardEnabled
- // Whenever the keyguard is disabled...
- .filter { enabled -> !enabled }
- .sampleCombine(
- transitionInteractor.currentTransitionInfoInternal,
- biometricSettingsRepository.isCurrentUserInLockdown
- )
- .map { (_, transitionInfo, inLockdown) ->
- // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case,
- // we want to remember that and re-show it when keyguard is enabled again.
- transitionInfo.to != KeyguardState.GONE && !inLockdown
- }
-
fun notifyKeyguardEnabled(enabled: Boolean) {
repository.setKeyguardEnabled(enabled)
}
+
+ fun setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled: Boolean) {
+ repository.setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled)
+ }
+
+ fun isShowKeyguardWhenReenabled(): Boolean {
+ return repository.isShowKeyguardWhenReenabled()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index ecdc21cf3bb5..dc7a649c8e6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -24,6 +24,7 @@ import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToOccludedTransitionViewModel
@@ -49,6 +50,7 @@ import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransition
import com.android.systemui.keyguard.ui.viewmodel.OffToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToAodTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToDozingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
import dagger.Binds
import dagger.Module
@@ -253,4 +255,16 @@ abstract class DeviceEntryIconTransitionModule {
abstract fun goneToGlanceableHub(
impl: GoneToGlanceableHubTransitionViewModel
): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun primaryBouncerToGlanceableHub(
+ impl: PrimaryBouncerToGlanceableHubTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun dozingToGlanceableHub(
+ impl: DozingToGlanceableHubTransitionViewModel
+ ): DeviceEntryIconTransition
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index 215ac4662b55..1c6323594c70 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -73,14 +73,14 @@ constructor(
AccessibilityNodeInfoCompat.ACTION_CLICK,
resources.getString(R.string.accessibility_enter_hint)
)
+
override fun onInitializeAccessibilityNodeInfo(
v: View,
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(v, info)
when (accessibilityHintType) {
- AccessibilityHintType.BOUNCER ->
- info.addAction(accessibilityBouncerHint)
+ AccessibilityHintType.BOUNCER -> info.addAction(accessibilityBouncerHint)
AccessibilityHintType.ENTER -> info.addAction(accessibilityEnterHint)
AccessibilityHintType.NONE -> return
}
@@ -204,12 +204,12 @@ constructor(
/* reversible */ false,
)
- // LockscreenFingerprint <=> LockscreenLocked
+ // LockscreenFingerprint => LockscreenLocked
animatedIconDrawable.addTransition(
R.id.locked_fp,
R.id.locked,
context.getDrawable(R.drawable.fp_to_locked) as AnimatedVectorDrawable,
- /* reversible */ true,
+ /* reversible */ false,
)
// LockscreenUnlocked <=> AodLocked
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.kt
new file mode 100644
index 000000000000..aee34e1e713b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class DozingToGlanceableHubTransitionViewModel
+@Inject
+constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ animationFlow
+ .setup(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(DOZING, Scenes.Communal)
+ )
+ .setupWithoutSceneContainer(edge = Edge.create(DOZING, GLANCEABLE_HUB))
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
new file mode 100644
index 000000000000..754fb94a572e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class PrimaryBouncerToGlanceableHubTransitionViewModel
+@Inject
+constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ animationFlow
+ .setup(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(PRIMARY_BOUNCER, Scenes.Communal)
+ )
+ .setupWithoutSceneContainer(edge = Edge.create(PRIMARY_BOUNCER, GLANCEABLE_HUB))
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
index a2d7fb152e5c..341b8d87eeef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
@@ -27,6 +27,8 @@ import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
import com.android.systemui.media.controls.util.MediaSmartspaceLogger
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_SEEN_EVENT
import com.android.systemui.media.controls.util.SmallHash
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.time.SystemClock
@@ -136,10 +138,13 @@ constructor(
return mediaData
}
- fun addSelectedUserMediaEntry(data: MediaData) {
+ /** @return whether the added media data already exists. */
+ fun addSelectedUserMediaEntry(data: MediaData): Boolean {
val entries = LinkedHashMap<InstanceId, MediaData>(_selectedUserEntries.value)
+ val update = _selectedUserEntries.value.containsKey(data.instanceId)
entries[data.instanceId] = data
_selectedUserEntries.value = entries
+ return update
}
/**
@@ -183,7 +188,10 @@ constructor(
_reactivatedId.value = instanceId
}
- fun addMediaDataLoadingState(mediaDataLoadingModel: MediaDataLoadingModel) {
+ fun addMediaDataLoadingState(
+ mediaDataLoadingModel: MediaDataLoadingModel,
+ isUpdate: Boolean = true
+ ) {
val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
sortedMap.putAll(
sortedMedia.filter { (_, commonModel) ->
@@ -211,15 +219,10 @@ constructor(
MediaCommonModel.MediaControl(
mediaDataLoadingModel,
canBeRemoved(it),
- isMediaFromRec(it)
+ isMediaFromRec(it),
+ if (isUpdate) systemClock.currentTimeMillis() else 0,
)
sortedMap[sortKey] = newCommonModel
- val isUpdate =
- sortedMedia.values.any { commonModel ->
- commonModel is MediaCommonModel.MediaControl &&
- commonModel.mediaLoadedModel.instanceId ==
- mediaDataLoadingModel.instanceId
- }
// On Addition or tapping on recommendations, we should show the new order of media.
if (mediaFromRecPackageName == it.packageName) {
@@ -358,10 +361,118 @@ constructor(
return _selectedUserEntries.value.entries.isNotEmpty()
}
+ fun hasActiveMediaOrRecommendation(): Boolean {
+ return _selectedUserEntries.value.any { it.value.active } ||
+ (isRecommendationActive() &&
+ (_smartspaceMediaData.value.isValid() || _reactivatedId.value != null))
+ }
+
fun isRecommendationActive(): Boolean {
return _smartspaceMediaData.value.isActive
}
+ /** Log visible card given [visibleIndex]. */
+ fun logSmartspaceCardSeen(surface: Int, visibleIndex: Int, isMediaCardUpdate: Boolean) {
+ if (_currentMedia.value.size <= visibleIndex) return
+
+ when (val mediaCommonModel = _currentMedia.value[visibleIndex]) {
+ is MediaCommonModel.MediaControl -> {
+ if (
+ !isMediaCardUpdate ||
+ mediaCommonModel.mediaLoadedModel.receivedSmartspaceCardLatency != 0
+ ) {
+ logSmartspaceMediaCardUserEvent(
+ mediaCommonModel.mediaLoadedModel.instanceId,
+ visibleIndex,
+ SMARTSPACE_CARD_SEEN_EVENT,
+ surface,
+ mediaCommonModel.mediaLoadedModel.isSsReactivated,
+ )
+ }
+ }
+ is MediaCommonModel.MediaRecommendations -> {
+ if (isRecommendationActive()) {
+ logSmarspaceRecommendationCardUserEvent(
+ SMARTSPACE_CARD_SEEN_EVENT,
+ surface,
+ visibleIndex
+ )
+ }
+ }
+ }
+ }
+
+ /** Log user event on media card if smartspace logging is enabled. */
+ fun logSmartspaceCardUserEvent(
+ eventId: Int,
+ surface: Int,
+ interactedSubCardRank: Int = 0,
+ interactedSubCardCardinality: Int = 0,
+ instanceId: InstanceId? = null,
+ isRec: Boolean = false
+ ) {
+ _currentMedia.value.forEachIndexed { index, mediaCommonModel ->
+ when (mediaCommonModel) {
+ is MediaCommonModel.MediaControl -> {
+ if (mediaCommonModel.mediaLoadedModel.instanceId == instanceId) {
+ if (isSmartspaceLoggingEnabled(mediaCommonModel, index)) {
+ logSmartspaceMediaCardUserEvent(
+ instanceId,
+ index,
+ eventId,
+ surface,
+ mediaCommonModel.mediaLoadedModel.isSsReactivated,
+ interactedSubCardRank,
+ interactedSubCardCardinality
+ )
+ }
+ return
+ }
+ }
+ is MediaCommonModel.MediaRecommendations -> {
+ if (isRec) {
+ if (isSmartspaceLoggingEnabled(mediaCommonModel, index)) {
+ logSmarspaceRecommendationCardUserEvent(
+ eventId,
+ surface,
+ index,
+ interactedSubCardRank,
+ interactedSubCardCardinality
+ )
+ }
+ return
+ }
+ }
+ }
+ }
+ }
+
+ /** Log media and recommendation cards dismissal if smartspace logging is enabled for each. */
+ fun logSmartspaceCardsOnSwipeToDismiss(surface: Int) {
+ _currentMedia.value.forEachIndexed { index, mediaCommonModel ->
+ if (isSmartspaceLoggingEnabled(mediaCommonModel, index)) {
+ when (mediaCommonModel) {
+ is MediaCommonModel.MediaControl ->
+ logSmartspaceMediaCardUserEvent(
+ mediaCommonModel.mediaLoadedModel.instanceId,
+ index,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ surface,
+ mediaCommonModel.mediaLoadedModel.isSsReactivated,
+ isSwipeToDismiss = true
+ )
+ is MediaCommonModel.MediaRecommendations ->
+ logSmarspaceRecommendationCardUserEvent(
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ surface,
+ index,
+ isSwipeToDismiss = true
+ )
+ }
+ }
+ }
+ }
+
private fun canBeRemoved(data: MediaData): Boolean {
return data.isPlaying?.let { !it } ?: data.isClearable && !data.active
}
@@ -394,6 +505,54 @@ constructor(
}
}
+ private fun logSmartspaceMediaCardUserEvent(
+ instanceId: InstanceId,
+ index: Int,
+ eventId: Int,
+ surface: Int,
+ isReactivated: Boolean,
+ interactedSubCardRank: Int = 0,
+ interactedSubCardCardinality: Int = 0,
+ isSwipeToDismiss: Boolean = false
+ ) {
+ _selectedUserEntries.value[instanceId]?.let {
+ smartspaceLogger.logSmartspaceCardUIEvent(
+ eventId,
+ it.smartspaceId,
+ it.appUid,
+ surface,
+ _currentMedia.value.size,
+ isSsReactivated = isReactivated,
+ interactedSubcardRank = interactedSubCardRank,
+ interactedSubcardCardinality = interactedSubCardCardinality,
+ rank = index,
+ isSwipeToDismiss = isSwipeToDismiss,
+ )
+ }
+ }
+
+ private fun logSmarspaceRecommendationCardUserEvent(
+ eventId: Int,
+ surface: Int,
+ index: Int,
+ interactedSubCardRank: Int = 0,
+ interactedSubCardCardinality: Int = 0,
+ isSwipeToDismiss: Boolean = false
+ ) {
+ smartspaceLogger.logSmartspaceCardUIEvent(
+ eventId,
+ SmallHash.hash(_smartspaceMediaData.value.targetId),
+ _smartspaceMediaData.value.getUid(applicationContext),
+ surface,
+ _currentMedia.value.size,
+ isRecommendationCard = true,
+ interactedSubcardRank = interactedSubCardRank,
+ interactedSubcardCardinality = interactedSubCardCardinality,
+ rank = index,
+ isSwipeToDismiss = isSwipeToDismiss,
+ )
+ }
+
private fun isSmartspaceLoggingEnabled(commonModel: MediaCommonModel, index: Int): Boolean {
return sortedMedia.size > index &&
(_smartspaceMediaData.value.expiryTimeMs != 0L ||
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index 31bd4fb78ffa..803e7efa7f60 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -116,11 +116,12 @@ constructor(
return
}
- mediaFilterRepository.addSelectedUserMediaEntry(data)
+ val isUpdate = mediaFilterRepository.addSelectedUserMediaEntry(data)
mediaLoadingLogger.logMediaLoaded(data.instanceId, data.active, "loading media")
mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(data.instanceId)
+ MediaDataLoadingModel.Loaded(data.instanceId),
+ isUpdate
)
// Notify listeners
@@ -323,9 +324,10 @@ constructor(
mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
if (lockscreenUserManager.isCurrentProfile(data.userId)) {
- mediaFilterRepository.addSelectedUserMediaEntry(data)
+ val isUpdate = mediaFilterRepository.addSelectedUserMediaEntry(data)
mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(data.instanceId)
+ MediaDataLoadingModel.Loaded(data.instanceId),
+ isUpdate
)
mediaLoadingLogger.logMediaLoaded(
data.instanceId,
@@ -338,8 +340,9 @@ constructor(
}
/** Invoked when the user has dismissed the media carousel */
- fun onSwipeToDismiss() {
+ fun onSwipeToDismiss(surface: Int) {
if (DEBUG) Log.d(TAG, "Media carousel swiped away")
+ mediaFilterRepository.logSmartspaceCardsOnSwipeToDismiss(surface)
val mediaEntries = mediaFilterRepository.allUserEntries.value.entries
mediaEntries.forEach { (key, data) ->
if (mediaFilterRepository.selectedUserEntries.value.containsKey(data.instanceId)) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index 0630cbd3f3be..9d7160cbaffc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -36,6 +36,7 @@ import com.android.systemui.media.controls.domain.pipeline.MediaTimeoutListener
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
import com.android.systemui.media.controls.shared.model.MediaCommonModel
import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import java.io.PrintWriter
import javax.inject.Inject
@@ -204,11 +205,14 @@ constructor(
mediaDataProcessor.setMediaResumptionEnabled(isEnabled)
}
- override fun onSwipeToDismiss() {
- mediaDataFilter.onSwipeToDismiss()
+ override fun onSwipeToDismiss() = unsupported
+
+ fun onSwipeToDismiss(location: Int) {
+ mediaDataFilter.onSwipeToDismiss(MediaSmartspaceLogger.getSurface(location))
}
- override fun hasActiveMediaOrRecommendation() = hasActiveMediaOrRecommendation.value
+ override fun hasActiveMediaOrRecommendation() =
+ mediaFilterRepository.hasActiveMediaOrRecommendation()
override fun hasAnyMediaOrRecommendation() = hasAnyMediaOrRecommendation.value
@@ -222,6 +226,14 @@ constructor(
mediaFilterRepository.setOrderedMedia()
}
+ fun logSmartspaceSeenCard(visibleIndex: Int, location: Int, isMediaCardUpdate: Boolean) {
+ mediaFilterRepository.logSmartspaceCardSeen(
+ MediaSmartspaceLogger.getSurface(location),
+ visibleIndex,
+ isMediaCardUpdate
+ )
+ }
+
/** Add a listener for internal events. */
private fun addInternalListener(listener: MediaDataManager.Listener) =
mediaDataProcessor.addInternalListener(listener)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index 3f75938a91ea..245f6f8bf2f6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -19,7 +19,6 @@ package com.android.systemui.media.controls.domain.pipeline.interactor
import android.app.ActivityOptions
import android.app.BroadcastOptions
import android.app.PendingIntent
-import android.content.Context
import android.content.Intent
import android.media.session.MediaSession
import android.provider.Settings
@@ -31,11 +30,11 @@ import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.bluetooth.BroadcastDialogController
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
import com.android.systemui.media.controls.shared.model.MediaControlModel
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.media.dialog.MediaOutputDialogManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -50,9 +49,8 @@ import kotlinx.coroutines.flow.map
class MediaControlInteractor
@AssistedInject
constructor(
- @Application applicationContext: Context,
@Assisted private val instanceId: InstanceId,
- repository: MediaFilterRepository,
+ private val repository: MediaFilterRepository,
private val mediaDataProcessor: MediaDataProcessor,
private val keyguardStateController: KeyguardStateController,
private val activityStarter: ActivityStarter,
@@ -72,8 +70,11 @@ constructor(
fun removeMediaControl(
token: MediaSession.Token?,
instanceId: InstanceId,
- delayMs: Long
+ delayMs: Long,
+ eventId: Int,
+ location: Int
): Boolean {
+ logSmartspaceUserEvent(eventId, location)
val dismissed =
mediaDataProcessor.dismissMediaData(instanceId, delayMs, userInitiated = true)
if (!dismissed) {
@@ -114,7 +115,13 @@ constructor(
activityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */ true)
}
- fun startClickIntent(expandable: Expandable, clickIntent: PendingIntent) {
+ fun startClickIntent(
+ expandable: Expandable,
+ clickIntent: PendingIntent,
+ eventId: Int,
+ location: Int
+ ) {
+ logSmartspaceUserEvent(eventId, location)
if (!launchOverLockscreen(clickIntent)) {
activityStarter.postStartActivityDismissingKeyguard(
clickIntent,
@@ -176,6 +183,14 @@ constructor(
)
}
+ fun logSmartspaceUserEvent(eventId: Int, location: Int) {
+ repository.logSmartspaceCardUserEvent(
+ eventId,
+ MediaSmartspaceLogger.getSurface(location),
+ instanceId = instanceId
+ )
+ }
+
private fun Expandable.dialogController(): DialogTransitionAnimator.Controller? {
return dialogTransitionController(
cuj =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
index dd6b2646afd1..c3a36b258842 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
@@ -31,6 +31,7 @@ import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
import com.android.systemui.media.controls.shared.model.MediaRecModel
import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.plugins.ActivityStarter
import java.net.URISyntaxException
import javax.inject.Inject
@@ -67,7 +68,14 @@ constructor(
val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange
- fun removeMediaRecommendations(key: String, dismissIntent: Intent?, delayMs: Long) {
+ fun removeMediaRecommendations(
+ key: String,
+ dismissIntent: Intent?,
+ delayMs: Long,
+ eventId: Int,
+ location: Int
+ ) {
+ logSmartspaceCardUserEvent(eventId, location)
mediaDataProcessor.dismissSmartspaceRecommendation(key, delayMs)
if (dismissIntent == null) {
Log.w(TAG, "Cannot create dismiss action click action: extras missing dismiss_intent.")
@@ -87,7 +95,25 @@ constructor(
activityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */ true)
}
- fun startClickIntent(expandable: Expandable, intent: Intent) {
+ fun startClickIntent(
+ expandable: Expandable,
+ intent: Intent,
+ eventId: Int,
+ location: Int,
+ interactedSubCardRank: Int,
+ interactedSubCardCardinality: Int
+ ) {
+ if (interactedSubCardRank == -1) {
+ logSmartspaceCardUserEvent(eventId, MediaSmartspaceLogger.getSurface(location))
+ } else {
+ repository.logSmartspaceCardUserEvent(
+ eventId,
+ MediaSmartspaceLogger.getSurface(location),
+ interactedSubCardRank = interactedSubCardRank,
+ interactedSubCardCardinality = interactedSubCardCardinality,
+ isRec = true
+ )
+ }
if (shouldActivityOpenInForeground(intent)) {
// Request to unlock the device if the activity needs to be opened in foreground.
activityStarter.postStartActivityDismissingKeyguard(
@@ -103,6 +129,14 @@ constructor(
}
}
+ private fun logSmartspaceCardUserEvent(eventId: Int, location: Int) {
+ repository.logSmartspaceCardUserEvent(
+ eventId,
+ MediaSmartspaceLogger.getSurface(location),
+ isRec = true
+ )
+ }
+
/** Returns if the action will open the activity in foreground. */
private fun shouldActivityOpenInForeground(intent: Intent): Boolean {
val intentString = intent.extras?.getString(EXTRAS_SMARTSPACE_INTENT) ?: return false
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
index 56cc618eb61c..3d5d47b30860 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
@@ -22,6 +22,7 @@ sealed class MediaCommonModel {
val mediaLoadedModel: MediaDataLoadingModel.Loaded,
val canBeRemoved: Boolean = false,
val isMediaFromRec: Boolean = false,
+ val updateTime: Long = 0L,
) : MediaCommonModel()
data class MediaRecommendations(val recsLoadingModel: SmartspaceMediaLoadingModel) :
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 72fb218865b2..62759a4843d9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -109,6 +109,10 @@ object MediaControlViewBinder {
mainDispatcher: CoroutineDispatcher,
mediaFlags: MediaFlags,
) {
+ // Set up media control location and its listener.
+ viewModel.onLocationChanged(viewController.currentEndLocation)
+ viewController.locationChangeListener = viewModel.onLocationChanged
+
with(viewHolder) {
// AlbumView uses a hardware layer so that clipping of the foreground is handled with
// clipping the album art. Otherwise album art shows through at the edges.
@@ -221,7 +225,7 @@ object MediaControlViewBinder {
dismiss.isEnabled = model.isDismissEnabled
dismiss.setOnClickListener {
if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- model.onDismissClicked.invoke()
+ model.onDismissClicked()
}
}
cancelText.background = model.cancelTextBackground
@@ -349,7 +353,7 @@ object MediaControlViewBinder {
if (actionViewModel.isEnabled) {
button.setOnClickListener {
if (!falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)) {
- actionViewModel.onClicked.invoke(it.id)
+ actionViewModel.onClicked(it.id)
viewController.multiRippleController.play(
createTouchRippleAnimation(
@@ -469,8 +473,7 @@ object MediaControlViewBinder {
transitionDrawable.startTransition(
if (viewModel.shouldAddGradient) 333 else 80
)
- }
- ?: albumView.setImageDrawable(artwork)
+ } ?: albumView.setImageDrawable(artwork)
}
viewController.isArtworkBound = viewModel.shouldAddGradient
viewController.prevArtwork = artwork
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
index bd4d43531339..5e8a879adbb4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
@@ -71,14 +71,18 @@ object MediaRecommendationsViewBinder {
fun bindRecsCard(
viewHolder: RecommendationViewHolder,
viewModel: MediaRecsCardViewModel,
- mediaViewController: MediaViewController,
+ viewController: MediaViewController,
falsingManager: FalsingManager,
) {
+ // Set up media control location and its listener.
+ viewModel.onLocationChanged(viewController.currentEndLocation)
+ viewController.locationChangeListener = viewModel.onLocationChanged
+
// Bind main card.
viewHolder.cardTitle.setTextColor(viewModel.cardTitleColor)
viewHolder.recommendations.backgroundTintList = ColorStateList.valueOf(viewModel.cardColor)
viewHolder.recommendations.contentDescription =
- viewModel.contentDescription.invoke(mediaViewController.isGutsVisible)
+ viewModel.contentDescription.invoke(viewController.isGutsVisible)
viewHolder.recommendations.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
@@ -88,21 +92,21 @@ object MediaRecommendationsViewBinder {
viewHolder.recommendations.setOnLongClickListener {
if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY))
return@setOnLongClickListener true
- if (!mediaViewController.isGutsVisible) {
- openGuts(viewHolder, viewModel, mediaViewController)
+ if (!viewController.isGutsVisible) {
+ openGuts(viewHolder, viewModel, viewController)
} else {
- closeGuts(viewHolder, viewModel, mediaViewController)
+ closeGuts(viewHolder, viewModel, viewController)
}
return@setOnLongClickListener true
}
// Bind all recommendations.
bindRecommendationsList(viewHolder, viewModel.mediaRecs, falsingManager)
- updateRecommendationsVisibility(mediaViewController, viewHolder.recommendations)
+ updateRecommendationsVisibility(viewController, viewHolder.recommendations)
// Set visibility of recommendations.
- val expandedSet: ConstraintSet = mediaViewController.expandedLayout
- val collapsedSet: ConstraintSet = mediaViewController.collapsedLayout
+ val expandedSet: ConstraintSet = viewController.expandedLayout
+ val collapsedSet: ConstraintSet = viewController.collapsedLayout
viewHolder.mediaTitles.forEach {
setVisibleAndAlpha(expandedSet, it.id, viewModel.areTitlesVisible)
setVisibleAndAlpha(collapsedSet, it.id, viewModel.areTitlesVisible)
@@ -112,15 +116,15 @@ object MediaRecommendationsViewBinder {
setVisibleAndAlpha(collapsedSet, it.id, viewModel.areSubtitlesVisible)
}
- bindRecommendationsGuts(viewHolder, viewModel, mediaViewController, falsingManager)
+ bindRecommendationsGuts(viewHolder, viewModel, viewController, falsingManager)
- mediaViewController.refreshState()
+ viewController.refreshState()
}
private fun bindRecommendationsGuts(
viewHolder: RecommendationViewHolder,
viewModel: MediaRecsCardViewModel,
- mediaViewController: MediaViewController,
+ viewController: MediaViewController,
falsingManager: FalsingManager,
) {
val gutsViewHolder = viewHolder.gutsViewHolder
@@ -131,14 +135,14 @@ object MediaRecommendationsViewBinder {
gutsViewHolder.dismiss.isEnabled = true
gutsViewHolder.dismiss.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
- closeGuts(viewHolder, viewModel, mediaViewController)
- gutsViewModel.onDismissClicked.invoke()
+ closeGuts(viewHolder, viewModel, viewController)
+ gutsViewModel.onDismissClicked()
}
gutsViewHolder.cancelText.background = gutsViewModel.cancelTextBackground
gutsViewHolder.cancel.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- closeGuts(viewHolder, viewModel, mediaViewController)
+ closeGuts(viewHolder, viewModel, viewController)
}
}
@@ -173,7 +177,7 @@ object MediaRecommendationsViewBinder {
val mediaCoverContainer = viewHolder.mediaCoverContainers[index]
mediaCoverContainer.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
- mediaRecViewModel.onClicked.invoke(Expandable.fromView(it), index)
+ mediaRecViewModel.onClicked(Expandable.fromView(it), index)
}
mediaCoverContainer.setOnLongClickListener {
if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY))
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 53794d2d2225..125f97375fea 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -42,6 +42,7 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -106,6 +107,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@@ -123,6 +125,7 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
class MediaCarouselController
@Inject
constructor(
+ @Application applicationScope: CoroutineScope,
private val context: Context,
private val mediaControlPanelFactory: Provider<MediaControlPanel>,
private val visualStabilityProvider: VisualStabilityProvider,
@@ -387,18 +390,18 @@ constructor(
repeatOnLifecycle(Lifecycle.State.STARTED) {
listenForAnyStateToGoneKeyguardTransition(this)
listenForAnyStateToLockscreenTransition(this)
- listenForLockscreenSettingChanges(this)
if (!mediaFlags.isSceneContainerEnabled()) return@repeatOnLifecycle
listenForMediaItemsChanges(this)
}
}
+ listenForLockscreenSettingChanges(applicationScope)
// Notifies all active players about animation scale changes.
bgExecutor.execute {
globalSettings.registerContentObserverSync(
- Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
- animationScaleObserver
+ Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+ animationScaleObserver
)
}
}
@@ -696,6 +699,7 @@ constructor(
.onStart { emit(Unit) }
.map { getMediaLockScreenSetting() }
.distinctUntilChanged()
+ .flowOn(backgroundDispatcher)
.collectLatest {
allowMediaPlayerOnLockScreen = it
updateHostVisibility()
@@ -764,6 +768,7 @@ constructor(
mediaContent.addView(viewHolder.recommendations, position)
}
}
+ onAddOrUpdateVisibleToUserCard(position, isMediaCardUpdate = false)
viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded)
controllerByViewModel[commonViewModel] = viewController
updateViewControllerToState(viewController, noAnimation = true)
@@ -781,10 +786,14 @@ constructor(
commonViewModel.onAdded(commonViewModel)
}
- private fun onUpdated(commonViewModel: MediaCommonViewModel) {
+ private fun onUpdated(commonViewModel: MediaCommonViewModel, position: Int) {
commonViewModel.onUpdated(commonViewModel)
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
+ onAddOrUpdateVisibleToUserCard(
+ position,
+ commonViewModel is MediaCommonViewModel.MediaControl
+ )
}
private fun onRemoved(commonViewModel: MediaCommonViewModel) {
@@ -821,6 +830,20 @@ constructor(
mediaCarouselScrollHandler.onPlayersChanged()
}
+ private fun onAddOrUpdateVisibleToUserCard(position: Int, isMediaCardUpdate: Boolean) {
+ if (
+ mediaCarouselScrollHandler.visibleToUser &&
+ mediaCarouselScrollHandler.visibleMediaIndex == position
+ ) {
+ mediaCarouselViewModel.onCardVisibleToUser(
+ mediaCarouselScrollHandler.qsExpanded,
+ mediaCarouselScrollHandler.visibleMediaIndex,
+ currentEndLocation,
+ isMediaCardUpdate
+ )
+ }
+ }
+
private fun setNewViewModelsList(viewModels: List<MediaCommonViewModel>) {
commonViewModels.clear()
commonViewModels.addAll(viewModels)
@@ -884,8 +907,7 @@ constructor(
val previousVisibleIndex =
MediaPlayerData.playerKeys().indexOfFirst { key -> it == key }
mediaCarouselScrollHandler.scrollToPlayer(previousVisibleIndex, mediaIndex)
- }
- ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
+ } ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
}
} else if (isRtl && mediaContent.childCount > 0) {
// In RTL, Scroll to the first player as it is the rightmost player in media carousel.
@@ -1435,6 +1457,14 @@ constructor(
/** Log the user impression for media card at visibleMediaIndex. */
fun logSmartspaceImpression(qsExpanded: Boolean) {
+ if (SceneContainerFlag.isEnabled) {
+ mediaCarouselViewModel.onCardVisibleToUser(
+ qsExpanded,
+ mediaCarouselScrollHandler.visibleMediaIndex,
+ currentEndLocation
+ )
+ return
+ }
val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex
if (MediaPlayerData.players().size > visibleMediaIndex) {
val mediaControlPanel = MediaPlayerData.getMediaControlPanel(visibleMediaIndex)
@@ -1547,7 +1577,7 @@ constructor(
@VisibleForTesting
fun onSwipeToDismiss() {
if (mediaFlags.isSceneContainerEnabled()) {
- mediaCarouselViewModel.onSwipeToDismiss()
+ mediaCarouselViewModel.onSwipeToDismiss(currentEndLocation)
return
}
MediaPlayerData.players().forEachIndexed { index, it ->
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 5ec4f88721ca..846c596a238d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -19,6 +19,7 @@ package com.android.systemui.media.controls.ui.controller;
import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
import static com.android.settingslib.flags.Flags.legacyLeAudioSharing;
+import static com.android.systemui.Flags.mediaLockscreenLaunchAnimation;
import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
import android.animation.Animator;
@@ -577,13 +578,23 @@ public class MediaControlPanel {
&& mActivityIntentHelper.wouldPendingShowOverLockscreen(clickIntent,
mLockscreenUserManager.getCurrentUserId());
if (showOverLockscreen) {
- try {
- ActivityOptions opts = ActivityOptions.makeBasic();
- opts.setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- clickIntent.send(opts.toBundle());
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent for " + key + " was cancelled");
+ if (mediaLockscreenLaunchAnimation()) {
+ mActivityStarter.startPendingIntentMaybeDismissingKeyguard(
+ clickIntent,
+ /* dismissShade = */ true,
+ /* intentSentUiThreadCallback = */ null,
+ buildLaunchAnimatorController(mMediaViewHolder.getPlayer()),
+ /* fillIntent = */ null,
+ /* extraOptions = */ null);
+ } else {
+ try {
+ ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ clickIntent.send(opts.toBundle());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent for " + key + " was cancelled");
+ }
}
} else {
mActivityStarter.postStartActivityDismissingKeyguard(clickIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 9d0723211d4b..681bf390e3e9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -103,6 +103,7 @@ constructor(
lateinit var sizeChangedListener: () -> Unit
lateinit var configurationChangeListener: () -> Unit
lateinit var recsConfigurationChangeListener: (MediaViewController, TransitionLayout) -> Unit
+ var locationChangeListener: (Int) -> Unit = {}
private var firstRefresh: Boolean = true
@VisibleForTesting private var transitionLayout: TransitionLayout? = null
private val layoutController = TransitionLayoutController()
@@ -119,7 +120,15 @@ constructor(
* The ending location of the view where it ends when all animations and transitions have
* finished
*/
- @MediaLocation var currentEndLocation: Int = -1
+ @MediaLocation
+ var currentEndLocation: Int = -1
+ set(value) {
+ if (field != value) {
+ field = value
+ if (!mediaFlags.isSceneContainerEnabled()) return
+ locationChangeListener(value)
+ }
+ }
/** The starting location of the view where it starts for all animations and transitions */
@MediaLocation private var currentStartLocation: Int = -1
@@ -799,7 +808,7 @@ constructor(
fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) {
if (!mediaFlags.isSceneContainerEnabled()) return
seekBarViewModel.logSeek = onSeek
- onBindSeekBar.invoke(seekBarViewModel)
+ onBindSeekBar(seekBarViewModel)
}
fun setUpTurbulenceNoise() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
index 952b134c5e16..f28edd638b10 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
@@ -54,7 +54,8 @@ class MediaViewModelCallback(
oldItem is MediaCommonViewModel.MediaControl &&
newItem is MediaCommonViewModel.MediaControl
) {
- oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi
+ oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi &&
+ oldItem.updateTime == newItem.updateTime
} else if (
oldItem is MediaCommonViewModel.MediaRecommendations &&
newItem is MediaCommonViewModel.MediaRecommendations
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
index bd81e44d091c..709723fa9480 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
@@ -24,7 +24,7 @@ class MediaViewModelListUpdateCallback(
private val old: List<MediaCommonViewModel>,
private val new: List<MediaCommonViewModel>,
private val onAdded: (MediaCommonViewModel, Int) -> Unit,
- private val onUpdated: (MediaCommonViewModel) -> Unit,
+ private val onUpdated: (MediaCommonViewModel, Int) -> Unit,
private val onRemoved: (MediaCommonViewModel) -> Unit,
private val onMoved: (MediaCommonViewModel, Int, Int) -> Unit,
) : ListUpdateCallback {
@@ -47,7 +47,7 @@ class MediaViewModelListUpdateCallback(
override fun onChanged(position: Int, count: Int, payload: Any?) {
for (i in position until position + count) {
- onUpdated(new[i])
+ onUpdated(new[i], position)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index f0d8df511c73..c453a212a3cd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -98,9 +98,9 @@ constructor(
private var allowReorder = false
- fun onSwipeToDismiss() {
+ fun onSwipeToDismiss(location: Int) {
logger.logSwipeDismiss()
- interactor.onSwipeToDismiss()
+ interactor.onSwipeToDismiss(location)
}
fun onReorderingAllowed() {
@@ -108,12 +108,24 @@ constructor(
interactor.reorderMedia()
}
+ fun onCardVisibleToUser(
+ qsExpanded: Boolean,
+ visibleIndex: Int,
+ location: Int,
+ isUpdate: Boolean = false
+ ) {
+ // Skip logging if on LS or QQS, and there is no active media card
+ if (!qsExpanded && !interactor.hasActiveMediaOrRecommendation()) return
+ interactor.logSmartspaceSeenCard(visibleIndex, location, isUpdate)
+ }
+
private fun toViewModel(
commonModel: MediaCommonModel.MediaControl
): MediaCommonViewModel.MediaControl {
val instanceId = commonModel.mediaLoadedModel.instanceId
return mediaControlByInstanceId[instanceId]?.copy(
- immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi
+ immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi,
+ updateTime = commonModel.updateTime
)
?: MediaCommonViewModel.MediaControl(
instanceId = instanceId,
@@ -125,7 +137,8 @@ constructor(
mediaControlByInstanceId.remove(instanceId)
},
onUpdated = { onMediaControlAddedOrUpdated(it, commonModel) },
- isMediaFromRec = commonModel.isMediaFromRec
+ isMediaFromRec = commonModel.isMediaFromRec,
+ updateTime = commonModel.updateTime
)
.also { mediaControlByInstanceId[instanceId] = it }
}
@@ -175,7 +188,6 @@ constructor(
commonViewModel: MediaCommonViewModel,
commonModel: MediaCommonModel.MediaControl
) {
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_RECEIVED)
if (commonModel.canBeRemoved && !Utils.useMediaResumption(applicationContext)) {
// This media control is due for removal as it is now paused + timed out, and resumption
// setting is off.
@@ -196,8 +208,6 @@ constructor(
if (!mediaFlags.isPersistentSsCardEnabled()) {
commonViewModel.onRemoved(true)
}
- } else {
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_RECEIVED)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
index a96d75c9ed30..52cb173b39cb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
@@ -33,6 +33,7 @@ sealed class MediaCommonViewModel {
override val onRemoved: (Boolean) -> Unit,
override val onUpdated: (MediaCommonViewModel) -> Unit,
val isMediaFromRec: Boolean = false,
+ val updateTime: Long = 0,
) : MediaCommonViewModel()
data class MediaRecommendations(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 099991d7c671..64820e0d0ced 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -37,6 +37,8 @@ import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
@@ -72,6 +74,7 @@ class MediaControlViewModel(
private var isPlaying = false
private var isAnyButtonClicked = false
+ private var location = -1
private fun onDismissMediaData(
token: Token?,
@@ -80,7 +83,13 @@ class MediaControlViewModel(
instanceId: InstanceId
) {
logger.logLongPressDismiss(uid, packageName, instanceId)
- interactor.removeMediaControl(token, instanceId, MEDIA_PLAYER_ANIMATION_DELAY)
+ interactor.removeMediaControl(
+ token,
+ instanceId,
+ MEDIA_PLAYER_ANIMATION_DELAY,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ location
+ )
}
private suspend fun toViewModel(model: MediaControlModel): MediaPlayerViewModel? {
@@ -100,7 +109,7 @@ class MediaControlViewModel(
TAG,
Style.CONTENT
)
- ?: return null
+ ?: return null
val gutsViewModel = toGutsViewModel(model, scheme)
@@ -144,8 +153,12 @@ class MediaControlViewModel(
onClicked = { expandable ->
model.clickIntent?.let { clickIntent ->
logger.logTapContentView(model.uid, model.packageName, model.instanceId)
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
- interactor.startClickIntent(expandable, clickIntent)
+ interactor.startClickIntent(
+ expandable,
+ clickIntent,
+ SMARTSPACE_CARD_CLICK_EVENT,
+ location
+ )
}
},
onLongClicked = {
@@ -153,7 +166,7 @@ class MediaControlViewModel(
},
onSeek = {
logger.logSeek(model.uid, model.packageName, model.instanceId)
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
+ interactor.logSmartspaceUserEvent(SMARTSPACE_CARD_CLICK_EVENT, location)
},
onBindSeekbar = { seekBarViewModel ->
if (model.isResume && model.resumeProgress != null) {
@@ -163,7 +176,8 @@ class MediaControlViewModel(
seekBarViewModel.updateController(mediaController)
}
}
- }
+ },
+ onLocationChanged = { location = it }
)
}
@@ -179,8 +193,7 @@ class MediaControlViewModel(
it,
applicationContext.getString(R.string.broadcasting_description_is_broadcasting)
)
- }
- ?: false
+ } ?: false
val useDisabledAlpha =
if (showBroadcastButton) {
!isCurrentBroadcastApp
@@ -197,7 +210,8 @@ class MediaControlViewModel(
return MediaOutputSwitcherViewModel(
isTapEnabled = showBroadcastButton || !useDisabledAlpha,
deviceString = deviceString,
- deviceIcon = device?.icon?.let { Icon.Loaded(it, null) }
+ deviceIcon =
+ device?.icon?.let { Icon.Loaded(it, null) }
?: if (showBroadcastButton) {
Icon.Resource(R.drawable.settings_input_antenna, null)
} else {
@@ -364,7 +378,7 @@ class MediaControlViewModel(
action: Runnable
) {
logger.logTapAction(id, uid, packageName, instanceId)
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
+ interactor.logSmartspaceUserEvent(SMARTSPACE_CARD_CLICK_EVENT, location)
isAnyButtonClicked = true
action.run()
}
@@ -385,8 +399,7 @@ class MediaControlViewModel(
SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.stream().allMatch { id: Int ->
semanticActions.getActionById(id) != null
}
- }
- ?: false
+ } ?: false
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
index 433434129b96..96e7fc79c8eb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
@@ -42,4 +42,5 @@ data class MediaPlayerViewModel(
val onLongClicked: () -> Unit,
val onSeek: () -> Unit,
val onBindSeekbar: (SeekBarViewModel) -> Unit,
+ val onLocationChanged: (Int) -> Unit,
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
index 52c4bc54eab4..1fd9c4f014ee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
@@ -37,6 +37,7 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor
import com.android.systemui.media.controls.shared.model.MediaRecModel
import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
+import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
@@ -44,6 +45,8 @@ import com.android.systemui.media.controls.ui.animation.textSecondaryFromScheme
import com.android.systemui.media.controls.ui.controller.MediaViewController.Companion.GUTS_ANIMATION_DURATION
import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
import com.android.systemui.media.controls.util.MediaDataUtils
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
@@ -78,6 +81,8 @@ constructor(
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
+ private var location = -1
+
/**
* Called whenever the recommendation has been expired or removed by the user. This method
* removes the recommendation card entirely from the carousel.
@@ -89,9 +94,14 @@ constructor(
dismissIntent: Intent?,
instanceId: InstanceId?
) {
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_DISMISS_EVENT).
logger.logLongPressDismiss(uid, packageName, instanceId)
- interactor.removeMediaRecommendations(key, dismissIntent, GUTS_DISMISS_DELAY_MS_DURATION)
+ interactor.removeMediaRecommendations(
+ key,
+ dismissIntent,
+ GUTS_DISMISS_DELAY_MS_DURATION,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ location
+ )
}
private fun onClicked(
@@ -111,12 +121,18 @@ constructor(
} else {
logger.logRecommendationItemTap(packageName, instanceId, index)
}
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT).
// set the package name of the player added by recommendation once the media is loaded.
interactor.switchToMediaControl(packageName)
- interactor.startClickIntent(expandable, intent)
+ interactor.startClickIntent(
+ expandable,
+ intent,
+ SMARTSPACE_CARD_CLICK_EVENT,
+ location,
+ index,
+ NUM_REQUIRED_RECOMMENDATIONS
+ )
}
private suspend fun toRecsViewModel(model: MediaRecommendationsModel): MediaRecsCardViewModel? {
@@ -212,6 +228,7 @@ constructor(
areTitlesVisible = areTitlesVisible,
areSubtitlesVisible = areSubtitlesVisible,
gutsMenu = toGutsViewModel(model, scheme),
+ onLocationChanged = { location = it }
)
}
@@ -259,8 +276,7 @@ constructor(
width,
height
)
- }
- ?: ColorDrawable(Color.TRANSPARENT)
+ } ?: ColorDrawable(Color.TRANSPARENT)
}
private fun addGradientToRecommendationAlbum(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
index d1713b5cd2fd..5ecbcb20a58a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
@@ -30,4 +30,5 @@ data class MediaRecsCardViewModel(
val areTitlesVisible: Boolean,
val areSubtitlesVisible: Boolean,
val gutsMenu: GutsViewModel,
+ val onLocationChanged: (Int) -> Unit,
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt
index 01fbf4af7626..9c59aa2729b1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt
@@ -18,6 +18,8 @@ package com.android.systemui.media.controls.util
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shared.system.SysUiStatsLog
import javax.inject.Inject
@@ -69,7 +71,7 @@ class MediaSmartspaceLogger @Inject constructor() {
* @param eventId id of the event. eg: dismiss, click, or seen.
* @param instanceId id to uniquely identify a card.
* @param uid uid for the application that media comes from.
- * @param location location of media carousel holding media card.
+ * @param surface location of media carousel holding media card.
* @param cardinality number of card in carousel.
* @param isRecommendationCard whether media card being logged is a recommendations card.
* @param isSsReactivated indicates resume media card is reactivated by Smartspace
@@ -81,10 +83,12 @@ class MediaSmartspaceLogger @Inject constructor() {
eventId: Int,
instanceId: Int,
uid: Int,
- location: Int,
+ surface: Int,
cardinality: Int,
isRecommendationCard: Boolean = false,
isSsReactivated: Boolean = false,
+ interactedSubcardRank: Int = 0,
+ interactedSubcardCardinality: Int = 0,
rank: Int = 0,
isSwipeToDismiss: Boolean = false,
) {
@@ -92,10 +96,12 @@ class MediaSmartspaceLogger @Inject constructor() {
eventId,
instanceId,
uid,
- surfaces = intArrayOf(location),
+ surfaces = intArrayOf(surface),
cardinality,
isRecommendationCard,
isSsReactivated,
+ interactedSubcardRank,
+ interactedSubcardCardinality,
rank = rank,
isSwipeToDismiss = isSwipeToDismiss,
)
@@ -187,5 +193,27 @@ class MediaSmartspaceLogger @Inject constructor() {
const val SMARTSPACE_CARD_CLICK_EVENT = 760
const val SMARTSPACE_CARD_DISMISS_EVENT = 761
const val SMARTSPACE_CARD_SEEN_EVENT = 800
+
+ /**
+ * Get the location of media view given [currentEndLocation]
+ *
+ * @return location used for Smartspace logging
+ */
+ fun getSurface(location: Int): Int {
+ SceneContainerFlag.isUnexpectedlyInLegacyMode()
+ return when (location) {
+ MediaHierarchyManager.LOCATION_QQS,
+ MediaHierarchyManager.LOCATION_QS -> {
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE
+ }
+ MediaHierarchyManager.LOCATION_LOCKSCREEN -> {
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN
+ }
+ MediaHierarchyManager.LOCATION_DREAM_OVERLAY -> {
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY
+ }
+ else -> SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DEFAULT_SURFACE
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
index c8e896dacfb8..5084944685b6 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
@@ -84,9 +84,13 @@ constructor(
SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it.scene != Scenes.Gone },
SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to
{
- it.scene == Scenes.Lockscreen ||
- it.scene == Scenes.NotificationsShade ||
- it.scene == Scenes.Shade
+ when {
+ it.invisibleDueToOcclusion -> false
+ it.scene == Scenes.Lockscreen -> true
+ it.scene == Scenes.NotificationsShade -> true
+ it.scene == Scenes.Shade -> true
+ else -> false
+ }
},
SYSUI_STATE_QUICK_SETTINGS_EXPANDED to
{
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
index d0c7fbcac189..1f7471668355 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
@@ -22,6 +22,8 @@ import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeAlignment
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -29,11 +31,19 @@ import kotlinx.coroutines.flow.asStateFlow
/** Models UI state and handles user input for the Notifications Shade scene. */
@SysUISingleton
-class NotificationsShadeSceneViewModel @Inject constructor() {
+class NotificationsShadeSceneViewModel
+@Inject
+constructor(
+ shadeInteractor: ShadeInteractor,
+) {
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
MutableStateFlow(
mapOf(
- Swipe.Up to SceneFamilies.Home,
+ if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
+ Swipe.Up
+ } else {
+ Swipe.Down
+ } to SceneFamilies.Home,
Back to SceneFamilies.Home,
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index e77bd03b8af2..4018320f2195 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -103,8 +103,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
}
private int mLastMaxHeight = -1;
- @Override
- public void setPageMargin(int marginPixels) {
+ public void setPageMargin(int marginPixelsStart, int marginPixelsEnd) {
// Using page margins creates some rounding issues that interfere with the correct position
// in the onPageChangedListener and therefore present bad positions to the PageIndicator.
// Instead, we use negative margins in the container and positive padding in the pages,
@@ -113,14 +112,19 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
// QSContainerImpl resources are set onAttachedView, so this view will always have the right
// values when attached.
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
- lp.setMarginStart(-marginPixels);
- lp.setMarginEnd(-marginPixels);
+ lp.setMarginStart(-marginPixelsStart);
+ lp.setMarginEnd(-marginPixelsEnd);
setLayoutParams(lp);
int nPages = mPages.size();
for (int i = 0; i < nPages; i++) {
View v = mPages.get(i);
- v.setPadding(marginPixels, v.getPaddingTop(), marginPixels, v.getPaddingBottom());
+ v.setPadding(
+ marginPixelsStart,
+ v.getPaddingTop(),
+ marginPixelsEnd,
+ v.getPaddingBottom()
+ );
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index cc0901fca822..db0676e26639 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -35,6 +35,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.Dumpable;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.LargeScreenHeaderHelper;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.util.LargeScreenUtils;
@@ -300,7 +301,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
// QS panel lays out some of its content full width
qsPanelController.setContentMargins(mContentHorizontalPadding,
mContentHorizontalPadding);
- qsPanelController.setPageMargin(mTilesPageMargin);
+ setPageMargins(qsPanelController);
} else if (view == mHeader) {
quickStatusBarHeaderController.setContentMargins(mContentHorizontalPadding,
mContentHorizontalPadding);
@@ -318,6 +319,18 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
}
}
+ private void setPageMargins(QSPanelController qsPanelController) {
+ if (SceneContainerFlag.isEnabled()) {
+ if (mHorizontalMargins == mTilesPageMargin * 2 + 1) {
+ qsPanelController.setPageMargin(mTilesPageMargin, mTilesPageMargin + 1);
+ } else {
+ qsPanelController.setPageMargin(mTilesPageMargin, mTilesPageMargin);
+ }
+ } else {
+ qsPanelController.setPageMargin(mTilesPageMargin, mTilesPageMargin);
+ }
+ }
+
/**
* Clip QS bottom using a concave shape.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index abc2b7fcf3ac..032891fa715e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -638,9 +638,9 @@ public class QSPanel extends LinearLayout implements Tunable {
return mListening;
}
- protected void setPageMargin(int pageMargin) {
+ protected void setPageMargin(int pageMarginStart, int pageMarginEnd) {
if (mTileLayout instanceof PagedTileLayout) {
- ((PagedTileLayout) mTileLayout).setPageMargin(pageMargin);
+ ((PagedTileLayout) mTileLayout).setPageMargin(pageMarginStart, pageMarginEnd);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index f76183ed2d74..d5cd8dc55045 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -284,8 +284,8 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
return mView.isExpanded();
}
- void setPageMargin(int pageMargin) {
- mView.setPageMargin(pageMargin);
+ void setPageMargin(int pageMarginStart, int pageMarginEnd) {
+ mView.setPageMargin(pageMarginStart, pageMarginEnd);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
index bd748d5c2174..e395d30c0c19 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
@@ -26,6 +26,8 @@ import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@@ -37,6 +39,7 @@ import kotlinx.coroutines.flow.asStateFlow
class QuickSettingsShadeSceneViewModel
@Inject
constructor(
+ shadeInteractor: ShadeInteractor,
val overlayShadeViewModel: OverlayShadeViewModel,
val brightnessSliderViewModel: BrightnessSliderViewModel,
val tileGridViewModel: TileGridViewModel,
@@ -46,7 +49,11 @@ constructor(
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
MutableStateFlow(
mapOf(
- Swipe.Up to SceneFamilies.Home,
+ if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
+ Swipe.Up
+ } else {
+ Swipe.Down
+ } to SceneFamilies.Home,
Back to SceneFamilies.Home,
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
index 6bcd92316106..233e9b5bf818 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
@@ -121,7 +121,7 @@ constructor(
private val SceneKey.canBeOccluded: Boolean
get() =
when (this) {
- Scenes.Bouncer -> true
+ Scenes.Bouncer -> false
Scenes.Communal -> true
Scenes.Gone -> true
Scenes.Lockscreen -> true
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 4738dbd2b21d..25a9e9e6f9f2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -22,6 +22,7 @@ import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.resolver.SceneResolver
import com.android.systemui.scene.shared.logger.SceneLogger
@@ -60,6 +61,7 @@ constructor(
private val logger: SceneLogger,
private val sceneFamilyResolvers: Lazy<Map<SceneKey, @JvmSuppressWildcards SceneResolver>>,
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
+ private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
) {
interface OnSceneAboutToChangeListener {
@@ -381,7 +383,8 @@ constructor(
val isChangeAllowed =
to != Scenes.Gone ||
inMidTransitionFromGone ||
- deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked
+ deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked ||
+ !keyguardEnabledInteractor.isKeyguardEnabled.value
check(isChangeAllowed) {
"Cannot change to the Gone scene while the device is locked and not currently" +
" transitioning from Gone. Current transition state is ${transitionState.value}." +
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
index 9e91b6604dfc..41a3c8aff6cf 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
@@ -22,6 +22,7 @@ import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import dagger.Binds
@@ -45,11 +46,13 @@ class HomeSceneFamilyResolver
constructor(
@Application private val applicationScope: CoroutineScope,
deviceEntryInteractor: DeviceEntryInteractor,
+ keyguardEnabledInteractor: KeyguardEnabledInteractor,
) : SceneResolver {
override val targetFamily: SceneKey = SceneFamilies.Home
override val resolvedScene: StateFlow<SceneKey> =
combine(
+ keyguardEnabledInteractor.isKeyguardEnabled,
deviceEntryInteractor.canSwipeToEnter,
deviceEntryInteractor.isDeviceEntered,
deviceEntryInteractor.isUnlocked,
@@ -60,20 +63,23 @@ constructor(
started = SharingStarted.Eagerly,
initialValue =
homeScene(
- deviceEntryInteractor.canSwipeToEnter.value,
- deviceEntryInteractor.isDeviceEntered.value,
- deviceEntryInteractor.isUnlocked.value,
+ isKeyguardEnabled = keyguardEnabledInteractor.isKeyguardEnabled.value,
+ canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value,
+ isDeviceEntered = deviceEntryInteractor.isDeviceEntered.value,
+ isUnlocked = deviceEntryInteractor.isUnlocked.value,
)
)
override fun includesScene(scene: SceneKey): Boolean = scene in homeScenes
private fun homeScene(
+ isKeyguardEnabled: Boolean,
canSwipeToEnter: Boolean?,
isDeviceEntered: Boolean,
isUnlocked: Boolean,
): SceneKey =
when {
+ !isKeyguardEnabled -> Scenes.Gone
canSwipeToEnter == true -> Scenes.Lockscreen
!isDeviceEntered -> Scenes.Lockscreen
!isUnlocked -> Scenes.Lockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 218853d7a1fb..84f7c37ac2e2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -22,7 +22,6 @@ import android.app.StatusBarManager
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.internal.logging.UiEventLogger
-import com.android.internal.policy.IKeyguardStateCallback
import com.android.systemui.CoreStartable
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
@@ -39,6 +38,8 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInt
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor
import com.android.systemui.model.SceneContainerPlugin
@@ -83,6 +84,7 @@ import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -124,12 +126,12 @@ constructor(
private val sceneBackInteractor: SceneBackInteractor,
private val shadeSessionStorage: SessionStorage,
private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor,
+ private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+ private val dismissCallbackRegistry: DismissCallbackRegistry,
) : CoreStartable {
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
- private val keyguardStateCallbacks = mutableListOf<IKeyguardStateCallback>()
-
override fun start() {
if (SceneContainerFlag.isEnabled) {
sceneLogger.logFrameworkEnabled(isEnabled = true)
@@ -143,6 +145,8 @@ constructor(
hydrateWindowController()
hydrateBackStack()
resetShadeSessions()
+ handleKeyguardEnabledness()
+ notifyKeyguardDismissCallbacks()
} else {
sceneLogger.logFrameworkEnabled(
isEnabled = false,
@@ -653,6 +657,51 @@ constructor(
}
}
+ private fun handleKeyguardEnabledness() {
+ // Automatically switches scenes when keyguard is enabled or disabled, as needed.
+ applicationScope.launch {
+ keyguardEnabledInteractor.isKeyguardEnabled
+ .sample(
+ combine(
+ deviceEntryInteractor.isInLockdown,
+ deviceEntryInteractor.isDeviceEntered,
+ ::Pair,
+ )
+ ) { isKeyguardEnabled, (isInLockdown, isDeviceEntered) ->
+ when {
+ !isKeyguardEnabled && !isInLockdown && !isDeviceEntered -> {
+ keyguardEnabledInteractor.setShowKeyguardWhenReenabled(true)
+ Scenes.Gone to "Keyguard became disabled"
+ }
+ isKeyguardEnabled &&
+ keyguardEnabledInteractor.isShowKeyguardWhenReenabled() -> {
+ keyguardEnabledInteractor.setShowKeyguardWhenReenabled(false)
+ Scenes.Lockscreen to "Keyguard became enabled"
+ }
+ else -> null
+ }
+ }
+ .filterNotNull()
+ .collect { (targetScene, loggingReason) ->
+ switchToScene(targetScene, loggingReason)
+ }
+ }
+
+ // Clears the showKeyguardWhenReenabled if the auth method changes to an insecure one.
+ applicationScope.launch {
+ authenticationInteractor
+ .get()
+ .authenticationMethod
+ .map { it.isSecure }
+ .distinctUntilChanged()
+ .collect { isAuthenticationMethodSecure ->
+ if (!isAuthenticationMethodSecure) {
+ keyguardEnabledInteractor.setShowKeyguardWhenReenabled(false)
+ }
+ }
+ }
+ }
+
private fun switchToScene(targetSceneKey: SceneKey, loggingReason: String) {
sceneInteractor.changeScene(
toScene = targetSceneKey,
@@ -667,4 +716,16 @@ constructor(
}
}
}
+
+ private fun notifyKeyguardDismissCallbacks() {
+ applicationScope.launch {
+ sceneInteractor.currentScene.pairwise().collect { (from, to) ->
+ when {
+ from != Scenes.Bouncer -> Unit
+ to == Scenes.Gone -> dismissCallbackRegistry.notifyDismissSucceeded()
+ else -> dismissCallbackRegistry.notifyDismissCancelled()
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index d382b7ae2bf0..a62edcb50058 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -25,6 +25,7 @@ import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.app.Activity;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.Gravity;
@@ -32,7 +33,9 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
@@ -152,18 +155,27 @@ public class BrightnessDialog extends Activity {
Configuration configuration = getResources().getConfiguration();
int orientation = configuration.orientation;
- int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
+ int windowWidth = getWindowAvailableWidth();
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
boolean shouldBeFullWidth = getIntent()
.getBooleanExtra(EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH, false);
- lp.width = (shouldBeFullWidth ? screenWidth : screenWidth / 2) - horizontalMargin * 2;
+ lp.width = (shouldBeFullWidth ? windowWidth : windowWidth / 2) - horizontalMargin * 2;
} else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- lp.width = screenWidth - horizontalMargin * 2;
+ lp.width = windowWidth - horizontalMargin * 2;
}
frame.setLayoutParams(lp);
+ }
+ private int getWindowAvailableWidth() {
+ final WindowMetrics metrics = getWindowManager().getCurrentWindowMetrics();
+ // Gets all excluding insets
+ final WindowInsets windowInsets = metrics.getWindowInsets();
+ Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
+ | WindowInsets.Type.displayCutout());
+ int insetsWidth = insets.right + insets.left;
+ return metrics.getBounds().width() - insetsWidth;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 2def6c7cfdc2..59c1592c1485 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -37,7 +37,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.Flags.glanceableHubFullscreenSwipe
import com.android.systemui.ambient.touch.TouchMonitor
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
import com.android.systemui.communal.dagger.Communal
@@ -309,13 +308,9 @@ constructor(
)
collectFlow(containerView, keyguardInteractor.isDreaming, { isDreaming = it })
- if (glanceableHubFullscreenSwipe()) {
- communalContainerWrapper = CommunalWrapper(containerView.context)
- communalContainerWrapper?.addView(communalContainerView)
- return communalContainerWrapper!!
- } else {
- return containerView
- }
+ communalContainerWrapper = CommunalWrapper(containerView.context)
+ communalContainerWrapper?.addView(communalContainerView)
+ return communalContainerWrapper!!
}
/**
@@ -371,8 +366,7 @@ constructor(
// and the touch is within the horizontal notification band on the screen, do not process
// the touch.
if (
- glanceableHubFullscreenSwipe() &&
- !hubShowing &&
+ !hubShowing &&
!notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y)
) {
return false
@@ -389,17 +383,7 @@ constructor(
val hubOccluded = anyBouncerShowing || shadeShowing
if (isDown && !hubOccluded) {
- if (glanceableHubFullscreenSwipe()) {
- isTrackingHubTouch = true
- } else {
- val x = ev.rawX
- val inOpeningSwipeRegion: Boolean = x >= view.width - rightEdgeSwipeRegionWidth
- if (inOpeningSwipeRegion || hubShowing) {
- // Steal touch events when the hub is open, or if the touch started in the
- // opening gesture region.
- isTrackingHubTouch = true
- }
- }
+ isTrackingHubTouch = true
}
if (isTrackingHubTouch) {
@@ -419,20 +403,12 @@ constructor(
private fun dispatchTouchEvent(view: View, ev: MotionEvent): Boolean {
try {
var handled = false
- if (glanceableHubFullscreenSwipe()) {
- communalContainerWrapper?.dispatchTouchEvent(ev) {
- if (it) {
- handled = true
- }
+ communalContainerWrapper?.dispatchTouchEvent(ev) {
+ if (it) {
+ handled = true
}
- return handled || hubShowing
- } else {
- view.dispatchTouchEvent(ev)
- // Return true regardless of dispatch result as some touches at the start of a
- // gesture
- // may return false from dispatchTouchEvent.
- return true
}
+ return handled || hubShowing
} finally {
powerManager.userActivity(
SystemClock.uptimeMillis(),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 1d43ec277222..8265cee8843e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -3051,7 +3051,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) {
mUpdateMonitor.requestActiveUnlock(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY,
"lockScreenEmptySpaceTap");
} else {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 307e7025c153..9b1e782fcba4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -62,6 +62,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry.
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.util.DumpUtilsKt;
@@ -399,7 +400,7 @@ public class NotificationRemoteInputManager implements CoreStartable {
public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo) {
return activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo,
- null /* userMessageContent */, null /* authBypassCheck */);
+ null /* userMessageContent */, null /* authBypassCheck */);
}
/**
@@ -420,6 +421,12 @@ public class NotificationRemoteInputManager implements CoreStartable {
PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo,
@Nullable String userMessageContent,
@Nullable AuthBypassPredicate authBypassCheck) {
+ if (ExpandHeadsUpOnInlineReply.isEnabled()) {
+ return activateRemoteInputOnExpanded(view, inputs, input, pendingIntent,
+ editedSuggestionInfo, userMessageContent,
+ authBypassCheck);
+ }
+
ViewParent p = view.getParent();
RemoteInputView riv = null;
ExpandableNotificationRow row = null;
@@ -491,6 +498,86 @@ public class NotificationRemoteInputManager implements CoreStartable {
return true;
}
+ /**
+ * Activates a given {@link RemoteInput} on the expanded notification.
+ * If the given notification is not expanded, this method will expand the notification
+ * first and after that activate remote input on the expanded.
+ * @param view The view of the action button or suggestion chip that was tapped.
+ * @param inputs The remote inputs that need to be sent to the app.
+ * @param input The remote input that needs to be activated.
+ * @param pendingIntent The pending intent to be sent to the app.
+ * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or
+ * {@code null} if the user is not editing a smart reply.
+ * @param userMessageContent User-entered text with which to initialize the remote input view.
+ * @param authBypassCheck Optional auth bypass check associated with this remote input
+ * activation. If {@code null}, we never bypass.
+ * @return Whether the {@link RemoteInput} was activated.
+ */
+ public boolean activateRemoteInputOnExpanded(View view, RemoteInput[] inputs, RemoteInput input,
+ PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo,
+ @Nullable String userMessageContent,
+ @Nullable AuthBypassPredicate authBypassCheck) {
+ ViewParent p = view.getParent();
+ RemoteInputView riv = null;
+ ExpandableNotificationRow row = null;
+ while (p != null) {
+ if (p instanceof View) {
+ View pv = (View) p;
+ if (pv.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
+ row = (ExpandableNotificationRow) pv.getTag(R.id.row_tag_for_content_view);
+ break;
+ }
+ }
+ p = p.getParent();
+ }
+
+ if (row == null) {
+ return false;
+ }
+
+ final boolean deferBouncer = authBypassCheck != null;
+ if (!deferBouncer && showBouncerForRemoteInput(view, pendingIntent, row)) {
+ return true;
+ }
+
+ if (!row.getPrivateLayout().getExpandedChild().isShown()) {
+ // The expanded layout is selected, but it's not shown yet, let's wait on it to
+ // show before we do the animation.
+ mCallback.onMakeExpandedVisibleForRemoteInput(row, view, deferBouncer, () -> {
+ activateRemoteInputOnExpanded(view, inputs, input, pendingIntent,
+ editedSuggestionInfo, userMessageContent, authBypassCheck);
+ });
+ return true;
+ }
+
+ riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild());
+ if (riv == null) {
+ return false;
+ }
+
+ if (!riv.isAttachedToWindow()) {
+ // if we still didn't find a view that is attached, let's abort.
+ return false;
+ }
+
+ riv.getController().setPendingIntent(pendingIntent);
+ riv.getController().setRemoteInput(input);
+ riv.getController().setRemoteInputs(inputs);
+ riv.getController().setEditedSuggestionInfo(editedSuggestionInfo);
+ riv.focusAnimated();
+ if (userMessageContent != null) {
+ riv.setEditTextContent(userMessageContent);
+ }
+ if (deferBouncer) {
+ final ExpandableNotificationRow finalRow = row;
+ riv.getController().setBouncerChecker(() ->
+ !authBypassCheck.canSendRemoteInputWithoutBouncer()
+ && showBouncerForRemoteInput(view, pendingIntent, finalRow));
+ }
+
+ return true;
+ }
+
private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent,
ExpandableNotificationRow row) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 79218ae4ca20..89b7ccf09013 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -53,6 +53,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.model.Scenes;
@@ -114,6 +115,7 @@ public class StatusBarStateControllerImpl implements
private final Lazy<ShadeInteractor> mShadeInteractorLazy;
private final Lazy<DeviceUnlockedInteractor> mDeviceUnlockedInteractorLazy;
private final Lazy<SceneInteractor> mSceneInteractorLazy;
+ private final Lazy<SceneContainerOcclusionInteractor> mSceneContainerOcclusionInteractorLazy;
private final Lazy<KeyguardClockInteractor> mKeyguardClockInteractorLazy;
private int mState;
private int mLastState;
@@ -182,6 +184,7 @@ public class StatusBarStateControllerImpl implements
Lazy<ShadeInteractor> shadeInteractorLazy,
Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy,
Lazy<SceneInteractor> sceneInteractorLazy,
+ Lazy<SceneContainerOcclusionInteractor> sceneContainerOcclusionInteractor,
Lazy<KeyguardClockInteractor> keyguardClockInteractorLazy) {
mUiEventLogger = uiEventLogger;
mInteractionJankMonitorLazy = interactionJankMonitorLazy;
@@ -190,6 +193,7 @@ public class StatusBarStateControllerImpl implements
mShadeInteractorLazy = shadeInteractorLazy;
mDeviceUnlockedInteractorLazy = deviceUnlockedInteractorLazy;
mSceneInteractorLazy = sceneInteractorLazy;
+ mSceneContainerOcclusionInteractorLazy = sceneContainerOcclusionInteractor;
mKeyguardClockInteractorLazy = keyguardClockInteractorLazy;
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistoricalRecords[i] = new HistoricalState();
@@ -214,6 +218,7 @@ public class StatusBarStateControllerImpl implements
combineFlows(
mDeviceUnlockedInteractorLazy.get().getDeviceUnlockStatus(),
mSceneInteractorLazy.get().getCurrentScene(),
+ mSceneContainerOcclusionInteractorLazy.get().getInvisibleDueToOcclusion(),
this::calculateStateFromSceneFramework),
this::onStatusBarStateChanged);
}
@@ -664,10 +669,11 @@ public class StatusBarStateControllerImpl implements
private int calculateStateFromSceneFramework(
DeviceUnlockStatus deviceUnlockStatus,
- SceneKey currentScene) {
+ SceneKey currentScene,
+ boolean isOccluded) {
SceneContainerFlag.isUnexpectedlyInLegacyMode();
- if (deviceUnlockStatus.isUnlocked()) {
+ if (deviceUnlockStatus.isUnlocked() || isOccluded) {
return StatusBarState.SHADE;
} else {
return Preconditions.checkNotNull(sStatusBarStateByLockedSceneKey.get(currentScene));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index 79f1874c1f32..3562fd1e9134 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.chips.call.ui.viewmodel
import android.view.View
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityTransitionAnimator
+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
@@ -65,7 +66,9 @@ constructor(
icon =
Icon.Resource(
com.android.internal.R.drawable.ic_phone,
- contentDescription = null,
+ ContentDescription.Resource(
+ R.string.ongoing_phone_call_content_description,
+ ),
),
colors = ColorsModel.Themed,
startTimeMs = startTimeInElapsedRealtime,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
index 42e921ec2b69..73ccaab68ae9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
@@ -85,7 +85,13 @@ constructor(
icon =
Icon.Resource(
CAST_TO_OTHER_DEVICE_ICON,
- ContentDescription.Resource(R.string.accessibility_casting),
+ // Note: This string is "Casting screen", which is okay right now because this
+ // chip does not currently support audio-only casting. If the chip starts
+ // supporting audio-only casting (see b/342169876), update the content
+ // description to just "Casting".
+ ContentDescription.Resource(
+ R.string.cast_to_other_device_chip_accessibility_label,
+ ),
),
colors = ColorsModel.Red,
// TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
index af6d7f27eddd..53679f1c0a6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel
import android.app.ActivityManager
import androidx.annotation.DrawableRes
import com.android.systemui.animation.DialogTransitionAnimator
+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
@@ -64,8 +65,13 @@ constructor(
}
is ScreenRecordChipModel.Recording -> {
OngoingActivityChipModel.Shown.Timer(
- // TODO(b/332662551): Also provide a content description.
- icon = Icon.Resource(ICON, contentDescription = null),
+ icon =
+ Icon.Resource(
+ ICON,
+ ContentDescription.Resource(
+ R.string.screenrecord_ongoing_screen_only,
+ ),
+ ),
colors = ColorsModel.Red,
startTimeMs = systemClock.elapsedRealtime(),
createDialogLaunchOnClickListener(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
index c3b145624aba..8aef5a4e7629 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel
import androidx.annotation.DrawableRes
import com.android.systemui.animation.DialogTransitionAnimator
+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
@@ -78,8 +79,11 @@ constructor(
state: ProjectionChipModel.Projecting,
): OngoingActivityChipModel.Shown {
return OngoingActivityChipModel.Shown.Timer(
- // TODO(b/332662551): Use the right content description.
- icon = Icon.Resource(SHARE_TO_APP_ICON, contentDescription = null),
+ icon =
+ Icon.Resource(
+ SHARE_TO_APP_ICON,
+ ContentDescription.Resource(R.string.share_to_app_chip_accessibility_label),
+ ),
colors = ColorsModel.Red,
// TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
startTimeMs = systemClock.elapsedRealtime(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 4c66f6617312..0bb18d704ac7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -58,7 +58,7 @@ class NotificationWakeUpCoordinator
constructor(
@Application applicationScope: CoroutineScope,
dumpManager: DumpManager,
- private val mHeadsUpManager: HeadsUpManager,
+ private val headsUpManager: HeadsUpManager,
private val statusBarStateController: StatusBarStateController,
private val bypassController: KeyguardBypassController,
private val dozeParameters: DozeParameters,
@@ -71,8 +71,8 @@ constructor(
StatusBarStateController.StateListener,
ShadeExpansionListener,
Dumpable {
- private lateinit var mStackScrollerController: NotificationStackScrollLayoutController
- private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE
+ private lateinit var stackScrollerController: NotificationStackScrollLayoutController
+ private var visibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE
private var inputLinearDozeAmount: Float = 0.0f
private var inputEasedDozeAmount: Float = 0.0f
@@ -85,13 +85,13 @@ constructor(
private var outputEasedDozeAmount: Float = 0.0f
@VisibleForTesting val dozeAmountInterpolator: Interpolator = Interpolators.FAST_OUT_SLOW_IN
- private var mNotificationVisibleAmount = 0.0f
- private var mNotificationsVisible = false
- private var mNotificationsVisibleForExpansion = false
- private var mVisibilityAnimator: ObjectAnimator? = null
- private var mVisibilityAmount = 0.0f
- private var mLinearVisibilityAmount = 0.0f
- private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
+ private var notificationVisibleAmount = 0.0f
+ private var notificationsVisible = false
+ private var notificationsVisibleForExpansion = false
+ private var visibilityAnimator: ObjectAnimator? = null
+ private var visibilityAmount = 0.0f
+ private var linearVisibilityAmount = 0.0f
+ private val entrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
private var pulseExpanding: Boolean = false
private val wakeUpListeners = arrayListOf<WakeUpListener>()
private var state: Int = StatusBarState.KEYGUARD
@@ -104,14 +104,14 @@ constructor(
willWakeUp = false
if (value) {
if (
- mNotificationsVisible &&
- !mNotificationsVisibleForExpansion &&
+ notificationsVisible &&
+ !notificationsVisibleForExpansion &&
!bypassController.bypassEnabled
) {
// We're waking up while pulsing, let's make sure the animation looks nice
- mStackScrollerController.wakeUpFromPulse()
+ stackScrollerController.wakeUpFromPulse()
}
- if (bypassController.bypassEnabled && !mNotificationsVisible) {
+ if (bypassController.bypassEnabled && !notificationsVisible) {
// Let's make sure our huns become visible once we are waking up in case
// they were blocked by the proximity sensor
updateNotificationVisibility(
@@ -186,13 +186,13 @@ constructor(
init {
dumpManager.registerDumpable(this)
- mHeadsUpManager.addListener(this)
+ headsUpManager.addListener(this)
statusBarStateController.addCallback(this)
bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
addListener(
object : WakeUpListener {
override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
- if (isFullyHidden && mNotificationsVisibleForExpansion) {
+ if (isFullyHidden && notificationsVisibleForExpansion) {
// When the notification becomes fully invisible, let's make sure our
// expansion
// flag also changes. This can happen if the bouncer shows when dragging
@@ -217,7 +217,7 @@ constructor(
}
fun setStackScroller(stackScrollerController: NotificationStackScrollLayoutController) {
- mStackScrollerController = stackScrollerController
+ this.stackScrollerController = stackScrollerController
pulseExpanding = stackScrollerController.isPulseExpanding
stackScrollerController.setOnPulseHeightChangedListener {
val nowExpanding = isPulseExpanding()
@@ -237,7 +237,7 @@ constructor(
}
}
- fun isPulseExpanding(): Boolean = mStackScrollerController.isPulseExpanding
+ fun isPulseExpanding(): Boolean = stackScrollerController.isPulseExpanding
/**
* @param visible should notifications be visible
@@ -249,13 +249,13 @@ constructor(
animate: Boolean,
increaseSpeed: Boolean
) {
- mNotificationsVisibleForExpansion = visible
+ notificationsVisibleForExpansion = visible
updateNotificationVisibility(animate, increaseSpeed)
- if (!visible && mNotificationsVisible) {
+ if (!visible && notificationsVisible) {
// If we stopped expanding and we're still visible because we had a pulse that hasn't
// times out, let's release them all to make sure were not stuck in a state where
// notifications are visible
- mHeadsUpManager.releaseAllImmediately()
+ headsUpManager.releaseAllImmediately()
}
}
@@ -269,12 +269,12 @@ constructor(
private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
// TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore
- var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications()
+ var visible = notificationsVisibleForExpansion || headsUpManager.hasNotifications()
visible = visible && canShowPulsingHuns
if (
!visible &&
- mNotificationsVisible &&
+ notificationsVisible &&
(wakingUp || willWakeUp) &&
outputLinearDozeAmount != 0.0f
) {
@@ -290,11 +290,11 @@ constructor(
animate: Boolean,
increaseSpeed: Boolean
) {
- if (mNotificationsVisible == visible) {
+ if (notificationsVisible == visible) {
return
}
- mNotificationsVisible = visible
- mVisibilityAnimator?.cancel()
+ notificationsVisible = visible
+ visibilityAnimator?.cancel()
if (animate) {
notifyAnimationStart(visible)
startVisibilityAnimation(increaseSpeed)
@@ -371,7 +371,7 @@ constructor(
state = statusBarStateController.state,
changed = changed
)
- mStackScrollerController.setDozeAmount(outputEasedDozeAmount)
+ stackScrollerController.setDozeAmount(outputEasedDozeAmount)
updateHideAmount()
if (changed && outputLinearDozeAmount == 0.0f) {
setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
@@ -475,7 +475,7 @@ constructor(
this.collapsedEnoughToHide = collapsedEnough
if (couldShowPulsingHuns && !canShowPulsingHuns) {
updateNotificationVisibility(animate = true, increaseSpeed = true)
- mHeadsUpManager.releaseAllImmediately()
+ headsUpManager.releaseAllImmediately()
}
}
}
@@ -562,12 +562,12 @@ constructor(
}
private fun startVisibilityAnimation(increaseSpeed: Boolean) {
- if (mNotificationVisibleAmount == 0f || mNotificationVisibleAmount == 1f) {
- mVisibilityInterpolator =
- if (mNotificationsVisible) Interpolators.TOUCH_RESPONSE
+ if (notificationVisibleAmount == 0f || notificationVisibleAmount == 1f) {
+ visibilityInterpolator =
+ if (notificationsVisible) Interpolators.TOUCH_RESPONSE
else Interpolators.FAST_OUT_SLOW_IN_REVERSE
}
- val target = if (mNotificationsVisible) 1.0f else 0.0f
+ val target = if (notificationsVisible) 1.0f else 0.0f
val visibilityAnimator = ObjectAnimator.ofFloat(this, notificationVisibility, target)
visibilityAnimator.interpolator = InterpolatorsAndroidX.LINEAR
var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
@@ -576,34 +576,34 @@ constructor(
}
visibilityAnimator.duration = duration
visibilityAnimator.start()
- mVisibilityAnimator = visibilityAnimator
+ this.visibilityAnimator = visibilityAnimator
}
private fun setVisibilityAmount(visibilityAmount: Float) {
logger.logSetVisibilityAmount(visibilityAmount)
- mLinearVisibilityAmount = visibilityAmount
- mVisibilityAmount = mVisibilityInterpolator.getInterpolation(visibilityAmount)
+ linearVisibilityAmount = visibilityAmount
+ this.visibilityAmount = visibilityInterpolator.getInterpolation(visibilityAmount)
handleAnimationFinished()
updateHideAmount()
}
private fun handleAnimationFinished() {
- if (outputLinearDozeAmount == 0.0f || mLinearVisibilityAmount == 0.0f) {
- mEntrySetToClearWhenFinished.forEach { it.setHeadsUpAnimatingAway(false) }
- mEntrySetToClearWhenFinished.clear()
+ if (outputLinearDozeAmount == 0.0f || linearVisibilityAmount == 0.0f) {
+ entrySetToClearWhenFinished.forEach { it.setHeadsUpAnimatingAway(false) }
+ entrySetToClearWhenFinished.clear()
}
}
private fun updateHideAmount() {
- val linearAmount = min(1.0f - mLinearVisibilityAmount, outputLinearDozeAmount)
- val amount = min(1.0f - mVisibilityAmount, outputEasedDozeAmount)
+ val linearAmount = min(1.0f - linearVisibilityAmount, outputLinearDozeAmount)
+ val amount = min(1.0f - visibilityAmount, outputEasedDozeAmount)
logger.logSetHideAmount(linearAmount)
- mStackScrollerController.setHideAmount(linearAmount, amount)
+ stackScrollerController.setHideAmount(linearAmount, amount)
notificationsFullyHidden = linearAmount == 1.0f
}
private fun notifyAnimationStart(awake: Boolean) {
- mStackScrollerController.notifyHideAnimationStart(!awake)
+ stackScrollerController.notifyHideAnimationStart(!awake)
}
override fun onDozingChanged(isDozing: Boolean) {
@@ -615,7 +615,7 @@ constructor(
override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
var animate = shouldAnimateVisibility()
if (!isHeadsUp) {
- if (outputLinearDozeAmount != 0.0f && mLinearVisibilityAmount != 0.0f) {
+ if (outputLinearDozeAmount != 0.0f && linearVisibilityAmount != 0.0f) {
if (entry.isRowDismissed) {
// if we animate, we see the shelf briefly visible. Instead we fully animate
// the notification and its background out
@@ -623,11 +623,11 @@ constructor(
} else if (!wakingUp && !willWakeUp) {
// TODO: look that this is done properly and not by anyone else
entry.setHeadsUpAnimatingAway(true)
- mEntrySetToClearWhenFinished.add(entry)
+ entrySetToClearWhenFinished.add(entry)
}
}
- } else if (mEntrySetToClearWhenFinished.contains(entry)) {
- mEntrySetToClearWhenFinished.remove(entry)
+ } else if (entrySetToClearWhenFinished.contains(entry)) {
+ entrySetToClearWhenFinished.remove(entry)
entry.setHeadsUpAnimatingAway(false)
}
updateNotificationVisibility(animate, increaseSpeed = false)
@@ -644,11 +644,11 @@ constructor(
pw.println("hardDozeAmountOverrideSource: $hardDozeAmountOverrideSource")
pw.println("outputLinearDozeAmount: $outputLinearDozeAmount")
pw.println("outputEasedDozeAmount: $outputEasedDozeAmount")
- pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
- pw.println("mNotificationsVisible: $mNotificationsVisible")
- pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
- pw.println("mVisibilityAmount: $mVisibilityAmount")
- pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount")
+ pw.println("notificationVisibleAmount: $notificationVisibleAmount")
+ pw.println("notificationsVisible: $notificationsVisible")
+ pw.println("notificationsVisibleForExpansion: $notificationsVisibleForExpansion")
+ pw.println("visibilityAmount: $visibilityAmount")
+ pw.println("linearVisibilityAmount: $linearVisibilityAmount")
pw.println("pulseExpanding: $pulseExpanding")
pw.println("state: ${StatusBarState.toString(state)}")
pw.println("fullyAwake: $fullyAwake")
@@ -698,7 +698,7 @@ constructor(
}
override fun get(coordinator: NotificationWakeUpCoordinator): Float {
- return coordinator.mLinearVisibilityAmount
+ return coordinator.linearVisibilityAmount
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 05d71967321f..f73223f3370b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -111,6 +111,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationChildrenCon
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.SwipeableView;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
@@ -328,38 +329,61 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private OnClickListener mExpandClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
- if (!shouldShowPublic() && (!mIsMinimized || isExpanded())
- && mGroupMembershipManager.isGroupSummary(mEntry)) {
- mGroupExpansionChanging = true;
- final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
- boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
- mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ toggleExpansionState(v, /* shouldLogExpandClickMetric = */true);
+ }
+ };
+
+ /**
+ * Toggles expansion state.
+ */
+ public void toggleExpansionState() {
+ toggleExpansionState(this, /*shouldLogExpandClickMetric*/ false);
+ }
+
+ private void toggleExpansionState(View v, boolean shouldLogExpandClickMetric) {
+ if (!shouldShowPublic() && (!mIsMinimized || isExpanded())
+ && mGroupMembershipManager.isGroupSummary(mEntry)) {
+ mGroupExpansionChanging = true;
+ final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
+ boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
+ mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ if (shouldLogExpandClickMetric) {
mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
- onExpansionChanged(true /* userAction */, wasExpanded);
- } else if (mEnableNonGroupedNotificationExpand) {
- if (v.isAccessibilityFocused()) {
- mPrivateLayout.setFocusOnVisibilityChange();
- }
- boolean nowExpanded;
- if (isPinned()) {
- nowExpanded = !mExpandedWhenPinned;
- mExpandedWhenPinned = nowExpanded;
- // Also notify any expansion changed listeners. This is necessary since the
- // expansion doesn't actually change (it's already system expanded) but it
- // changes visually
- if (mExpansionChangedListener != null) {
- mExpansionChangedListener.onExpansionChanged(nowExpanded);
- }
- } else {
- nowExpanded = !isExpanded();
- setUserExpanded(nowExpanded);
+ }
+ onExpansionChanged(true /* userAction */, wasExpanded);
+ } else if (mEnableNonGroupedNotificationExpand) {
+ if (v.isAccessibilityFocused()) {
+ mPrivateLayout.setFocusOnVisibilityChange();
+ }
+ boolean nowExpanded;
+ if (isPinned()) {
+ nowExpanded = !mExpandedWhenPinned;
+ mExpandedWhenPinned = nowExpanded;
+ // Also notify any expansion changed listeners. This is necessary since the
+ // expansion doesn't actually change (it's already system expanded) but it
+ // changes visually
+ if (mExpansionChangedListener != null) {
+ mExpansionChangedListener.onExpansionChanged(nowExpanded);
}
- notifyHeightChanged(/* needsAnimation= */ true);
- mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ } else {
+ nowExpanded = !isExpanded();
+ setUserExpanded(nowExpanded);
+ }
+
+ if (ExpandHeadsUpOnInlineReply.isEnabled() && mExpandable) {
+ // it is triggered by the user.
+ // So, mHasUserChangedExpansion should be marked true.
+ mHasUserChangedExpansion = true;
+ }
+
+ notifyHeightChanged(/* needsAnimation= */ true);
+ mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ if (shouldLogExpandClickMetric) {
mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded);
}
}
- };
+ }
+
private boolean mKeepInParentForDismissAnimation;
private boolean mRemoved;
public static final FloatProperty<ExpandableNotificationRow> TRANSLATE_CONTENT =
@@ -2845,9 +2869,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public boolean isExpanded(boolean allowOnKeyguard) {
+ // System expanded should be ignored in heads up state
+ final boolean isHeadsUpState = ExpandHeadsUpOnInlineReply.isEnabled()
+ && canShowHeadsUp() && isHeadsUpState();
+ // Heads Up Notification can be expanded when it is pinned.
+ final boolean isPinnedAndExpanded =
+ isHeadsUpState && isPinnedAndExpanded();
return (!shouldShowPublic()) && (!mOnKeyguard || allowOnKeyguard)
- && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
- || isUserExpanded());
+ && (!hasUserChangedExpansion() && !isHeadsUpState
+ && (isSystemExpanded() || isSystemChildExpanded())
+ || isUserExpanded() || isPinnedAndExpanded);
}
private boolean isSystemChildExpanded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 7fc331d6adab..96b1cf2db662 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -61,6 +61,7 @@ import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInf
import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -913,7 +914,9 @@ public class NotificationContentView extends FrameLayout implements Notification
View visibleView = getViewForVisibleType(visibleType);
if (visibleView != null) {
visibleView.setVisibility(VISIBLE);
- transferRemoteInputFocus(visibleType);
+ if (!ExpandHeadsUpOnInlineReply.isEnabled()) {
+ transferRemoteInputFocus(visibleType);
+ }
}
if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
@@ -1397,30 +1400,39 @@ public class NotificationContentView extends FrameLayout implements Notification
mCachedExpandedRemoteInput = null;
mCachedExpandedRemoteInputViewController = null;
- if (mHeadsUpChild != null) {
- RemoteInputViewData headsUpData = applyRemoteInput(mHeadsUpChild, mNotificationEntry,
- hasFreeformRemoteInput, mPreviousHeadsUpRemoteInputIntent,
- mCachedHeadsUpRemoteInput, mCachedHeadsUpRemoteInputViewController,
- mHeadsUpWrapper);
- mHeadsUpRemoteInput = headsUpData.mView;
- mHeadsUpRemoteInputController = headsUpData.mController;
- if (mHeadsUpRemoteInputController != null) {
- mHeadsUpRemoteInputController.bind();
- }
- } else {
+ if (ExpandHeadsUpOnInlineReply.isEnabled()) {
mHeadsUpRemoteInput = null;
- if (mHeadsUpRemoteInputController != null) {
- mHeadsUpRemoteInputController.unbind();
- }
mHeadsUpRemoteInputController = null;
+ mCachedHeadsUpRemoteInput = null;
+ mCachedHeadsUpRemoteInputViewController = null;
+ } else {
+ ExpandHeadsUpOnInlineReply.assertInLegacyMode();
+ if (mHeadsUpChild != null) {
+ RemoteInputViewData headsUpData = applyRemoteInput(mHeadsUpChild,
+ mNotificationEntry,
+ hasFreeformRemoteInput, mPreviousHeadsUpRemoteInputIntent,
+ mCachedHeadsUpRemoteInput, mCachedHeadsUpRemoteInputViewController,
+ mHeadsUpWrapper);
+ mHeadsUpRemoteInput = headsUpData.mView;
+ mHeadsUpRemoteInputController = headsUpData.mController;
+ if (mHeadsUpRemoteInputController != null) {
+ mHeadsUpRemoteInputController.bind();
+ }
+ } else {
+ mHeadsUpRemoteInput = null;
+ if (mHeadsUpRemoteInputController != null) {
+ mHeadsUpRemoteInputController.unbind();
+ }
+ mHeadsUpRemoteInputController = null;
+ }
+ if (mCachedHeadsUpRemoteInput != null
+ && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
+ // We had a cached remote input but didn't reuse it. Clean up required.
+ mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
+ }
+ mCachedHeadsUpRemoteInput = null;
+ mCachedHeadsUpRemoteInputViewController = null;
}
- if (mCachedHeadsUpRemoteInput != null
- && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
- // We had a cached remote input but didn't reuse it. Clean up required.
- mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
- }
- mCachedHeadsUpRemoteInput = null;
- mCachedHeadsUpRemoteInputViewController = null;
}
private RemoteInputViewData applyRemoteInput(View view, NotificationEntry entry,
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 d54e66ee3525..ddfa86d1e26f 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
@@ -89,7 +89,6 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.TouchLogger;
@@ -620,8 +619,6 @@ public class NotificationStackScrollLayout
@Nullable
private OnClickListener mManageButtonClickListener;
- @Nullable
- private OnNotificationRemovedListener mOnNotificationRemovedListener;
public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0, 0);
@@ -773,10 +770,6 @@ public class NotificationStackScrollLayout
&& !mIsRemoteInputActive;
}
- public NotificationSwipeActionHelper getSwipeActionHelper() {
- return mSwipeHelper;
- }
-
void updateBgColor() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
@@ -1980,17 +1973,6 @@ public class NotificationStackScrollLayout
return insets;
}
- private final Runnable mReclamp = new Runnable() {
- @Override
- public void run() {
- int range = getScrollRange();
- mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
- mDontReportNextOverScroll = true;
- mDontClampNextScroll = true;
- animateScroll();
- }
- };
-
public void setExpandingEnabled(boolean enable) {
mExpandHelper.setEnabled(enable);
}
@@ -2444,17 +2426,6 @@ public class NotificationStackScrollLayout
return null;
}
- private ExpandableNotificationRow getLastRowNotGone() {
- int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- View child = getChildAt(i);
- if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
- return (ExpandableNotificationRow) child;
- }
- }
- return null;
- }
-
/**
* @return the number of children which have visibility unequal to GONE
*/
@@ -2704,10 +2675,6 @@ public class NotificationStackScrollLayout
return mTopPaddingOverflow;
}
- private int clampPadding(int desiredPadding) {
- return Math.max(desiredPadding, mIntrinsicPadding);
- }
-
private float getRubberBandFactor(boolean onTop) {
if (!onTop) {
return RUBBER_BAND_FACTOR_NORMAL;
@@ -4474,6 +4441,7 @@ public class NotificationStackScrollLayout
}
void goToFullShade(long delay) {
+ SceneContainerFlag.assertInLegacyMode();
mGoToFullShadeNeedsAnimation = true;
mGoToFullShadeDelay = delay;
mNeedsAnimation = true;
@@ -6582,16 +6550,4 @@ public class NotificationStackScrollLayout
void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
}
-
- /**
- *
- */
- public interface OnNotificationRemovedListener {
- /**
- *
- * @param child
- * @param isTransferInProgress
- */
- void onNotificationRemoved(ExpandableView child, boolean isTransferInProgress);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index d9846850fc71..bf53ee2b73c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1177,6 +1177,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
public void goToFullShade(long delay) {
+ SceneContainerFlag.assertInLegacyMode();
mView.goToFullShade(delay);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandHeadsUpOnInlineReply.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandHeadsUpOnInlineReply.kt
new file mode 100644
index 000000000000..d4e2688395bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandHeadsUpOnInlineReply.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the expand heads up on inline reply flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object ExpandHeadsUpOnInlineReply {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_EXPAND_HEADS_UP_ON_INLINE_REPLY
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.expandHeadsUpOnInlineReply()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index 639560fcdcd5..e96326a945b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -34,6 +34,7 @@ import android.view.WindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.Flags.communalHub
+import com.android.systemui.Flags.mediaLockscreenLaunchAnimation
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DelegateTransitionAnimatorController
import com.android.systemui.assist.AssistManager
@@ -635,8 +636,9 @@ constructor(
isActivityIntent: Boolean,
showOverLockscreen: Boolean,
): Boolean {
- // TODO(b/294418322): Support launch animations when occluded.
- if (keyguardStateController.isOccluded) {
+ // TODO(b/294418322): always support launch animations when occluded.
+ val ignoreOcclusion = showOverLockscreen && mediaLockscreenLaunchAnimation()
+ if (keyguardStateController.isOccluded && !ignoreOcclusion) {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 4505a1d2c548..b1754fd59cee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -192,27 +192,42 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
if (!deferBouncer && mKeyguardStateController.isShowing()) {
onLockedRemoteInput(row, clickedView);
} else {
- if (row.isChildInGroup() && !row.areChildrenExpanded()) {
- // The group isn't expanded, let's make sure it's visible!
- mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
- }
-
- if (android.app.Flags.compactHeadsUpNotificationReply()
- && row.isCompactConversationHeadsUpOnScreen()) {
- // Notification can be system expanded true and it is set user expanded in
- // activateRemoteInput. notifyHeightChanged also doesn't work as visibleType doesn't
- // change. To expand huning notification properly, we need set userExpanded false.
- if (!row.isPinned() && row.isExpanded()) {
- row.setUserExpanded(false);
+ if (ExpandHeadsUpOnInlineReply.isEnabled()) {
+ if (row.isChildInGroup() && !row.areChildrenExpanded()) {
+ // The group isn't expanded, let's make sure it's visible!
+ mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+ } else if (!row.isChildInGroup() && !row.isExpanded()) {
+ // notification isn't expanded, let's make sure it's visible!
+ row.toggleExpansionState();
+ row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
}
- // expand notification emits expanded information to HUN listener.
- row.expandNotification();
} else {
- // Note: Since Normal HUN has remote input view in it, we don't expect to hit
- // onMakeExpandedVisibleForRemoteInput from activateRemoteInput for Normal HUN.
- row.setUserExpanded(true);
+ if (row.isChildInGroup() && !row.areChildrenExpanded()) {
+ // The group isn't expanded, let's make sure it's visible!
+ mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+ }
+
+ if (android.app.Flags.compactHeadsUpNotificationReply()
+ && row.isCompactConversationHeadsUpOnScreen()) {
+ // Notification can be system expanded true and it is set user expanded in
+ // activateRemoteInput. notifyHeightChanged also doesn't work as visibleType
+ // doesn't change. To expand huning notification properly,
+ // we need set userExpanded false.
+ if (!row.isPinned() && row.isExpanded()) {
+ row.setUserExpanded(false);
+ }
+ // expand notification emits expanded information to HUN listener.
+ row.expandNotification();
+ } else {
+ // TODO(b/346976443) Group and normal notification expansions are two different
+ // concepts. We should never call setUserExpanded for expanding groups.
+
+ // Note: Since Normal HUN has remote input view in it, we don't expect to hit
+ // onMakeExpandedVisibleForRemoteInput from activateRemoteInput for Normal HUN.
+ row.setUserExpanded(true);
+ }
+ row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
}
- row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index 68983a1a10b3..631befc5dd29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -108,6 +108,9 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa
setChipMainContent(chipModel, chipTextView, chipTimeView)
chipView.setOnClickListener(chipModel.onClickListener)
+ // Accessibility
+ setChipAccessibility(chipModel, chipView)
+
// Colors
val textColor = chipModel.colors.text(chipContext)
chipIconView.imageTintList = ColorStateList.valueOf(textColor)
@@ -116,6 +119,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa
(chipBackgroundView.background as GradientDrawable).color =
chipModel.colors.background(chipContext)
+ // Notify listeners
listener.onOngoingActivityStatusChanged(
hasOngoingActivity = true
)
@@ -191,6 +195,18 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa
this.setPaddingRelative(/* start= */ 0, paddingTop, paddingEnd, paddingBottom)
}
+ private fun setChipAccessibility(chipModel: OngoingActivityChipModel.Shown, chipView: View) {
+ when (chipModel) {
+ is OngoingActivityChipModel.Shown.Countdown -> {
+ // Set as assertive so talkback will announce the countdown
+ chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
+ }
+ is OngoingActivityChipModel.Shown.Timer -> {
+ chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE
+ }
+ }
+ }
+
private fun animateLightsOutView(view: View, visible: Boolean) {
view.animate().cancel()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 8858d132c4be..48f6cc4261a9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -28,6 +28,7 @@ import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
@@ -40,6 +41,7 @@ import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
+import dagger.Lazy
import java.io.PrintWriter
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -51,7 +53,6 @@ import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import dagger.Lazy
@SmallTest
class ActiveUnlockConfigTest : SysuiTestCase() {
@@ -111,6 +112,48 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
)
)
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
+ )
+ }
+
+ @Test
+ fun onUnlockIntentLegacySettingChanged() {
+ // GIVEN no active unlock settings enabled
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
+
+ // WHEN unlock on unlock intent legacy is allowed
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY))
+
+ // THEN active unlock triggers allowed on unlock_intent_legacy, unlock_intent,
+ // AND biometric fail
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
assertTrue(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
@@ -132,16 +175,21 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
)
)
- // WHEN unlock on biometric failed is allowed
+ // WHEN unlock on unlock intent is allowed
secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 1, currentUser)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
- // THEN active unlock triggers allowed on: biometric failure ONLY
+ // THEN active unlock triggers allowed on: unlock intent AND biometric failure
assertFalse(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
)
)
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
assertTrue(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
@@ -184,6 +232,11 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
)
assertFalse(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index 361a9450399a..16b9ab5c1652 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.accessibility;
-import static com.android.systemui.accessibility.Magnification.DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS;
+import static com.android.systemui.accessibility.MagnificationImpl.DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -65,7 +65,7 @@ import org.mockito.MockitoAnnotations;
/**
* Tests for {@link android.view.accessibility.IMagnificationConnection} retrieved from
- * {@link Magnification}
+ * {@link MagnificationImpl}
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -104,7 +104,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
private IWindowManager mIWindowManager;
private IMagnificationConnection mIMagnificationConnection;
- private Magnification mMagnification;
+ private MagnificationImpl mMagnification;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private TestableLooper mTestableLooper;
@@ -119,11 +119,11 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
any(IMagnificationConnection.class));
mTestableLooper = TestableLooper.get(this);
assertNotNull(mTestableLooper);
- mMagnification = new Magnification(getContext(),
+ mMagnification = new MagnificationImpl(getContext(),
mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
mDisplayTracker, getContext().getSystemService(DisplayManager.class),
- mA11yLogger, mIWindowManager);
+ mA11yLogger, mIWindowManager, mAccessibilityManager);
mMagnification.mWindowMagnificationControllerSupplier =
new FakeWindowMagnificationControllerSupplier(
mContext.getSystemService(DisplayManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index 17b7e21f5e74..41a41164b2a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -84,7 +84,7 @@ public class MagnificationTest extends SysuiTestCase {
private SecureSettings mSecureSettings;
private CommandQueue mCommandQueue;
- private Magnification mMagnification;
+ private MagnificationImpl mMagnification;
private OverviewProxyListener mOverviewProxyListener;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@@ -124,11 +124,12 @@ public class MagnificationTest extends SysuiTestCase {
when(mWindowMagnificationController.isActivated()).thenReturn(true);
mCommandQueue = new CommandQueue(getContext(), mDisplayTracker);
- mMagnification = new Magnification(getContext(),
+ mMagnification = new MagnificationImpl(getContext(),
getContext().getMainThreadHandler(), mContext.getMainExecutor(),
mCommandQueue, mModeSwitchesController,
mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker,
- getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager);
+ getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager,
+ getContext().getSystemService(AccessibilityManager.class));
mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class), mWindowMagnificationController);
mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
index 4118c90a80a0..64c162f3d983 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
@@ -212,6 +212,40 @@ public class TouchMonitorTest extends SysuiTestCase {
}
@Test
+ public void testSessionResetOnLifecycle() {
+ final TouchHandler touchHandler = createTouchHandler();
+ final Rect touchArea = new Rect(4, 4, 8 , 8);
+
+ doAnswer(invocation -> {
+ final Region region = (Region) invocation.getArguments()[1];
+ region.set(touchArea);
+ return null;
+ }).when(touchHandler).getTouchInitiationRegion(any(), any(), any());
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)), mKosmos);
+
+ // Ensure touch outside specified region is not delivered.
+ final MotionEvent initialEvent = Mockito.mock(MotionEvent.class);
+
+ // Make sure touch inside region causes session start.
+ when(initialEvent.getX()).thenReturn(5.0f);
+ when(initialEvent.getY()).thenReturn(5.0f);
+ environment.publishInputEvent(initialEvent);
+ verify(touchHandler).onSessionStart(any());
+
+ Mockito.clearInvocations(touchHandler);
+
+ // Reset lifecycle, forcing monitoring to be reset
+ environment.updateLifecycle(Lifecycle.State.STARTED);
+ environment.updateLifecycle(Lifecycle.State.RESUMED);
+ environment.executeAll();
+
+ environment.publishInputEvent(initialEvent);
+ verify(touchHandler).onSessionStart(any());
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_AMBIENT_TOUCH_MONITOR_LISTEN_TO_DISPLAY_CHANGES)
public void testConfigurationListenerUpdatesBounds() {
final TouchHandler touchHandler = createTouchHandler();
@@ -332,7 +366,6 @@ public class TouchMonitorTest extends SysuiTestCase {
}
}
-
@Test
public void testNoActiveSessionWhenHandlerDisabled() {
final TouchHandler touchHandler = Mockito.mock(TouchHandler.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 93c6d9ee732c..0ed40e9be471 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.biometrics.ui.viewmodel
+package com.android.systemui.biometrics.ui.kosmos.promptViewmodel
import android.app.ActivityManager.RunningTaskInfo
-import android.app.ActivityTaskManager
import android.content.ComponentName
+import android.content.applicationContext
+import android.content.packageManager
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Configuration
import android.graphics.Bitmap
@@ -41,26 +41,20 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.platform.test.annotations.EnableFlags
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
+import android.view.Surface
import androidx.test.filters.SmallTest
-import com.android.internal.widget.LockPatternUtils
-import com.android.launcher3.icons.IconProvider
+import com.android.app.activityTaskManager
import com.android.systemui.Flags.FLAG_BP_TALKBACK
import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.Utils.toBitmap
-import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
-import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
-import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
-import com.android.systemui.biometrics.data.repository.FakePromptRepository
-import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
-import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.biometrics.authController
+import com.android.systemui.biometrics.data.repository.biometricStatusRepository
+import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
-import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
+import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor
+import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
import com.android.systemui.biometrics.extractAuthenticatorTypes
import com.android.systemui.biometrics.faceSensorPropertiesInternal
import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
@@ -68,18 +62,26 @@ import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.shared.model.toSensorStrength
import com.android.systemui.biometrics.shared.model.toSensorType
+import com.android.systemui.biometrics.udfpsUtils
+import com.android.systemui.biometrics.ui.viewmodel.FingerprintStartMode
+import com.android.systemui.biometrics.ui.viewmodel.PromptMessage
+import com.android.systemui.biometrics.ui.viewmodel.PromptPosition
+import com.android.systemui.biometrics.ui.viewmodel.PromptSize
+import com.android.systemui.biometrics.ui.viewmodel.iconProvider
+import com.android.systemui.biometrics.ui.viewmodel.promptViewModel
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
@@ -94,7 +96,9 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -113,20 +117,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
- @Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var authController: AuthController
- @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
- @Mock private lateinit var udfpsUtils: UdfpsUtils
- @Mock private lateinit var packageManager: PackageManager
- @Mock private lateinit var iconProvider: IconProvider
@Mock private lateinit var applicationInfoWithIcon: ApplicationInfo
@Mock private lateinit var applicationInfoNoIcon: ApplicationInfo
- @Mock private lateinit var activityTaskManager: ActivityTaskManager
@Mock private lateinit var activityInfo: ActivityInfo
@Mock private lateinit var runningTaskInfo: RunningTaskInfo
- private val fakeExecutor = FakeExecutor(FakeSystemClock())
- private val testScope = TestScope()
private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
private val defaultLogoIconWithOverrides = context.getDrawable(R.drawable.ic_add)
private val logoResFromApp = R.drawable.ic_cake
@@ -156,28 +152,81 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
context.resources.getDimensionPixelSize(
R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding
)
+ private val mockFaceIconSize = 200
+ private val mockFingerprintIconWidth = 300
+ private val mockFingerprintIconHeight = 300
+
+ /** Mock [UdfpsOverlayParams] for a test. */
+ private fun mockUdfpsOverlayParams(isLandscape: Boolean = false): UdfpsOverlayParams =
+ UdfpsOverlayParams(
+ sensorBounds = Rect(400, 1600, 600, 1800),
+ overlayBounds = Rect(0, 1200, 1000, 2400),
+ naturalDisplayWidth = 1000,
+ naturalDisplayHeight = 3000,
+ scaleFactor = 1f,
+ rotation = if (isLandscape) Surface.ROTATION_90 else Surface.ROTATION_0
+ )
- private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
- private lateinit var promptRepository: FakePromptRepository
- private lateinit var displayStateRepository: FakeDisplayStateRepository
- private lateinit var biometricStatusRepository: FakeBiometricStatusRepository
- private lateinit var displayRepository: FakeDisplayRepository
- private lateinit var displayStateInteractor: DisplayStateInteractor
- private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
- private lateinit var biometricStatusInteractor: BiometricStatusInteractor
-
- private lateinit var selector: PromptSelectorInteractor
- private lateinit var viewModel: PromptViewModel
- private lateinit var iconViewModel: PromptIconViewModel
private lateinit var promptContentView: PromptContentView
private lateinit var promptContentViewWithMoreOptionsButton:
PromptContentViewWithMoreOptionsButton
+ private val kosmos = Kosmos()
+
@Before
fun setup() {
- fingerprintRepository = FakeFingerprintPropertyRepository()
+
+ // Set up default logo info and app customized info
+ whenever(kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_ICON), anyInt()))
+ .thenReturn(applicationInfoNoIcon)
+ whenever(kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME), anyInt()))
+ .thenReturn(applicationInfoWithIcon)
+ whenever(
+ kosmos.packageManager.getApplicationInfo(
+ eq(packageNameForLogoWithOverrides),
+ anyInt()
+ )
+ )
+ .thenReturn(applicationInfoWithIcon)
+ whenever(
+ kosmos.packageManager.getApplicationInfo(
+ eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND),
+ anyInt()
+ )
+ )
+ .thenThrow(NameNotFoundException())
+
+ whenever(kosmos.packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo)
+ whenever(kosmos.iconProvider.getIcon(activityInfo)).thenReturn(defaultLogoIconWithOverrides)
+ whenever(kosmos.packageManager.getApplicationIcon(applicationInfoWithIcon))
+ .thenReturn(defaultLogoIcon)
+ whenever(kosmos.packageManager.getApplicationLabel(applicationInfoWithIcon))
+ .thenReturn(defaultLogoDescription)
+ whenever(kosmos.packageManager.getUserBadgedIcon(any(), any())).then { it.getArgument(0) }
+ whenever(kosmos.packageManager.getUserBadgedLabel(any(), any())).then { it.getArgument(0) }
+
+ context.setMockPackageManager(kosmos.packageManager)
+ overrideResource(logoResFromApp, logoDrawableFromAppRes)
+ overrideResource(
+ R.array.biometric_dialog_package_names_for_logo_with_overrides,
+ arrayOf(packageNameForLogoWithOverrides)
+ )
+
+ overrideResource(R.dimen.biometric_dialog_fingerprint_icon_width, mockFingerprintIconWidth)
+ overrideResource(
+ R.dimen.biometric_dialog_fingerprint_icon_height,
+ mockFingerprintIconHeight
+ )
+ overrideResource(R.dimen.biometric_dialog_face_icon_size, mockFaceIconSize)
+
+ kosmos.applicationContext = context
+
+ if (testCase.fingerprint?.isAnyUdfpsType == true) {
+ kosmos.authController = authController
+ }
+
testCase.fingerprint?.let {
- fingerprintRepository.setProperties(
+ kosmos.fakeFingerprintPropertyRepository.setProperties(
it.sensorId,
it.sensorStrength.toSensorStrength(),
it.sensorType.toSensorType(),
@@ -186,31 +235,20 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
)
}
- promptRepository = FakePromptRepository()
- displayStateRepository = FakeDisplayStateRepository()
- displayRepository = FakeDisplayRepository()
- displayStateInteractor =
- DisplayStateInteractorImpl(
- testScope.backgroundScope,
- mContext,
- fakeExecutor,
- displayStateRepository,
- displayRepository,
- )
- udfpsOverlayInteractor =
- UdfpsOverlayInteractor(
- context,
- authController,
- selectedUserInteractor,
- testScope.backgroundScope
- )
- biometricStatusRepository = FakeBiometricStatusRepository()
- biometricStatusInteractor =
- BiometricStatusInteractorImpl(
- activityTaskManager,
- biometricStatusRepository,
- fingerprintRepository
- )
+
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ testCase.fingerprint?.isAnySidefpsType.let {
+ kosmos.displayStateRepository.setIsInRearDisplayMode(testCase.isInRearDisplayMode)
+ if (testCase.isDeviceFolded) {
+ kosmos.promptViewModel.iconViewModel.onConfigurationChanged(
+ getFoldedConfiguration()
+ )
+ } else {
+ kosmos.promptViewModel.iconViewModel.onConfigurationChanged(
+ getUnfoldedConfiguration()
+ )
+ }
+ }
promptContentView =
PromptVerticalListContentView.Builder()
@@ -221,47 +259,29 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
promptContentViewWithMoreOptionsButton =
PromptContentViewWithMoreOptionsButton.Builder()
.setDescription("test")
- .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
+ .setMoreOptionsButtonListener(kosmos.fakeExecutor) { _, _ -> }
.build()
-
- // Set up default logo info and app customized info
- whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_ICON), anyInt()))
- .thenReturn(applicationInfoNoIcon)
- whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME), anyInt()))
- .thenReturn(applicationInfoWithIcon)
- whenever(packageManager.getApplicationInfo(eq(packageNameForLogoWithOverrides), anyInt()))
- .thenReturn(applicationInfoWithIcon)
- whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND), anyInt()))
- .thenThrow(NameNotFoundException())
-
- whenever(packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo)
- whenever(iconProvider.getIcon(activityInfo)).thenReturn(defaultLogoIconWithOverrides)
- whenever(packageManager.getApplicationIcon(applicationInfoWithIcon))
- .thenReturn(defaultLogoIcon)
- whenever(packageManager.getApplicationLabel(applicationInfoWithIcon))
- .thenReturn(defaultLogoDescription)
- whenever(packageManager.getUserBadgedIcon(any(), any())).then { it.getArgument(0) }
- whenever(packageManager.getUserBadgedLabel(any(), any())).then { it.getArgument(0) }
-
- context.setMockPackageManager(packageManager)
- val resources = context.getOrCreateTestableResources()
- resources.addOverride(logoResFromApp, logoDrawableFromAppRes)
- resources.addOverride(
- R.array.biometric_dialog_package_names_for_logo_with_overrides,
- arrayOf(packageNameForLogoWithOverrides)
- )
}
@Test
fun start_idle_and_show_authenticating() =
runGenericTest(doNotStart = true) {
- val expectedSize =
+ var expectedPromptSize =
if (testCase.shouldStartAsImplicitFlow) PromptSize.SMALL else PromptSize.MEDIUM
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val modalities by collectLastValue(viewModel.modalities)
- val message by collectLastValue(viewModel.message)
- val size by collectLastValue(viewModel.size)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val modalities by collectLastValue(kosmos.promptViewModel.modalities)
+ val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+ val iconOverlayAsset by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.iconOverlayAsset)
+ val shouldAnimateIconView by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView)
+ val shouldAnimateIconOverlay by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconOverlay)
+ val iconContentDescriptionId by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val size by collectLastValue(kosmos.promptViewModel.size)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isNotAuthenticated).isTrue()
@@ -269,131 +289,49 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(hasFace).isEqualTo(testCase.face != null)
assertThat(hasFingerprint).isEqualTo(testCase.fingerprint != null)
}
- assertThat(message).isEqualTo(PromptMessage.Empty)
- assertThat(size).isEqualTo(expectedSize)
-
- val startMessage = "here we go"
- viewModel.showAuthenticating(startMessage, isRetry = false)
-
- assertThat(message).isEqualTo(PromptMessage.Help(startMessage))
- assertThat(authenticating).isTrue()
- assertThat(authenticated?.isNotAuthenticated).isTrue()
- assertThat(size).isEqualTo(expectedSize)
- assertButtonsVisible(negative = expectedSize != PromptSize.SMALL)
- }
-
- @Test
- fun shows_authenticated_with_no_errors() = runGenericTest {
- // this case can't happen until fingerprint is started
- // trigger it now since no error has occurred in this test
- val forceError = testCase.isCoex && testCase.authenticatedByFingerprint
- if (forceError) {
- assertThat(viewModel.fingerprintStartMode.first())
- .isEqualTo(FingerprintStartMode.Pending)
- viewModel.ensureFingerprintHasStarted(isDelayed = true)
- }
-
- showAuthenticated(
- testCase.authenticatedModality,
- testCase.expectConfirmation(atLeastOneFailure = forceError),
- )
- }
-
- @Test
- fun set_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
- runGenericTest {
- val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+ assertThat(message).isEqualTo(PromptMessage.Empty)
+ assertThat(size).isEqualTo(expectedPromptSize)
- viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
+ val forceExplicitFlow =
+ testCase.isCoex && testCase.confirmationRequested ||
+ testCase.authenticatedByFingerprint
- val confirmHaptics by collectLastValue(viewModel.hapticsToPlay)
- assertThat(confirmHaptics?.hapticFeedbackConstant)
- .isEqualTo(
- if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
- else HapticFeedbackConstants.CONFIRM
- )
- assertThat(confirmHaptics?.flag)
- .isEqualTo(
- if (expectConfirmation) null
- else HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
- )
-
- if (expectConfirmation) {
- viewModel.confirmAuthenticated()
+ if ((testCase.isCoex && !forceExplicitFlow) || testCase.isFaceOnly) {
+ // Face-only or implicit co-ex auth
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_idle_static)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(shouldAnimateIconView).isEqualTo(false)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
}
- val confirmedHaptics by collectLastValue(viewModel.hapticsToPlay)
- assertThat(confirmedHaptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.CONFIRM)
- assertThat(confirmedHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
- }
-
- @Test
- fun playSuccessHaptic_SetsConfirmConstant() = runGenericTest {
- val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
- viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
-
- if (expectConfirmation) {
- viewModel.confirmAuthenticated()
- }
-
- val currentHaptics by collectLastValue(viewModel.hapticsToPlay)
- assertThat(currentHaptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.CONFIRM)
- assertThat(currentHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
- }
-
- @Test
- fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
- viewModel.showTemporaryError("test", "messageAfterError", false)
-
- val currentHaptics by collectLastValue(viewModel.hapticsToPlay)
- assertThat(currentHaptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
- assertThat(currentHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
- }
-
- @Test
- fun start_idle_and_show_authenticating_iconUpdate() =
- runGenericTest(doNotStart = true) {
- val currentRotation by collectLastValue(displayStateInteractor.currentRotation)
- val iconAsset by collectLastValue(iconViewModel.iconAsset)
- val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
- val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
-
- val forceExplicitFlow = testCase.isCoex && testCase.authenticatedByFingerprint
if (forceExplicitFlow) {
- viewModel.ensureFingerprintHasStarted(isDelayed = true)
+ expectedPromptSize = PromptSize.MEDIUM
+ kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true)
}
val startMessage = "here we go"
- viewModel.showAuthenticating(startMessage, isRetry = false)
-
- if (testCase.isFingerprintOnly) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by
- collectLastValue(iconViewModel.shouldAnimateIconOverlay)
+ kosmos.promptViewModel.showAuthenticating(startMessage, isRetry = false)
+ verifyIconSize(forceExplicitFlow)
+ // Icon asset assertions
+ if ((testCase.isCoex && !forceExplicitFlow) || testCase.isFaceOnly) {
+ // Face-only or implicit co-ex auth
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_authenticating)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) {
+ // Fingerprint-only or explicit co-ex auth
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val expectedOverlayAsset =
- when (currentRotation) {
- DisplayRotation.ROTATION_0 ->
- R.raw.biometricprompt_fingerprint_to_error_landscape
- DisplayRotation.ROTATION_90 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
- DisplayRotation.ROTATION_180 ->
- R.raw.biometricprompt_fingerprint_to_error_landscape
- DisplayRotation.ROTATION_270 ->
- R.raw
- .biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
- else -> throw Exception("invalid rotation")
- }
- assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconAsset).isEqualTo(getSfpsBaseIconAsset())
+ assertThat(iconOverlayAsset)
+ .isEqualTo(R.raw.biometricprompt_fingerprint_to_error_landscape)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
assertThat(shouldAnimateIconOverlay).isEqualTo(false)
} else {
assertThat(iconAsset)
@@ -406,85 +344,74 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
}
- if (testCase.isFaceOnly) {
- val expectedIconAsset = R.raw.face_dialog_authenticating
- assertThat(iconAsset).isEqualTo(expectedIconAsset)
- assertThat(iconContentDescriptionId)
- .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
- assertThat(shouldAnimateIconView).isEqualTo(true)
- }
-
- if (testCase.isCoex) {
- if (testCase.confirmationRequested || forceExplicitFlow) {
- // explicit flow
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by
- collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
- // TODO: Update when SFPS co-ex is implemented
- if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- assertThat(iconAsset)
- .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie)
- assertThat(iconOverlayAsset).isEqualTo(-1)
- assertThat(iconContentDescriptionId)
- .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
- assertThat(shouldAnimateIconView).isEqualTo(false)
- assertThat(shouldAnimateIconOverlay).isEqualTo(false)
- }
- } else {
- // implicit flow
- val expectedIconAsset = R.raw.face_dialog_authenticating
- assertThat(iconAsset).isEqualTo(expectedIconAsset)
- assertThat(iconContentDescriptionId)
- .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
- assertThat(shouldAnimateIconView).isEqualTo(true)
- }
- }
+ assertThat(message).isEqualTo(PromptMessage.Help(startMessage))
+ assertThat(authenticating).isTrue()
+ assertThat(authenticated?.isNotAuthenticated).isTrue()
+ assertThat(size).isEqualTo(expectedPromptSize)
+ assertButtonsVisible(negative = expectedPromptSize != PromptSize.SMALL)
}
@Test
- fun start_authenticating_show_and_clear_error_iconUpdate() = runGenericTest {
- val currentRotation by collectLastValue(displayStateInteractor.currentRotation)
-
- val iconAsset by collectLastValue(iconViewModel.iconAsset)
- val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
- val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
-
- val forceExplicitFlow = testCase.isCoex && testCase.authenticatedByFingerprint
+ fun start_authenticating_show_and_clear_error() = runGenericTest {
+ val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+ val iconOverlayAsset by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.iconOverlayAsset)
+ val iconContentDescriptionId by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView)
+ val shouldAnimateIconOverlay by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconOverlay)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+
+ var forceExplicitFlow =
+ testCase.isCoex && testCase.confirmationRequested || testCase.authenticatedByFingerprint
if (forceExplicitFlow) {
- viewModel.ensureFingerprintHasStarted(isDelayed = true)
+ kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true)
}
+ verifyIconSize(forceExplicitFlow)
val errorJob = launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
"so sad",
messageAfterError = "",
authenticateAfterError = testCase.isFingerprintOnly || testCase.isCoex,
)
+ forceExplicitFlow = true
// Usually done by binder
- iconViewModel.setPreviousIconWasError(true)
- iconViewModel.setPreviousIconOverlayWasError(true)
+ kosmos.promptViewModel.iconViewModel.setPreviousIconWasError(true)
+ kosmos.promptViewModel.iconViewModel.setPreviousIconOverlayWasError(true)
}
- if (testCase.isFingerprintOnly) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by collectLastValue(iconViewModel.shouldAnimateIconOverlay)
+ assertThat(message?.isError).isEqualTo(true)
+ assertThat(message?.message).isEqualTo("so sad")
+
+ // Icon asset assertions
+ if (testCase.isFaceOnly) {
+ // Face-only auth
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_error)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId).isEqualTo(R.string.keyguard_face_failed)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+
+ // Clear error, go to idle
+ errorJob.join()
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_error_to_idle)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.biometric_dialog_face_icon_description_idle)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) {
+ // Fingerprint-only or explicit co-ex auth
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val expectedOverlayAsset =
- when (currentRotation) {
- DisplayRotation.ROTATION_0 ->
- R.raw.biometricprompt_fingerprint_to_error_landscape
- DisplayRotation.ROTATION_90 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
- DisplayRotation.ROTATION_180 ->
- R.raw.biometricprompt_fingerprint_to_error_landscape
- DisplayRotation.ROTATION_270 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
- else -> throw Exception("invalid rotation")
- }
- assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconAsset).isEqualTo(getSfpsBaseIconAsset())
+ assertThat(iconOverlayAsset)
+ .isEqualTo(R.raw.biometricprompt_fingerprint_to_error_landscape)
assertThat(iconContentDescriptionId).isEqualTo(R.string.biometric_dialog_try_again)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
assertThat(shouldAnimateIconOverlay).isEqualTo(true)
} else {
assertThat(iconAsset)
@@ -499,19 +426,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
errorJob.join()
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val expectedOverlayAsset =
- when (currentRotation) {
- DisplayRotation.ROTATION_0 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
- DisplayRotation.ROTATION_90 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft
- DisplayRotation.ROTATION_180 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
- DisplayRotation.ROTATION_270 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright
- else -> throw Exception("invalid rotation")
- }
- assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconAsset).isEqualTo(getSfpsBaseIconAsset())
+ assertThat(iconOverlayAsset)
+ .isEqualTo(R.raw.biometricprompt_symbol_error_to_fingerprint_landscape)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
assertThat(shouldAnimateIconOverlay).isEqualTo(true)
@@ -525,89 +442,48 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(shouldAnimateIconOverlay).isEqualTo(false)
}
}
+ }
- if (testCase.isFaceOnly) {
- assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_error)
- assertThat(iconContentDescriptionId).isEqualTo(R.string.keyguard_face_failed)
- assertThat(shouldAnimateIconView).isEqualTo(true)
-
- // Clear error, go to idle
- errorJob.join()
-
- assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_error_to_idle)
- assertThat(iconContentDescriptionId)
- .isEqualTo(R.string.biometric_dialog_face_icon_description_idle)
- assertThat(shouldAnimateIconView).isEqualTo(true)
- }
-
- if (testCase.isCoex) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
- // TODO: Update when SFPS co-ex is implemented
- if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- assertThat(iconAsset)
- .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie)
- assertThat(iconOverlayAsset).isEqualTo(-1)
- assertThat(iconContentDescriptionId).isEqualTo(R.string.biometric_dialog_try_again)
- assertThat(shouldAnimateIconView).isEqualTo(true)
- assertThat(shouldAnimateIconOverlay).isEqualTo(false)
- }
-
- // Clear error, restart authenticating
- errorJob.join()
-
- if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- assertThat(iconAsset)
- .isEqualTo(R.raw.fingerprint_dialogue_error_to_fingerprint_lottie)
- assertThat(iconOverlayAsset).isEqualTo(-1)
- assertThat(iconContentDescriptionId)
- .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
- assertThat(shouldAnimateIconView).isEqualTo(true)
- assertThat(shouldAnimateIconOverlay).isEqualTo(false)
- }
+ private fun getSfpsBaseIconAsset(): Int {
+ return if (testCase.isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_landscape_base
+ } else if (testCase.isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_default
+ } else {
+ R.raw.biometricprompt_landscape_base
}
}
@Test
- fun shows_authenticated_no_errors_no_confirmation_required_iconUpdate() = runGenericTest {
+ fun shows_authenticated_no_errors_no_confirmation_required() = runGenericTest {
if (!testCase.confirmationRequested) {
- val currentRotation by collectLastValue(displayStateInteractor.currentRotation)
-
- val iconAsset by collectLastValue(iconViewModel.iconAsset)
- val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
- val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
-
- viewModel.showAuthenticated(
+ val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+ val iconOverlayAsset by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.iconOverlayAsset)
+ val iconContentDescriptionId by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView)
+ val shouldAnimateIconOverlay by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconOverlay)
+ verifyIconSize()
+
+ kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
dismissAfterDelay = DELAY
)
if (testCase.isFingerprintOnly) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by
- collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
+ // Fingerprint icon asset assertions
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val expectedOverlayAsset =
- when (currentRotation) {
- DisplayRotation.ROTATION_0 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
- DisplayRotation.ROTATION_90 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
- DisplayRotation.ROTATION_180 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
- DisplayRotation.ROTATION_270 ->
- R.raw
- .biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
- else -> throw Exception("invalid rotation")
- }
- assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconAsset).isEqualTo(getSfpsBaseIconAsset())
+ assertThat(iconOverlayAsset)
+ .isEqualTo(R.raw.biometricprompt_symbol_fingerprint_to_success_landscape)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
assertThat(shouldAnimateIconOverlay).isEqualTo(true)
} else {
- val isAuthenticated by collectLastValue(viewModel.isAuthenticated)
assertThat(iconAsset)
.isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_success_lottie)
assertThat(iconOverlayAsset).isEqualTo(-1)
@@ -616,47 +492,52 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(shouldAnimateIconView).isEqualTo(true)
assertThat(shouldAnimateIconOverlay).isEqualTo(false)
}
- }
-
- // If co-ex, using implicit flow (explicit flow always requires confirmation)
- if (testCase.isFaceOnly || testCase.isCoex) {
- assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_checkmark)
+ } else if (testCase.isFaceOnly || testCase.isCoex) {
+ // Face icon asset assertions
+ // If co-ex, use implicit flow (explicit flow always requires confirmation)
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_checkmark)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated)
assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
}
}
}
@Test
- fun shows_pending_confirmation_iconUpdate() = runGenericTest {
+ fun shows_pending_confirmation() = runGenericTest {
if (
(testCase.isFaceOnly || testCase.isCoex) &&
testCase.authenticatedByFace &&
testCase.confirmationRequested
) {
- val iconAsset by collectLastValue(iconViewModel.iconAsset)
- val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
- val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
-
- viewModel.showAuthenticated(
+ val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+ val iconOverlayAsset by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.iconOverlayAsset)
+ val iconContentDescriptionId by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView)
+ val shouldAnimateIconOverlay by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconOverlay)
+
+ val forceExplicitFlow = testCase.isCoex && testCase.confirmationRequested
+ verifyIconSize(forceExplicitFlow = forceExplicitFlow)
+
+ kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
dismissAfterDelay = DELAY
)
if (testCase.isFaceOnly) {
- assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_wink_from_dark)
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_wink_from_dark)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated)
assertThat(shouldAnimateIconView).isEqualTo(true)
- }
-
- // explicit flow because confirmation requested
- if (testCase.isCoex) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by
- collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ } else if (testCase.isCoex) { // explicit flow, confirmation requested
// TODO: Update when SFPS co-ex is implemented
if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
assertThat(iconAsset)
@@ -678,30 +559,36 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
testCase.authenticatedByFace &&
testCase.confirmationRequested
) {
- val iconAsset by collectLastValue(iconViewModel.iconAsset)
- val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
- val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
-
- viewModel.showAuthenticated(
+ val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+ val iconOverlayAsset by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.iconOverlayAsset)
+ val iconContentDescriptionId by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView)
+ val shouldAnimateIconOverlay by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconOverlay)
+ val forceExplicitFlow = testCase.isCoex && testCase.confirmationRequested
+ verifyIconSize(forceExplicitFlow = forceExplicitFlow)
+
+ kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
dismissAfterDelay = DELAY
)
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
if (testCase.isFaceOnly) {
- assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_checkmark)
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_checkmark)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.biometric_dialog_face_icon_description_confirmed)
assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
}
// explicit flow because confirmation requested
if (testCase.isCoex) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by
- collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
// TODO: Update when SFPS co-ex is implemented
if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
assertThat(iconAsset)
@@ -717,19 +604,119 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- fun sfpsIconUpdates_onConfigurationChanged() = runGenericTest {
- if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val testConfig = Configuration()
- val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
- val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
- val currentIcon by collectLastValue(iconViewModel.iconAsset)
+ fun shows_authenticated_with_no_errors() = runGenericTest {
+ // this case can't happen until fingerprint is started
+ // trigger it now since no error has occurred in this test
+ val forceError = testCase.isCoex && testCase.authenticatedByFingerprint
+
+ if (forceError) {
+ assertThat(kosmos.promptViewModel.fingerprintStartMode.first())
+ .isEqualTo(FingerprintStartMode.Pending)
+ kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true)
+ }
+
+ showAuthenticated(
+ testCase.authenticatedModality,
+ testCase.expectConfirmation(atLeastOneFailure = forceError),
+ )
+ }
+
+ // Verifies expected icon sizes for all modalities
+ private fun TestScope.verifyIconSize(forceExplicitFlow: Boolean = false) {
+ val iconSize by collectLastValue(kosmos.promptViewModel.iconSize)
+ if ((testCase.isCoex && !forceExplicitFlow) || testCase.isFaceOnly) {
+ // Face-only or implicit co-ex auth
+ assertThat(iconSize).isEqualTo(Pair(mockFaceIconSize, mockFaceIconSize))
+ } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) {
+ // Fingerprint-only or explicit co-ex auth
+ if (testCase.fingerprint?.isAnyUdfpsType == true) {
+ val udfpsOverlayParams by
+ collectLastValue(kosmos.promptViewModel.udfpsOverlayParams)
+ val expectedUdfpsOverlayParams = mockUdfpsOverlayParams()
+ assertThat(udfpsOverlayParams).isEqualTo(expectedUdfpsOverlayParams)
+
+ assertThat(iconSize)
+ .isEqualTo(
+ Pair(
+ expectedUdfpsOverlayParams.sensorBounds.width(),
+ expectedUdfpsOverlayParams.sensorBounds.height()
+ )
+ )
+ } else {
+ assertThat(iconSize)
+ .isEqualTo(Pair(mockFingerprintIconWidth, mockFingerprintIconHeight))
+ }
+ }
+ }
+
+ @Test
+ fun set_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
+ runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
- testConfig.smallestScreenWidthDp = folded
- iconViewModel.onConfigurationChanged(testConfig)
+ val confirmHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(confirmHaptics?.hapticFeedbackConstant)
+ .isEqualTo(
+ if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
+ else HapticFeedbackConstants.CONFIRM
+ )
+ assertThat(confirmHaptics?.flag)
+ .isEqualTo(
+ if (expectConfirmation) null
+ else HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
+ )
+
+ if (expectConfirmation) {
+ kosmos.promptViewModel.confirmAuthenticated()
+ }
+
+ val confirmedHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(confirmedHaptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(confirmedHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ }
+
+ @Test
+ fun playSuccessHaptic_SetsConfirmConstant() = runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
+
+ if (expectConfirmation) {
+ kosmos.promptViewModel.confirmAuthenticated()
+ }
+
+ val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(currentHaptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(currentHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ }
+
+ @Test
+ fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
+ kosmos.promptViewModel.showTemporaryError("test", "messageAfterError", false)
+
+ val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(currentHaptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
+ assertThat(currentHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ }
+
+ @Test
+ fun sfpsIconUpdates_onFoldConfigurationChanged() = runGenericTest {
+ if (
+ testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON &&
+ !testCase.isInRearDisplayMode
+ ) {
+ val currentIcon by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+
+ kosmos.promptViewModel.iconViewModel.onConfigurationChanged(getFoldedConfiguration())
val foldedIcon = currentIcon
- testConfig.smallestScreenWidthDp = unfolded
- iconViewModel.onConfigurationChanged(testConfig)
+ kosmos.promptViewModel.iconViewModel.onConfigurationChanged(getUnfoldedConfiguration())
val unfoldedIcon = currentIcon
assertThat(foldedIcon).isNotEqualTo(unfoldedIcon)
@@ -739,18 +726,18 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun sfpsIconUpdates_onRotation() = runGenericTest {
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val currentIcon by collectLastValue(iconViewModel.iconAsset)
+ val currentIcon by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
val iconRotation0 = currentIcon
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
val iconRotation90 = currentIcon
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
val iconRotation180 = currentIcon
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
val iconRotation270 = currentIcon
assertThat(iconRotation0).isEqualTo(iconRotation180)
@@ -762,12 +749,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun sfpsIconUpdates_onRearDisplayMode() = runGenericTest {
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val currentIcon by collectLastValue(iconViewModel.iconAsset)
+ val currentIcon by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
- displayStateRepository.setIsInRearDisplayMode(false)
+ kosmos.displayStateRepository.setIsInRearDisplayMode(false)
val iconNotRearDisplayMode = currentIcon
- displayStateRepository.setIsInRearDisplayMode(true)
+ kosmos.displayStateRepository.setIsInRearDisplayMode(true)
val iconRearDisplayMode = currentIcon
assertThat(iconNotRearDisplayMode).isNotEqualTo(iconRearDisplayMode)
@@ -778,10 +765,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
authenticatedModality: BiometricModality,
expectConfirmation: Boolean,
) {
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val fpStartMode by collectLastValue(viewModel.fingerprintStartMode)
- val size by collectLastValue(viewModel.size)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val fpStartMode by collectLastValue(kosmos.promptViewModel.fingerprintStartMode)
+ val size by collectLastValue(kosmos.promptViewModel.size)
val authWithSmallPrompt =
testCase.shouldStartAsImplicitFlow &&
@@ -791,7 +778,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(size).isEqualTo(if (authWithSmallPrompt) PromptSize.SMALL else PromptSize.MEDIUM)
assertButtonsVisible(negative = !authWithSmallPrompt)
- viewModel.showAuthenticated(authenticatedModality, DELAY)
+ kosmos.promptViewModel.showAuthenticated(authenticatedModality, DELAY)
assertThat(authenticated?.isAuthenticated).isTrue()
assertThat(authenticated?.delay).isEqualTo(DELAY)
@@ -822,50 +809,49 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun set_haptic_on_errors() = runGenericTest {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
"so sad",
messageAfterError = "",
authenticateAfterError = false,
hapticFeedback = true,
)
- val haptics by collectLastValue(viewModel.hapticsToPlay)
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
}
@Test
fun plays_haptic_on_errors_unless_skipped() = runGenericTest {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
"still sad",
messageAfterError = "",
authenticateAfterError = false,
hapticFeedback = false,
)
- val haptics by collectLastValue(viewModel.hapticsToPlay)
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
}
@Test
fun plays_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
"still sad",
messageAfterError = "",
authenticateAfterError = false,
hapticFeedback = true,
)
- val haptics by collectLastValue(viewModel.hapticsToPlay)
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
if (expectConfirmation) {
assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
} else {
- assertThat(haptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
}
}
@@ -875,15 +861,15 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
block: suspend TestScope.() -> Unit = {},
) {
val errorMessage = "oh no!"
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
- val size by collectLastValue(viewModel.size)
- val canTryAgainNow by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val canTryAgainNow by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
val errorJob = launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
errorMessage,
authenticateAfterError = restart,
messageAfterError = helpAfterError,
@@ -913,13 +899,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun no_errors_or_temporary_help_after_authenticated() = runGenericTest {
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val messageIsShowing by collectLastValue(viewModel.isIndicatorMessageVisible)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val messageIsShowing by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
val verifyNoError = {
assertThat(authenticating).isFalse()
@@ -929,7 +915,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
val errorJob = launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
"error",
messageAfterError = "",
authenticateAfterError = false,
@@ -939,14 +925,14 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
errorJob.join()
verifyNoError()
- val helpJob = launch { viewModel.showTemporaryHelp("hi") }
+ val helpJob = launch { kosmos.promptViewModel.showTemporaryHelp("hi") }
verifyNoError()
helpJob.join()
verifyNoError()
// persistent help is allowed
val stickyHelpMessage = "blah"
- viewModel.showHelp(stickyHelpMessage)
+ kosmos.promptViewModel.showHelp(stickyHelpMessage)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
assertThat(message).isEqualTo(PromptMessage.Help(stickyHelpMessage))
@@ -955,11 +941,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun suppress_temporary_error() = runGenericTest {
- val messages by collectValues(viewModel.message)
+ val messages by collectValues(kosmos.promptViewModel.message)
for (error in listOf("never", "see", "me")) {
launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
error,
messageAfterError = "or me",
authenticateAfterError = false,
@@ -984,11 +970,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
val errors = listOf("woot", "oh yeah", "nope")
val afterSuffix = "(after)"
val expectedErrorMessage = if (suppress) errors.first() else errors.last()
- val messages by collectValues(viewModel.message)
+ val messages by collectValues(kosmos.promptViewModel.message)
for (error in errors) {
launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
error,
messageAfterError = "$error $afterSuffix",
authenticateAfterError = false,
@@ -1017,15 +1003,15 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun authenticated_at_most_once_same_modality() = runGenericTest {
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
@@ -1033,15 +1019,15 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun authenticating_cannot_restart_after_authenticated() = runGenericTest {
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
- viewModel.showAuthenticating("again!")
+ kosmos.promptViewModel.showAuthenticating("again!")
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
@@ -1051,13 +1037,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
fun confirm_authentication() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val size by collectLastValue(viewModel.size)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
if (expectConfirmation) {
@@ -1067,7 +1053,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
confirm = true,
)
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
assertThat(message).isEqualTo(PromptMessage.Empty)
assertButtonsVisible()
}
@@ -1081,13 +1067,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
fun second_authentication_acts_as_confirmation() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val size by collectLastValue(viewModel.size)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
if (expectConfirmation) {
@@ -1098,7 +1084,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
)
if (testCase.modalities.hasSfps) {
- viewModel.showAuthenticated(BiometricModality.Fingerprint, 0)
+ kosmos.promptViewModel.showAuthenticated(BiometricModality.Fingerprint, 0)
assertThat(message).isEqualTo(PromptMessage.Empty)
assertButtonsVisible()
}
@@ -1114,15 +1100,15 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
if (testCase.isCoex) {
- viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
+ kosmos.promptViewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
}
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val size by collectLastValue(viewModel.size)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
assertThat(authenticating).isFalse()
assertThat(canTryAgain).isFalse()
@@ -1136,7 +1122,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
confirm = true,
)
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
} else if (testCase.isCoex) {
assertThat(authenticated?.isAuthenticatedAndConfirmed).isTrue()
}
@@ -1150,16 +1136,16 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
if (testCase.isCoex) {
- viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
- viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_UP))
+ kosmos.promptViewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
+ kosmos.promptViewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_UP))
}
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val size by collectLastValue(viewModel.size)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
if (expectConfirmation) {
@@ -1169,7 +1155,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
confirm = true,
)
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
assertThat(message).isEqualTo(PromptMessage.Empty)
assertButtonsVisible()
}
@@ -1181,18 +1167,18 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun cannot_confirm_unless_authenticated() = runGenericTest {
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
assertThat(authenticating).isTrue()
assertThat(authenticated?.isNotAuthenticated).isTrue()
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
// reconfirm should be a no-op
- viewModel.confirmAuthenticated()
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
assertThat(authenticating).isFalse()
assertThat(authenticated?.isNotAuthenticated).isFalse()
@@ -1201,36 +1187,36 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun shows_help_before_authenticated() = runGenericTest {
val helpMessage = "please help yourself to some cookies"
- val message by collectLastValue(viewModel.message)
- val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
- val size by collectLastValue(viewModel.size)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible)
+ val size by collectLastValue(kosmos.promptViewModel.size)
- viewModel.showHelp(helpMessage)
+ kosmos.promptViewModel.showHelp(helpMessage)
assertThat(size).isEqualTo(PromptSize.MEDIUM)
assertThat(message).isEqualTo(PromptMessage.Help(helpMessage))
assertThat(messageVisible).isTrue()
- assertThat(viewModel.isAuthenticating.first()).isFalse()
- assertThat(viewModel.isAuthenticated.first().isNotAuthenticated).isTrue()
+ assertThat(kosmos.promptViewModel.isAuthenticating.first()).isFalse()
+ assertThat(kosmos.promptViewModel.isAuthenticated.first().isNotAuthenticated).isTrue()
}
@Test
fun shows_help_after_authenticated() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
val helpMessage = "more cookies please"
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
- val size by collectLastValue(viewModel.size)
- val confirmationRequired by collectLastValue(viewModel.isConfirmationRequired)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val confirmationRequired by collectLastValue(kosmos.promptViewModel.isConfirmationRequired)
if (testCase.isCoex && testCase.authenticatedByFingerprint) {
- viewModel.ensureFingerprintHasStarted(isDelayed = true)
+ kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true)
}
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
- viewModel.showHelp(helpMessage)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showHelp(helpMessage)
assertThat(size).isEqualTo(PromptSize.MEDIUM)
@@ -1250,15 +1236,15 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
val errorMessage = "bad"
val helpMessage = "again?"
val expectTryAgainButton = testCase.isFaceOnly
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
- viewModel.showAuthenticating("go")
+ kosmos.promptViewModel.showAuthenticating("go")
val errorJob = launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
errorMessage,
messageAfterError = helpMessage,
authenticateAfterError = false,
@@ -1283,7 +1269,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertButtonsVisible(negative = true, tryAgain = expectTryAgainButton)
val helpMessage2 = "foo"
- viewModel.showAuthenticating(helpMessage2, isRetry = true)
+ kosmos.promptViewModel.showAuthenticating(helpMessage2, isRetry = true)
assertThat(authenticating).isTrue()
assertThat(authenticated?.isAuthenticated).isFalse()
assertThat(message).isEqualTo(PromptMessage.Help(helpMessage2))
@@ -1293,10 +1279,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun switch_to_credential_fallback() = runGenericTest {
- val size by collectLastValue(viewModel.size)
+ val size by collectLastValue(kosmos.promptViewModel.size)
// TODO(b/251476085): remove Spaghetti, migrate logic, and update this test
- viewModel.onSwitchToCredential()
+ kosmos.promptViewModel.onSwitchToCredential()
assertThat(size).isEqualTo(PromptSize.LARGE)
}
@@ -1304,15 +1290,15 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
@EnableFlags(FLAG_BP_TALKBACK)
fun hint_for_talkback_guidance() = runGenericTest {
- val hint by collectLastValue(viewModel.accessibilityHint)
+ val hint by collectLastValue(kosmos.promptViewModel.accessibilityHint)
// Touches should fall outside of sensor area
- whenever(udfpsUtils.getTouchInNativeCoordinates(any(), any(), any()))
+ whenever(kosmos.udfpsUtils.getTouchInNativeCoordinates(any(), any(), any()))
.thenReturn(Point(0, 0))
- whenever(udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
+ whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
.thenReturn("Direction")
- viewModel.onAnnounceAccessibilityHint(
+ kosmos.promptViewModel.onAnnounceAccessibilityHint(
obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
true
)
@@ -1328,8 +1314,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun descriptionOverriddenByVerticalListContentView() =
runGenericTest(description = "test description", contentView = promptContentView) {
- val contentView by collectLastValue(viewModel.contentView)
- val description by collectLastValue(viewModel.description)
+ val contentView by collectLastValue(kosmos.promptViewModel.contentView)
+ val description by collectLastValue(kosmos.promptViewModel.description)
assertThat(description).isEqualTo("")
assertThat(contentView).isEqualTo(promptContentView)
@@ -1342,8 +1328,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
description = "test description",
contentView = promptContentViewWithMoreOptionsButton
) {
- val contentView by collectLastValue(viewModel.contentView)
- val description by collectLastValue(viewModel.description)
+ val contentView by collectLastValue(kosmos.promptViewModel.contentView)
+ val description by collectLastValue(kosmos.promptViewModel.description)
assertThat(description).isEqualTo("")
assertThat(contentView).isEqualTo(promptContentViewWithMoreOptionsButton)
@@ -1353,8 +1339,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun descriptionWithoutContentView() =
runGenericTest(description = "test description") {
- val contentView by collectLastValue(viewModel.contentView)
- val description by collectLastValue(viewModel.description)
+ val contentView by collectLastValue(kosmos.promptViewModel.contentView)
+ val description by collectLastValue(kosmos.promptViewModel.description)
assertThat(description).isEqualTo("test description")
assertThat(contentView).isNull()
@@ -1364,7 +1350,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_nullIfPkgNameNotFound() =
runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
assertThat(logo).isNull()
}
@@ -1372,7 +1358,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_defaultWithOverrides() =
runGenericTest(packageName = packageNameForLogoWithOverrides) {
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
// 1. PM.getApplicationInfo(packageNameForLogoWithOverrides) is set to return
// applicationInfoWithIcon with defaultLogoIcon,
@@ -1385,14 +1371,14 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_defaultIsNull() =
runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
assertThat(logo).isNull()
}
@Test
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_default() = runGenericTest {
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
assertThat(logo).isEqualTo(defaultLogoIcon)
}
@@ -1401,7 +1387,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
fun logo_resSetByApp() =
runGenericTest(logoRes = logoResFromApp) {
val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
assertThat((logo as BitmapDrawable).bitmap.sameAs(expectedBitmap)).isTrue()
}
@@ -1409,7 +1395,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_bitmapSetByApp() =
runGenericTest(logoBitmap = logoBitmapFromApp) {
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
assertThat((logo as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp)
}
@@ -1417,7 +1403,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_emptyIfPkgNameNotFound() =
runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
- val logoDescription by collectLastValue(viewModel.logoDescription)
+ val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
assertThat(logoDescription).isEqualTo("")
}
@@ -1425,14 +1411,14 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_defaultIsEmpty() =
runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
- val logoDescription by collectLastValue(viewModel.logoDescription)
+ val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
assertThat(logoDescription).isEqualTo("")
}
@Test
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_default() = runGenericTest {
- val logoDescription by collectLastValue(viewModel.logoDescription)
+ val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
assertThat(logoDescription).isEqualTo(defaultLogoDescription)
}
@@ -1440,57 +1426,57 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_setByApp() =
runGenericTest(logoDescription = logoDescriptionFromApp) {
- val logoDescription by collectLastValue(viewModel.logoDescription)
+ val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
assertThat(logoDescription).isEqualTo(logoDescriptionFromApp)
}
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_bottom_rotation0() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ val position by collectLastValue(kosmos.promptViewModel.position)
assertThat(position).isEqualTo(PromptPosition.Bottom)
} // TODO(b/335278136): Add test for no sensor landscape
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_bottom_forceLarge() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
- viewModel.onSwitchToCredential()
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ kosmos.promptViewModel.onSwitchToCredential()
+ val position by collectLastValue(kosmos.promptViewModel.position)
assertThat(position).isEqualTo(PromptPosition.Bottom)
}
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_bottom_largeScreen() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
- displayStateRepository.setIsLargeScreen(true)
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ kosmos.displayStateRepository.setIsLargeScreen(true)
+ val position by collectLastValue(kosmos.promptViewModel.position)
assertThat(position).isEqualTo(PromptPosition.Bottom)
}
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_right_rotation90() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ val position by collectLastValue(kosmos.promptViewModel.position)
assertThat(position).isEqualTo(PromptPosition.Right)
}
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_left_rotation270() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ val position by collectLastValue(kosmos.promptViewModel.position)
assertThat(position).isEqualTo(PromptPosition.Left)
}
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_top_rotation180() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ val position by collectLastValue(kosmos.promptViewModel.position)
if (testCase.modalities.hasUdfps) {
assertThat(position).isEqualTo(PromptPosition.Top)
} else {
@@ -1501,18 +1487,18 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_bottom() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
assertThat(guidelineBounds).isEqualTo(Rect(0, mediumTopGuidelinePadding, 0, 0))
} // TODO(b/335278136): Add test for no sensor landscape
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_right() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
val isSmall = testCase.shouldStartAsImplicitFlow
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
if (isSmall) {
assertThat(guidelineBounds).isEqualTo(Rect(-smallHorizontalGuidelinePadding, 0, 0, 0))
@@ -1527,10 +1513,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_right_onlyShortTitle() =
runGenericTest(subtitle = "") {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
val isSmall = testCase.shouldStartAsImplicitFlow
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
if (!isSmall && testCase.modalities.hasUdfps) {
assertThat(guidelineBounds)
@@ -1541,10 +1527,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_left() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
val isSmall = testCase.shouldStartAsImplicitFlow
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
if (isSmall) {
assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -smallHorizontalGuidelinePadding, 0))
@@ -1559,10 +1545,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_left_onlyShortTitle() =
runGenericTest(subtitle = "") {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
val isSmall = testCase.shouldStartAsImplicitFlow
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
if (!isSmall && testCase.modalities.hasUdfps) {
assertThat(guidelineBounds)
@@ -1573,8 +1559,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_top() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
if (testCase.modalities.hasUdfps) {
assertThat(guidelineBounds).isEqualTo(Rect(0, 0, 0, 0))
}
@@ -1582,11 +1568,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun iconViewLoaded() = runGenericTest {
- val isIconViewLoaded by collectLastValue(viewModel.isIconViewLoaded)
+ val isIconViewLoaded by collectLastValue(kosmos.promptViewModel.isIconViewLoaded)
// TODO(b/328677869): Add test for noIcon logic.
assertThat(isIconViewLoaded).isFalse()
- viewModel.setIsIconViewLoaded(true)
+ kosmos.promptViewModel.setIsIconViewLoaded(true)
assertThat(isIconViewLoaded).isTrue()
}
@@ -1600,11 +1586,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
credential: Boolean = false,
) {
runCurrent()
- assertThat(viewModel.isTryAgainButtonVisible.first()).isEqualTo(tryAgain)
- assertThat(viewModel.isConfirmButtonVisible.first()).isEqualTo(confirm)
- assertThat(viewModel.isCancelButtonVisible.first()).isEqualTo(cancel)
- assertThat(viewModel.isNegativeButtonVisible.first()).isEqualTo(negative)
- assertThat(viewModel.isCredentialButtonVisible.first()).isEqualTo(credential)
+ assertThat(kosmos.promptViewModel.isTryAgainButtonVisible.first()).isEqualTo(tryAgain)
+ assertThat(kosmos.promptViewModel.isConfirmButtonVisible.first()).isEqualTo(confirm)
+ assertThat(kosmos.promptViewModel.isCancelButtonVisible.first()).isEqualTo(cancel)
+ assertThat(kosmos.promptViewModel.isNegativeButtonVisible.first()).isEqualTo(negative)
+ assertThat(kosmos.promptViewModel.isCredentialButtonVisible.first()).isEqualTo(credential)
}
private fun runGenericTest(
@@ -1621,30 +1607,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
) {
val topActivity = ComponentName(packageName, "test app")
runningTaskInfo.topActivity = topActivity
- whenever(activityTaskManager.getTasks(1)).thenReturn(listOf(runningTaskInfo))
- selector =
- PromptSelectorInteractorImpl(
- fingerprintRepository,
- displayStateInteractor,
- promptRepository,
- lockPatternUtils
- )
- selector.resetPrompt(REQUEST_ID)
-
- viewModel =
- PromptViewModel(
- displayStateInteractor,
- selector,
- mContext,
- udfpsOverlayInteractor,
- biometricStatusInteractor,
- udfpsUtils,
- iconProvider,
- activityTaskManager
- )
- iconViewModel = viewModel.iconViewModel
+ whenever(kosmos.activityTaskManager.getTasks(1)).thenReturn(listOf(runningTaskInfo))
+ kosmos.promptSelectorInteractor.resetPrompt(REQUEST_ID)
- selector.initializePrompt(
+ kosmos.promptSelectorInteractor.initializePrompt(
requireConfirmation = testCase.confirmationRequested,
allowCredentialFallback = allowCredentialFallback,
fingerprint = testCase.fingerprint,
@@ -1658,12 +1624,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
packageName = packageName,
)
- biometricStatusRepository.setFingerprintAcquiredStatus(
+ kosmos.biometricStatusRepository.setFingerprintAcquiredStatus(
AcquiredFingerprintAuthenticationStatus(
AuthenticationReason.BiometricPromptAuthentication,
BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN
)
)
+
// put the view model in the initial authenticating state, unless explicitly skipped
val startMode =
when {
@@ -1673,18 +1640,31 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
when (startMode) {
FingerprintStartMode.Normal -> {
- viewModel.ensureFingerprintHasStarted(isDelayed = false)
- viewModel.showAuthenticating()
+ kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = false)
+ kosmos.promptViewModel.showAuthenticating()
}
FingerprintStartMode.Delayed -> {
- viewModel.showAuthenticating()
+ kosmos.promptViewModel.showAuthenticating()
}
else -> {
/* skip */
}
}
- testScope.runTest { block() }
+ if (testCase.fingerprint?.isAnyUdfpsType == true) {
+ kosmos.testScope.collectLastValue(kosmos.udfpsOverlayInteractor.udfpsOverlayParams)
+ kosmos.testScope.runCurrent()
+ overrideUdfpsOverlayParams()
+ }
+
+ kosmos.testScope.runTest { block() }
+ }
+
+ private fun overrideUdfpsOverlayParams(isLandscape: Boolean = false) {
+ val authControllerCallback = authController.captureCallback()
+ authControllerCallback.onUdfpsLocationChanged(
+ mockUdfpsOverlayParams(isLandscape = isLandscape)
+ )
}
/** Obtain a MotionEvent with the specified MotionEvent action constant */
@@ -1705,11 +1685,43 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
TestCase(
fingerprint =
fingerprintSensorPropertiesInternal(
+ sensorType = FingerprintSensorProperties.TYPE_REAR
+ )
+ .first(),
+ authenticatedModality = BiometricModality.Fingerprint,
+ ),
+ TestCase(
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
+ strong = true,
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ )
+ .first(),
+ authenticatedModality = BiometricModality.Fingerprint,
+ isInRearDisplayMode = false,
+ isDeviceFolded = false
+ ),
+ TestCase(
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
+ strong = true,
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ )
+ .first(),
+ authenticatedModality = BiometricModality.Fingerprint,
+ isInRearDisplayMode = false,
+ isDeviceFolded = true
+ ),
+ TestCase(
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
strong = true,
sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
)
.first(),
authenticatedModality = BiometricModality.Fingerprint,
+ isInRearDisplayMode = true,
+ isDeviceFolded = false
),
TestCase(
fingerprint =
@@ -1783,6 +1795,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
internal data class TestCase(
val fingerprint: FingerprintSensorPropertiesInternal? = null,
val face: FaceSensorPropertiesInternal? = null,
+ val isInRearDisplayMode: Boolean = false,
+ val isDeviceFolded: Boolean = false,
val authenticatedModality: BiometricModality,
val confirmationRequested: Boolean = false,
) {
@@ -1796,7 +1810,9 @@ internal data class TestCase(
face != null -> "face only"
else -> "?"
}
- return "[$modality, by: $authenticatedModality, confirm: $confirmationRequested]"
+ return "[$modality, isInRearDisplayMode: $isInRearDisplayMode, " +
+ "isDeviceFolded: $isDeviceFolded, by: $authenticatedModality, " +
+ "confirm: $confirmationRequested]"
}
fun expectConfirmation(atLeastOneFailure: Boolean): Boolean =
@@ -1872,4 +1888,25 @@ private fun PromptSelectorInteractor.initializePrompt(
)
}
+private fun AuthController.captureCallback() =
+ withArgCaptor<AuthController.Callback> {
+ Mockito.verify(this@captureCallback).addCallback(capture())
+ }
+
+/** Get folded device configuration */
+fun getFoldedConfiguration(): Configuration {
+ val testConfig = Configuration()
+ val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
+ testConfig.smallestScreenWidthDp = folded
+ return testConfig
+}
+
+/** Get unfolded device configuration */
+fun getUnfoldedConfiguration(): Configuration {
+ val testConfig = Configuration()
+ val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
+ testConfig.smallestScreenWidthDp = unfolded
+ return testConfig
+}
+
internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt
new file mode 100644
index 000000000000..3caa8f6cd138
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import android.hardware.input.fakeInputManager
+import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
+import com.android.systemui.keyboard.shortcut.shortcutHelperStateRepository
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShortcutHelperStateRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private val repo = kosmos.shortcutHelperStateRepository
+ private val helper = kosmos.shortcutHelperTestHelper
+ private val testScope = kosmos.testScope
+ private val fakeInputManager = kosmos.fakeInputManager
+
+ @Test
+ fun state_activeThroughToggle_emitsActiveWithDeviceIdFromEvent() =
+ testScope.runTest {
+ val deviceId = 123
+ val state by collectLastValue(repo.state)
+
+ helper.toggle(deviceId)
+
+ assertThat(state).isEqualTo(ShortcutHelperState.Active(deviceId))
+ }
+
+ @Test
+ fun state_activeThroughActivity_noKeyboardActive_emitsActiveWithVirtualDeviceId() =
+ testScope.runTest {
+ val state by collectLastValue(repo.state)
+
+ helper.showFromActivity()
+
+ assertThat(state).isEqualTo(ShortcutHelperState.Active(VIRTUAL_KEYBOARD))
+ }
+
+ @Test
+ fun state_activeThroughActivity_virtualKeyboardActive_emitsActiveWithVirtualDeviceId() =
+ testScope.runTest {
+ val state by collectLastValue(repo.state)
+
+ fakeInputManager.addVirtualKeyboard()
+ helper.showFromActivity()
+
+ assertThat(state).isEqualTo(ShortcutHelperState.Active(VIRTUAL_KEYBOARD))
+ }
+
+ @Test
+ fun state_activeThroughActivity_physicalKeyboardActive_emitsActiveWithDeviceId() =
+ testScope.runTest {
+ val deviceId = 456
+ val state by collectLastValue(repo.state)
+
+ fakeInputManager.addPhysicalKeyboard(deviceId)
+ helper.showFromActivity()
+
+ assertThat(state).isEqualTo(ShortcutHelperState.Active(deviceId))
+ }
+
+ @Test
+ fun state_activeThroughActivity_physicalKeyboardDisabled_emitsActiveWithVirtualDeviceId() =
+ testScope.runTest {
+ val deviceId = 456
+ val state by collectLastValue(repo.state)
+
+ fakeInputManager.addPhysicalKeyboard(deviceId)
+ fakeInputManager.inputManager.disableInputDevice(deviceId)
+ helper.showFromActivity()
+
+ assertThat(state).isEqualTo(ShortcutHelperState.Active(VIRTUAL_KEYBOARD))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
new file mode 100644
index 000000000000..9c9e48e9200e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesInteractor
+import com.android.systemui.keyboard.shortcut.shortcutHelperMultiTaskingShortcutsSource
+import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSource
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() {
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private val kosmos = testKosmos().also { it.testDispatcher = UnconfinedTestDispatcher() }
+ private val testScope = kosmos.testScope
+ private val interactor = kosmos.shortcutHelperCategoriesInteractor
+ private val helper = kosmos.shortcutHelperTestHelper
+ private val systemShortcutsSource = kosmos.shortcutHelperSystemShortcutsSource
+ private val multitaskingShortcutsSource = kosmos.shortcutHelperMultiTaskingShortcutsSource
+
+ @Test
+ fun categories_emptyByDefault() =
+ testScope.runTest {
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ assertThat(categories).isEmpty()
+ }
+
+ @Test
+ fun categories_stateActive_emitsAllCategoriesInOrder() =
+ testScope.runTest {
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ assertThat(categories)
+ .containsExactly(
+ systemShortcutsSource.systemShortcutsCategory(),
+ multitaskingShortcutsSource.multitaskingShortcutCategory()
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun categories_stateInactiveAfterActive_emitsEmpty() =
+ testScope.runTest {
+ val categories by collectLastValue(interactor.shortcutCategories)
+ helper.showFromActivity()
+ helper.hideFromActivity()
+
+ assertThat(categories).isEmpty()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index c974e0dde5e6..850e2e014c57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -16,8 +16,11 @@
package com.android.systemui.media.controls.domain.pipeline
+import android.R
import android.app.smartspace.SmartspaceAction
+import android.graphics.drawable.Icon
import android.os.Bundle
+import android.os.Process
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -36,7 +39,11 @@ import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
import com.android.systemui.media.controls.ui.controller.MediaPlayerData
import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.media.controls.util.MediaUiEventLogger
+import com.android.systemui.media.controls.util.SmallHash
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.testKosmos
@@ -101,7 +108,12 @@ class MediaDataFilterImplTest : SysuiTestCase() {
private lateinit var dataGuest: MediaData
private lateinit var dataPrivateProfile: MediaData
private val clock = FakeSystemClock()
- private val repository: MediaFilterRepository = kosmos.mediaFilterRepository
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
+ private val repository: MediaFilterRepository =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaFilterRepository
+ }
private val mediaLoadingLogger = kosmos.mockMediaLoadingLogger
@Before
@@ -146,6 +158,8 @@ class MediaDataFilterImplTest : SysuiTestCase() {
whenever(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE)
whenever(smartspaceData.recommendations)
.thenReturn(listOf(smartspaceMediaRecommendationItem))
+ whenever(smartspaceMediaRecommendationItem.icon)
+ .thenReturn(Icon.createWithResource(context, R.drawable.ic_media_play))
whenever(smartspaceData.headphoneConnectionTimeMillis)
.thenReturn(clock.currentTimeMillis() - 100)
whenever(smartspaceData.instanceId).thenReturn(SMARTSPACE_INSTANCE_ID)
@@ -533,8 +547,22 @@ class MediaDataFilterImplTest : SysuiTestCase() {
@Test
fun onSwipeToDismiss_setsTimedOut() {
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
- mediaDataFilter.onSwipeToDismiss()
-
+ mediaDataFilter.onSwipeToDismiss(1)
+
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardUIEvent(
+ eq(MediaSmartspaceLogger.SMARTSPACE_CARD_DISMISS_EVENT),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ eq(true)
+ )
verify(mediaDataProcessor).setInactive(eq(KEY), eq(true), eq(true))
}
@@ -1053,11 +1081,27 @@ class MediaDataFilterImplTest : SysuiTestCase() {
targetId = SMARTSPACE_KEY,
isActive = true,
packageName = SMARTSPACE_PACKAGE,
- recommendations = listOf(smartspaceMediaRecommendationItem),
+ recommendations =
+ listOf(
+ smartspaceMediaRecommendationItem,
+ smartspaceMediaRecommendationItem,
+ smartspaceMediaRecommendationItem
+ ),
)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data)
- mediaDataFilter.onSwipeToDismiss()
+ mediaDataFilter.onSwipeToDismiss(1)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_DISMISS_EVENT,
+ SmallHash.hash(data.targetId),
+ Process.INVALID_UID,
+ surface = 1,
+ cardinality = 1,
+ isRecommendationCard = true,
+ isSwipeToDismiss = true
+ )
verify(mediaDataProcessor).setRecommendationInactive(eq(SMARTSPACE_KEY))
verify(mediaDataProcessor, never())
.dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index ccf926a535e4..f8358c51ed5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -40,6 +40,7 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.MediaTestUtils
@@ -158,6 +159,7 @@ class MediaCarouselControllerTest : SysuiTestCase() {
testDispatcher = UnconfinedTestDispatcher()
mediaCarouselController =
MediaCarouselController(
+ applicationScope = kosmos.applicationCoroutineScope,
context = context,
mediaControlPanelFactory = mediaControlPanelFactory,
visualStabilityProvider = visualStabilityProvider,
@@ -195,10 +197,10 @@ class MediaCarouselControllerTest : SysuiTestCase() {
MediaPlayerData.clear()
FakeExecutor.exhaustExecutors(bgExecutor)
verify(globalSettings)
- .registerContentObserverSync(
- eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
- capture(settingsObserverCaptor)
- )
+ .registerContentObserverSync(
+ eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
+ capture(settingsObserverCaptor)
+ )
}
@After
@@ -894,7 +896,10 @@ class MediaCarouselControllerTest : SysuiTestCase() {
mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
mediaCarouselController.mediaCarousel = mediaCarousel
- val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this)
+ val settingsJob =
+ mediaCarouselController.listenForLockscreenSettingChanges(
+ kosmos.applicationCoroutineScope
+ )
secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false)
val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this)
@@ -921,7 +926,10 @@ class MediaCarouselControllerTest : SysuiTestCase() {
mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
mediaCarouselController.mediaCarousel = mediaCarousel
- val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this)
+ val settingsJob =
+ mediaCarouselController.listenForLockscreenSettingChanges(
+ kosmos.applicationCoroutineScope
+ )
secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true)
val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index ecc456cb3888..a77072217873 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -40,6 +40,7 @@ import android.media.MediaMetadata
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.Bundle
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
@@ -1771,8 +1772,40 @@ public class MediaControlPanelTest : SysuiTestCase() {
verify(logger).logSeek(anyInt(), eq(PACKAGE), eq(instanceId))
}
+ @EnableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
@Test
- fun tapContentView_showOverLockscreen_openActivity() {
+ fun tapContentView_showOverLockscreen_openActivity_withOriginAnimation() {
+ // WHEN we are on lockscreen and this activity can show over lockscreen
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ whenever(activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())).thenReturn(true)
+
+ val clickIntent = mock(Intent::class.java)
+ val pendingIntent = mock(PendingIntent::class.java)
+ whenever(pendingIntent.intent).thenReturn(clickIntent)
+ val captor = ArgumentCaptor.forClass(View.OnClickListener::class.java)
+ val data = mediaData.copy(clickIntent = pendingIntent)
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(data, KEY)
+ verify(viewHolder.player).setOnClickListener(captor.capture())
+
+ // THEN it sends the PendingIntent without dismissing keyguard first,
+ // and does not use the Intent directly (see b/271845008)
+ captor.value.onClick(viewHolder.player)
+ verify(activityStarter)
+ .startPendingIntentMaybeDismissingKeyguard(
+ eq(pendingIntent),
+ eq(true),
+ eq(null),
+ any(),
+ eq(null),
+ eq(null),
+ )
+ verify(activityStarter, never()).postStartActivityDismissingKeyguard(eq(clickIntent), any())
+ }
+
+ @DisableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
+ @Test
+ fun tapContentView_showOverLockscreen_openActivity_withoutOriginAnimation() {
// WHEN we are on lockscreen and this activity can show over lockscreen
whenever(keyguardStateController.isShowing).thenReturn(true)
whenever(activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index b8267a0e83d4..b0213a4b6421 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -18,8 +18,6 @@ package com.android.systemui.shade
import android.graphics.Rect
import android.os.PowerManager
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -29,10 +27,8 @@ import android.widget.FrameLayout
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchHandler
import com.android.systemui.ambient.touch.TouchMonitor
@@ -56,10 +52,8 @@ import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -70,8 +64,6 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.Mock
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@@ -184,133 +176,6 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
}
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_communalClosed_doesNotIntercept() =
- with(kosmos) {
- testScope.runTest {
- // Communal is closed.
- goToScene(CommunalScenes.Blank)
-
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_openGesture_interceptsTouches() =
- with(kosmos) {
- testScope.runTest {
- // Communal is closed.
- goToScene(CommunalScenes.Blank)
-
- // Initial touch down is intercepted, and so are touches outside of the region,
- // until an
- // up event is received.
- assertThat(underTest.onTouchEvent(DOWN_IN_RIGHT_SWIPE_REGION_EVENT)).isTrue()
- assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
- assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
- assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_communalTransitioning_interceptsTouches() =
- with(kosmos) {
- testScope.runTest {
- // Communal is opening.
- communalRepository.setTransitionState(
- flowOf(
- ObservableTransitionState.Transition(
- fromScene = CommunalScenes.Blank,
- toScene = CommunalScenes.Communal,
- currentScene = flowOf(CommunalScenes.Blank),
- progress = flowOf(0.5f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(true)
- )
- )
- )
- testableLooper.processAllMessages()
-
- // Touch events are intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
- // User activity sent to PowerManager.
- verify(powerManager).userActivity(any(), any(), any())
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_communalOpen_interceptsTouches() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Touch events are intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
- // User activity sent to PowerManager.
- verify(powerManager).userActivity(any(), any(), any())
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Bouncer is visible.
- fakeKeyguardBouncerRepository.setPrimaryShow(true)
- testableLooper.processAllMessages()
-
- // Touch events are not intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- // User activity is not sent to PowerManager.
- verify(powerManager, times(0)).userActivity(any(), any(), any())
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Shade shows up.
- shadeTestUtil.setQsExpansion(1.0f)
- testableLooper.processAllMessages()
-
- // Touch events are not intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_containerViewDisposed_doesNotIntercept() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Touch events are intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
-
- // Container view disposed.
- underTest.disposeView()
-
- // Touch events are not intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- }
- }
-
@Test
fun lifecycle_initializedAfterConstruction() =
with(kosmos) {
@@ -517,7 +382,6 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
@Test
- @EnableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
fun fullScreenSwipeGesture_doNotProcessTouchesInNotificationStack() =
with(kosmos) {
testScope.runTest {
@@ -572,9 +436,5 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
CONTAINER_HEIGHT.toFloat() / 2,
0
)
- private val DOWN_IN_RIGHT_SWIPE_REGION_EVENT =
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, CONTAINER_WIDTH.toFloat(), 0f, 0)
- private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
- private val UP_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 88b832d15342..15c4bfce0a04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -464,6 +464,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
() -> mShadeInteractor,
() -> mKosmos.getDeviceUnlockedInteractor(),
() -> mKosmos.getSceneInteractor(),
+ () -> mKosmos.getSceneContainerOcclusionInteractor(),
() -> mKosmos.getKeyguardClockInteractor());
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
@@ -621,6 +622,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
() -> mShadeInteractor,
() -> mKosmos.getDeviceUnlockedInteractor(),
() -> mKosmos.getSceneInteractor(),
+ () -> mKosmos.getSceneContainerOcclusionInteractor(),
() -> mKosmos.getKeyguardClockInteractor()),
mKeyguardBypassController,
mDozeParameters,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index c3e810ed1e31..6cab71fd8e88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -105,6 +105,8 @@ class CallChipViewModelTest : SysuiTestCase() {
assertThat(((latest as OngoingActivityChipModel.Shown).icon as Icon.Resource).res)
.isEqualTo(com.android.internal.R.drawable.ic_phone)
+ assertThat((latest as OngoingActivityChipModel.Shown).icon!!.contentDescription)
+ .isNotNull()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index bde668ece83f..74b6ae2f9379 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -104,6 +104,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+ assertThat(icon.contentDescription).isNotNull()
}
@Test
@@ -117,6 +118,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+ assertThat(icon.contentDescription).isNotNull()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
index 8e8b0820110d..0a06cc773727 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -143,6 +143,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord)
+ assertThat(icon.contentDescription).isNotNull()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index 2e5f7f55031c..3028d008f01d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -129,6 +129,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all)
+ assertThat(icon.contentDescription).isNotNull()
}
@Test
@@ -142,6 +143,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all)
+ assertThat(icon.contentDescription).isNotNull()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index b8f802600b3b..164a06e68dc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -71,6 +71,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightCh
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import org.junit.Assert;
@@ -860,6 +861,133 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_systemExpandedTrueForHeadsUp_notExpanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUp(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_systemExpandedTrueForHeadsUpDisappearRunning_notExpanded()
+ throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUpAnimatingAway(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_userExpandedTrueForHeadsUp_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUpAnimatingAway(true);
+ row.setUserExpanded(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_userExpandedTrueForHeadsUpDisappearRunning_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUpAnimatingAway(true);
+ row.setUserExpanded(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_userExpandedFalseForHeadsUp_notExpanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUpAnimatingAway(true);
+ row.setUserExpanded(false);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_userExpandedFalseForHeadsUpDisappearRunning_notExpanded()
+ throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUpAnimatingAway(true);
+ row.setUserExpanded(false);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_HUNexpandedWhenPinningTrue_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUp(true);
+ row.setPinned(true);
+
+ // WHEN
+ row.expandNotification();
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_HUNexpandedWhenPinningFalse_notExpanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(false);
+ row.setHeadsUp(true);
+ row.setPinned(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void hasUserChangedExpansion_expandPinned_returnTrue() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setPinned(true);
+
+ // WHEN
+ row.expandNotification();
+
+ // THEN
+ assertThat(row.hasUserChangedExpansion()).isTrue();
+ }
+
+ @Test
public void onDisappearAnimationFinished_shouldSetFalse_headsUpAnimatingAway()
throws Exception {
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 35888a5fa734..80b9e800f445 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -16,14 +16,18 @@ package com.android.systemui.statusbar.phone;
import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.content.Intent;
+import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.View;
@@ -38,8 +42,10 @@ import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentView;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -55,6 +61,8 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
+
+ @Mock private GroupExpansionManager mGroupExpansionManager;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private com.android.systemui.shade.ShadeController mShadeController;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@@ -77,7 +85,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
mNotificationLockscreenUserManager);
mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
- mock(GroupExpansionManager.class), mNotificationLockscreenUserManager,
+ mGroupExpansionManager, mNotificationLockscreenUserManager,
mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager,
mActivityStarter, mShadeController,
new CommandQueue(mContext, new FakeDisplayTracker(mContext)),
@@ -103,4 +111,177 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
verify(mStatusBarKeyguardViewManager).showBouncer(true);
}
+
+ @Test
+ public void onMakeExpandedVisibleForRemoteInput_collapsedGroup_expandGroupExpansion() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(true);
+ when(enr.areChildrenExpanded()).thenReturn(false);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(mGroupExpansionManager).toggleGroupExpansion(enrEntry);
+ verify(enr).setUserExpanded(true);
+ verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+ }
+
+ @Test
+ public void onMakeExpandedVisibleForRemoteInput_expandedGroup_setUserExpandedTrue() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(true);
+ when(enr.areChildrenExpanded()).thenReturn(true);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+ verify(enr).setUserExpanded(true);
+ verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+ }
+
+ @Test
+ public void onMakeExpandedVisibleForRemoteInput_nonGroupNotifications_setUserExpandedTrue() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(false);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+ verify(enr).setUserExpanded(true);
+ verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void onMakeExpandedVisibleForRemoteInput_notExpandedGroup_toggleExpansion() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(true);
+ when(enr.areChildrenExpanded()).thenReturn(false);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(mGroupExpansionManager).toggleGroupExpansion(enrEntry);
+ verify(enr, never()).setUserExpanded(anyBoolean());
+ verify(privateLayout, never()).setOnExpandedVisibleListener(any());
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void onMakeExpandedVisibleForRemoteInput_expandedGroup_notToggleExpansion() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(true);
+ when(enr.areChildrenExpanded()).thenReturn(true);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(enrEntry);
+ verify(enr, never()).setUserExpanded(anyBoolean());
+ verify(privateLayout, never()).setOnExpandedVisibleListener(any());
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void onMakeExpandedVisibleForRemoteInput_notExpandedNotification_toggleExpansion() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(false);
+ when(enr.isExpanded()).thenReturn(false);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(enr).toggleExpansionState();
+ verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+ verify(enr, never()).setUserExpanded(anyBoolean());
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void onMakeExpandedVisibleForRemoteInput_expandedNotification_notToggleExpansion() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(false);
+ when(enr.isExpanded()).thenReturn(true);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(enr, never()).toggleExpansionState();
+ verify(privateLayout, never()).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+ verify(enr, never()).setUserExpanded(anyBoolean());
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fabb9b770a11..c5fbc39e79c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -25,6 +25,8 @@ import static android.service.notification.NotificationListenerService.NOTIFICAT
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+import static androidx.test.ext.truth.content.IntentSubject.assertThat;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING;
import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR;
@@ -2017,6 +2019,31 @@ public class BubblesTest extends SysuiTestCase {
}
@Test
+ public void testShowOrHideAppBubble_updateExistedBubbleInOverflow_updateIntentInBubble() {
+ String appBubbleKey = Bubble.getAppBubbleKeyForApp(mAppBubbleIntent.getPackage(), mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
+ // Collapse the stack so we don't need to wait for the dismiss animation in the test
+ mBubbleController.collapseStack();
+ // Dismiss the app bubble so it's in the overflow
+ mBubbleController.dismissBubble(appBubbleKey, Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNotNull();
+
+ // Modify the intent to include new extras.
+ Intent newAppBubbleIntent = new Intent(mContext, BubblesTestActivity.class)
+ .setPackage(mContext.getPackageName())
+ .putExtra("hello", "world");
+
+ // Calling this while collapsed will re-add and expand the app bubble
+ mBubbleController.showOrHideAppBubble(newAppBubbleIntent, mUser0, mAppBubbleIcon);
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleKey);
+ assertThat(mBubbleController.isStackExpanded()).isTrue();
+ assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
+ assertThat(mBubbleData.getBubbles().get(0).getAppBubbleIntent()).extras().string(
+ "hello").isEqualTo("world");
+ assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNull();
+ }
+
+ @Test
public void testCreateBubbleFromOngoingNotification() {
NotificationEntry notif = new NotificationEntryBuilder()
.setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
diff --git a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
new file mode 100644
index 000000000000..36ac4a469566
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input
+
+import android.view.InputDevice
+import android.view.KeyCharacterMap
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.invocation.InvocationOnMock
+
+class FakeInputManager {
+
+ private val devices = mutableMapOf<Int, InputDevice>()
+
+ val inputManager =
+ mock<InputManager> {
+ whenever(getInputDevice(anyInt())).thenAnswer { invocation ->
+ val deviceId = invocation.arguments[0] as Int
+ return@thenAnswer devices[deviceId]
+ }
+ whenever(inputDeviceIds).thenAnswer {
+ return@thenAnswer devices.keys.toIntArray()
+ }
+
+ fun setDeviceEnabled(invocation: InvocationOnMock, enabled: Boolean) {
+ val deviceId = invocation.arguments[0] as Int
+ val device = devices[deviceId] ?: return
+ devices[deviceId] = device.copy(enabled = enabled)
+ }
+
+ whenever(disableInputDevice(anyInt())).thenAnswer { invocation ->
+ setDeviceEnabled(invocation, enabled = false)
+ }
+ whenever(enableInputDevice(anyInt())).thenAnswer { invocation ->
+ setDeviceEnabled(invocation, enabled = true)
+ }
+ }
+
+ fun addPhysicalKeyboard(id: Int, enabled: Boolean = true) {
+ check(id > 0) { "Physical keyboard ids have to be > 0" }
+ addKeyboard(id, enabled)
+ }
+
+ fun addVirtualKeyboard(enabled: Boolean = true) {
+ addKeyboard(id = KeyCharacterMap.VIRTUAL_KEYBOARD, enabled)
+ }
+
+ private fun addKeyboard(id: Int, enabled: Boolean = true) {
+ devices[id] =
+ InputDevice.Builder()
+ .setId(id)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setEnabled(enabled)
+ .build()
+ }
+
+ private fun InputDevice.copy(
+ id: Int = getId(),
+ type: Int = keyboardType,
+ sources: Int = getSources(),
+ enabled: Boolean = isEnabled
+ ) =
+ InputDevice.Builder()
+ .setId(id)
+ .setKeyboardType(type)
+ .setSources(sources)
+ .setEnabled(enabled)
+ .build()
+}
diff --git a/packages/SystemUI/tests/utils/src/android/hardware/input/InputManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/hardware/input/InputManagerKosmos.kt
new file mode 100644
index 000000000000..bf68eabc8cc2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/hardware/input/InputManagerKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeInputManager by Kosmos.Fixture { FakeInputManager() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
index 4999a5a29a7a..43b57de3df9f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
@@ -26,6 +26,8 @@ import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
import com.android.systemui.biometrics.udfpsUtils
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.util.mockito.mock
+import org.mockito.Mockito.mock
val Kosmos.promptViewModel by Fixture {
PromptViewModel(
@@ -35,7 +37,9 @@ val Kosmos.promptViewModel by Fixture {
udfpsOverlayInteractor = udfpsOverlayInteractor,
biometricStatusInteractor = biometricStatusInteractor,
udfpsUtils = udfpsUtils,
- iconProvider = IconProvider(applicationContext),
+ iconProvider = iconProvider,
activityTaskManager = activityTaskManager,
)
}
+
+val Kosmos.iconProvider by Fixture { mock<IconProvider>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 3401cc4be231..f51036f3b54b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -17,10 +17,16 @@
package com.android.systemui.keyboard.shortcut
import android.content.applicationContext
+import android.content.res.mainResources
+import android.hardware.input.fakeInputManager
import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperTestHelper
-import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperInteractor
+import com.android.systemui.keyboard.shortcut.data.source.MultitaskingShortcutsSource
+import com.android.systemui.keyboard.shortcut.data.source.SystemShortcutsSource
+import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor
+import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
import com.android.systemui.keyboard.shortcut.ui.ShortcutHelperActivityStarter
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
import com.android.systemui.keyguard.data.repository.fakeCommandQueue
@@ -31,26 +37,61 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.model.sysUiState
import com.android.systemui.settings.displayTracker
-val Kosmos.shortcutHelperRepository by
- Kosmos.Fixture { ShortcutHelperRepository(fakeCommandQueue, broadcastDispatcher) }
+val Kosmos.shortcutHelperSystemShortcutsSource by
+ Kosmos.Fixture { SystemShortcutsSource(mainResources) }
+
+val Kosmos.shortcutHelperMultiTaskingShortcutsSource by
+ Kosmos.Fixture { MultitaskingShortcutsSource(mainResources) }
+
+val Kosmos.shortcutHelperStateRepository by
+ Kosmos.Fixture {
+ ShortcutHelperStateRepository(
+ fakeCommandQueue,
+ broadcastDispatcher,
+ fakeInputManager.inputManager,
+ testScope,
+ testDispatcher
+ )
+ }
+
+val Kosmos.shortcutHelperCategoriesRepository by
+ Kosmos.Fixture {
+ ShortcutHelperCategoriesRepository(
+ shortcutHelperSystemShortcutsSource,
+ shortcutHelperMultiTaskingShortcutsSource,
+ )
+ }
val Kosmos.shortcutHelperTestHelper by
Kosmos.Fixture {
ShortcutHelperTestHelper(
- shortcutHelperRepository,
+ shortcutHelperStateRepository,
applicationContext,
broadcastDispatcher,
fakeCommandQueue
)
}
-val Kosmos.shortcutHelperInteractor by
+val Kosmos.shortcutHelperStateInteractor by
Kosmos.Fixture {
- ShortcutHelperInteractor(displayTracker, testScope, sysUiState, shortcutHelperRepository)
+ ShortcutHelperStateInteractor(
+ displayTracker,
+ testScope,
+ sysUiState,
+ shortcutHelperStateRepository
+ )
+ }
+
+val Kosmos.shortcutHelperCategoriesInteractor by
+ Kosmos.Fixture {
+ ShortcutHelperCategoriesInteractor(
+ shortcutHelperStateRepository,
+ shortcutHelperCategoriesRepository
+ )
}
val Kosmos.shortcutHelperViewModel by
- Kosmos.Fixture { ShortcutHelperViewModel(testDispatcher, shortcutHelperInteractor) }
+ Kosmos.Fixture { ShortcutHelperViewModel(testDispatcher, shortcutHelperStateInteractor) }
val Kosmos.fakeShortcutHelperStartActivity by Kosmos.Fixture { FakeShortcutHelperStartActivity() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
index e6e7b8796d39..36608ff57c93 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
@@ -22,7 +22,7 @@ import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
class ShortcutHelperTestHelper(
- repo: ShortcutHelperRepository,
+ repo: ShortcutHelperStateRepository,
private val context: Context,
private val fakeBroadcastDispatcher: FakeBroadcastDispatcher,
private val fakeCommandQueue: FakeCommandQueue,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/DismissCallbackRegistryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/DismissCallbackRegistryKosmos.kt
new file mode 100644
index 000000000000..b12f947ab7fe
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/DismissCallbackRegistryKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard
+
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.dismissCallbackRegistry by Fixture { DismissCallbackRegistry(fakeExecutor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index dca531ac3cc6..5bae6ec89b65 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -133,6 +133,8 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
override val topClippingBounds = MutableStateFlow<Int?>(null)
+ private var isShowKeyguardWhenReenabled: Boolean = false
+
override fun setQuickSettingsVisible(isVisible: Boolean) {
_isQuickSettingsVisible.value = isVisible
}
@@ -268,6 +270,14 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
fun setIsEncryptedOrLockdown(value: Boolean) {
_isEncryptedOrLockdown.value = value
}
+
+ override fun setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled: Boolean) {
+ this.isShowKeyguardWhenReenabled = isShowKeyguardWhenReenabled
+ }
+
+ override fun isShowKeyguardWhenReenabled(): Boolean {
+ return isShowKeyguardWhenReenabled
+ }
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 31cdbc73f6fa..10ff73179f71 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -50,6 +50,7 @@ import com.android.systemui.model.sceneContainerPlugin
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.scrimStartable
import com.android.systemui.scene.sceneContainerConfig
@@ -142,4 +143,5 @@ class KosmosJavaAdapter() {
val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel }
val scrimController by lazy { kosmos.scrimController }
val scrimStartable by lazy { kosmos.scrimStartable }
+ val sceneContainerOcclusionInteractor by lazy { kosmos.sceneContainerOcclusionInteractor }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
index 81adefa11c9b..6e650a3c5391 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media.controls.domain.pipeline.interactor
-import android.content.applicationContext
import com.android.systemui.activityIntentHelper
import com.android.systemui.bluetooth.mockBroadcastDialogController
import com.android.systemui.kosmos.Kosmos
@@ -31,7 +30,6 @@ import com.android.systemui.statusbar.policy.keyguardStateController
val Kosmos.mediaControlInteractor by
Kosmos.Fixture {
MediaControlInteractor(
- applicationContext = applicationContext,
instanceId = mediaInstanceId,
repository = mediaFilterRepository,
mediaDataProcessor = mediaDataProcessor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
index 461eaa24426f..e490b7502894 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media.controls.domain.pipeline.interactor.factory
-import android.content.applicationContext
import com.android.internal.logging.InstanceId
import com.android.systemui.activityIntentHelper
import com.android.systemui.bluetooth.mockBroadcastDialogController
@@ -34,7 +33,6 @@ val Kosmos.mediaControlInteractorFactory by
object : MediaControlInteractorFactory {
override fun create(instanceId: InstanceId): MediaControlInteractor {
return MediaControlInteractor(
- applicationContext = applicationContext,
instanceId = instanceId,
repository = mediaFilterRepository,
mediaDataProcessor = mediaDataProcessor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
index ec56327c1101..f9f8d232611e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
@@ -22,6 +22,7 @@ import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.StatusBarStateControllerImpl
@@ -38,6 +39,7 @@ var Kosmos.statusBarStateController: SysuiStatusBarStateController by
{ shadeInteractor },
{ deviceUnlockedInteractor },
{ sceneInteractor },
+ { sceneContainerOcclusionInteractor },
{ keyguardClockInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
index 066736c1e036..0921eb9e83d3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.scene.domain.interactor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
@@ -31,5 +32,6 @@ val Kosmos.sceneInteractor by
logger = sceneLogger,
sceneFamilyResolvers = { sceneFamilyResolvers },
deviceUnlockedInteractor = deviceUnlockedInteractor,
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
index 6be19393ca2b..ae33aead67a7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
@@ -20,6 +20,7 @@ package com.android.systemui.scene.domain.resolver
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.shared.model.SceneFamilies
@@ -39,6 +40,7 @@ val Kosmos.homeSceneFamilyResolver by
HomeSceneFamilyResolver(
applicationScope = applicationCoroutineScope,
deviceEntryInteractor = deviceEntryInteractor,
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index 8b887d32f3c1..97fd2c43bb23 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -25,6 +25,8 @@ import com.android.systemui.classifier.falsingManager
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.keyguard.dismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor
import com.android.systemui.kosmos.Kosmos
@@ -71,5 +73,7 @@ val Kosmos.sceneContainerStartable by Fixture {
sceneBackInteractor = sceneBackInteractor,
shadeSessionStorage = shadeSessionStorage,
windowMgrLockscreenVisInteractor = windowManagerLockscreenVisibilityInteractor,
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
+ dismissCallbackRegistry = dismissCallbackRegistry,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 4813794a85bb..b9918f1e46d8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -30,7 +30,7 @@ import kotlinx.coroutines.flow.asStateFlow
@SysUISingleton
class FakeShadeRepository @Inject constructor() : ShadeRepository {
private val _qsExpansion = MutableStateFlow(0f)
- override val qsExpansion = _qsExpansion
+ @Deprecated("Use ShadeInteractor.qsExpansion instead") override val qsExpansion = _qsExpansion
private val _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
override val udfpsTransitionToFullShadeProgress = _udfpsTransitionToFullShadeProgress
@@ -59,12 +59,15 @@ class FakeShadeRepository @Inject constructor() : ShadeRepository {
private val _legacyIsQsExpanded = MutableStateFlow(false)
@Deprecated("Use ShadeInteractor instead") override val legacyIsQsExpanded = _legacyIsQsExpanded
+ @Deprecated("Use ShadeInteractor.isUserInteractingWithShade instead")
override val legacyLockscreenShadeTracking = MutableStateFlow(false)
private val _shadeMode = MutableStateFlow<ShadeMode>(ShadeMode.Single)
override val shadeMode: StateFlow<ShadeMode> = _shadeMode.asStateFlow()
- override val isDualShadeAlignedToBottom = false
+ private var _isDualShadeAlignedToBottom = false
+ override val isDualShadeAlignedToBottom
+ get() = _isDualShadeAlignedToBottom
@Deprecated("Use ShadeInteractor instead")
override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) {
@@ -139,8 +142,12 @@ class FakeShadeRepository @Inject constructor() : ShadeRepository {
_legacyShadeExpansion.value = expandedFraction
}
- override fun setShadeMode(shadeMode: ShadeMode) {
- _shadeMode.value = shadeMode
+ override fun setShadeMode(mode: ShadeMode) {
+ _shadeMode.value = mode
+ }
+
+ fun setDualShadeAlignedToBottom(isAlignedToBottom: Boolean) {
+ _isDualShadeAlignedToBottom = isAlignedToBottom
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt
index 1ca3509cbd79..72a80d480288 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.shade.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneViewModel
+import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.notificationsShadeSceneViewModel: NotificationsShadeSceneViewModel by
- Kosmos.Fixture { NotificationsShadeSceneViewModel() }
+ Kosmos.Fixture { NotificationsShadeSceneViewModel(shadeInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
index 4d81ea16220c..8adb26f2d65f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
@@ -22,6 +22,7 @@ import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel
import com.android.systemui.qs.ui.adapter.qsSceneAdapter
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneViewModel
+import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.quickSettingsShadeSceneViewModel: QuickSettingsShadeSceneViewModel by
Kosmos.Fixture {
@@ -31,5 +32,6 @@ val Kosmos.quickSettingsShadeSceneViewModel: QuickSettingsShadeSceneViewModel by
tileGridViewModel = tileGridViewModel,
editModeViewModel = editModeViewModel,
qsSceneAdapter = qsSceneAdapter,
+ shadeInteractor = shadeInteractor,
)
}
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 48bc803f4a5c..ad216b59c93d 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -276,3 +276,77 @@ sh_test_host {
":services.core.ravenwood.keep_all",
],
}
+
+java_library {
+ name: "services.fakes.ravenwood-jarjar",
+ installable: false,
+ srcs: [":services.fakes-sources"],
+ libs: [
+ "ravenwood-framework",
+ "services.core.ravenwood",
+ ],
+ jarjar_rules: ":ravenwood-services-jarjar-rules",
+ visibility: ["//visibility:private"],
+}
+
+java_library {
+ name: "mockito-ravenwood-prebuilt",
+ installable: false,
+ static_libs: [
+ "mockito-robolectric-prebuilt",
+ ],
+}
+
+java_library {
+ name: "inline-mockito-ravenwood-prebuilt",
+ installable: false,
+ static_libs: [
+ "inline-mockito-robolectric-prebuilt",
+ ],
+}
+
+android_ravenwood_libgroup {
+ name: "ravenwood-runtime",
+ libs: [
+ "100-framework-minus-apex.ravenwood",
+ "200-kxml2-android",
+
+ "ravenwood-runtime-common-ravenwood",
+
+ "android.test.mock.ravenwood",
+ "ravenwood-helper-runtime",
+ "hoststubgen-helper-runtime.ravenwood",
+ "services.core.ravenwood-jarjar",
+ "services.fakes.ravenwood-jarjar",
+
+ // Provide runtime versions of utils linked in below
+ "junit",
+ "truth",
+ "flag-junit",
+ "ravenwood-framework",
+ "ravenwood-junit-impl",
+ "ravenwood-junit-impl-flag",
+ "mockito-ravenwood-prebuilt",
+ "inline-mockito-ravenwood-prebuilt",
+
+ // It's a stub, so it should be towards the end.
+ "z00-all-updatable-modules-system-stubs",
+ ],
+ jni_libs: [
+ "libandroid_runtime",
+ "libravenwood_runtime",
+ ],
+}
+
+android_ravenwood_libgroup {
+ name: "ravenwood-utils",
+ libs: [
+ "junit",
+ "truth",
+ "flag-junit",
+ "ravenwood-framework",
+ "ravenwood-junit",
+ "mockito-ravenwood-prebuilt",
+ "inline-mockito-ravenwood-prebuilt",
+ ],
+}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 93531508b3eb..b4efae3a05e4 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -11,6 +11,16 @@ flag {
}
flag {
+ name: "always_allow_observing_touch_events"
+ namespace: "accessibility"
+ description: "Always allows InputFilter observing SOURCE_TOUCHSCREEN events, even if touch exploration is enabled."
+ bug: "344604959"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "resettable_dynamic_properties"
namespace: "accessibility"
description: "Maintains initial copies of a11yServiceInfo dynamic properties so they can reset on disconnect."
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 5fb60e75cf85..f9196f3e0e0e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -1087,7 +1087,15 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
}
- private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) {
+ private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) {
+ if (Flags.alwaysAllowObservingTouchEvents()) {
+ final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN);
+ if (isTouchEvent && !canShareGenericTouchEvent()) {
+ return false;
+ }
+ final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
+ return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0;
+ }
// Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
// touch exploration.
if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
@@ -1095,13 +1103,14 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
return false;
}
final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
- return (mCombinedGenericMotionEventSources
- & mCombinedMotionEventObservedSources
- & eventSourceWithoutClass)
- != 0;
+ return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0;
}
- private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) {
+ private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) {
+ if (Flags.alwaysAllowObservingTouchEvents()) {
+ final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
+ return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0;
+ }
// Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
// touch exploration.
if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
@@ -1109,7 +1118,22 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
return false;
}
final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
- return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0;
+ return (mCombinedGenericMotionEventSources
+ & mCombinedMotionEventObservedSources
+ & eventSourceWithoutClass)
+ != 0;
+ }
+
+ private boolean canShareGenericTouchEvent() {
+ if ((mCombinedMotionEventObservedSources & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
+ // Share touch events if a MotionEvent-observing service wants them.
+ return true;
+ }
+ if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) == 0) {
+ // Share touch events if touch exploration is not enabled.
+ return true;
+ }
+ return false;
}
public void setCombinedGenericMotionEventSources(int sources) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 4f9db8bece63..1654a8de6422 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -47,6 +47,11 @@ import static com.android.internal.accessibility.AccessibilityShortcutController
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityShortcutActivated;
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -923,25 +928,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
newValue, restoredFromSdk);
}
}
- case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> {
- synchronized (mLock) {
- restoreAccessibilityButtonTargetsLocked(
- previousValue, newValue);
- }
- }
- case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> {
- if (!android.view.accessibility.Flags.a11yQsShortcut()) {
- return;
- }
- restoreAccessibilityQsTargets(newValue);
- }
- case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> {
- if (!android.view.accessibility.Flags
- .restoreA11yShortcutTargetService()) {
- return;
- }
- restoreAccessibilityShortcutTargetService(previousValue, newValue);
- }
+ case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ Settings.Secure.ACCESSIBILITY_QS_TARGETS,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE ->
+ restoreShortcutTargets(newValue,
+ ShortcutUtils.convertToType(which));
}
}
}
@@ -1040,7 +1031,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
userState.mUserId, targetsFromSetting, str -> str);
- readAccessibilityButtonTargetsLocked(userState);
+ readAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
onUserStateChangedLocked(userState);
}
@@ -1720,12 +1711,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
// Turn on/off a11y qs shortcut for the a11y features based on the change in QS Panel
if (!a11yFeaturesToEnable.isEmpty()) {
- enableShortcutForTargets(/* enable= */ true, UserShortcutType.QUICK_SETTINGS,
+ enableShortcutForTargets(/* enable= */ true, QUICK_SETTINGS,
a11yFeaturesToEnable, userId);
}
if (!a11yFeaturesToRemove.isEmpty()) {
- enableShortcutForTargets(/* enable= */ false, UserShortcutType.QUICK_SETTINGS,
+ enableShortcutForTargets(/* enable= */ false, QUICK_SETTINGS,
a11yFeaturesToRemove, userId);
}
}
@@ -2057,100 +2048,74 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
/**
- * User could enable accessibility services and configure accessibility button during the SUW.
- * Merges current value of accessibility button settings into the restored one to make sure
- * user's preferences of accessibility button updated in SUW are not lost.
- *
- * Called only during settings restore; currently supports only the owner user
- * TODO: http://b/22388012
- */
- void restoreAccessibilityButtonTargetsLocked(String oldSetting, String newSetting) {
- final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedStringToSet(oldSetting, str -> str, targetsFromSetting,
- /* doMerge = */false);
- readColonDelimitedStringToSet(newSetting, str -> str, targetsFromSetting,
- /* doMerge = */true);
-
- final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
- userState.mAccessibilityButtonTargets.clear();
- userState.mAccessibilityButtonTargets.addAll(targetsFromSetting);
- persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
- UserHandle.USER_SYSTEM, userState.mAccessibilityButtonTargets, str -> str);
-
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- onUserStateChangedLocked(userState);
- }
-
- /**
* User could configure accessibility shortcut during the SUW before restoring user data.
* Merges the current value and the new value to make sure we don't lost the setting the user's
- * preferences of accessibility qs shortcut updated in SUW are not lost.
- *
- * Called only during settings restore; currently supports only the owner user
+ * preferences of accessibility shortcut updated in SUW are not lost.
+ * Called only during settings restore; currently supports only the owner user.
+ * <P>
+ * Throws an exception if used with {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP}.
+ * </P>
* TODO: http://b/22388012
*/
- private void restoreAccessibilityQsTargets(String newValue) {
+ private void restoreShortcutTargets(String newValue,
+ @UserShortcutType int shortcutType) {
+ assertNoTapShortcut(shortcutType);
+ if (shortcutType == QUICK_SETTINGS && !android.view.accessibility.Flags.a11yQsShortcut()) {
+ return;
+ }
+ if (shortcutType == HARDWARE
+ && !android.view.accessibility.Flags.restoreA11yShortcutTargetService()) {
+ return;
+ }
+
synchronized (mLock) {
final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
- final Set<String> mergedTargets = userState.getA11yQsTargets();
- readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
- /* doMerge = */ true);
+ final Set<String> mergedTargets = (shortcutType == HARDWARE)
+ ? new ArraySet<>(ShortcutUtils.getShortcutTargetsFromSettings(
+ mContext, shortcutType, userState.mUserId))
+ : userState.getShortcutTargetsLocked(shortcutType);
+
+ if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()
+ && shortcutType == HARDWARE) {
+ final String defaultService =
+ mContext.getString(R.string.config_defaultAccessibilityService);
+ final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService)
+ ? null : ComponentName.unflattenFromString(defaultService);
+ boolean shouldClearDefaultService = defaultServiceComponent != null
+ && !stringSetContainsComponentName(mergedTargets, defaultServiceComponent);
+ readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
+ /* doMerge = */ true);
+
+ if (shouldClearDefaultService && stringSetContainsComponentName(
+ mergedTargets, defaultServiceComponent)) {
+ Slog.i(LOG_TAG, "Removing default service " + defaultService
+ + " from restore of "
+ + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+ mergedTargets.removeIf(str ->
+ defaultServiceComponent.equals(ComponentName.unflattenFromString(str)));
+ }
+ if (mergedTargets.isEmpty()) {
+ return;
+ }
+ } else {
+ readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
+ /* doMerge = */ true);
+ }
- userState.updateA11yQsTargetLocked(mergedTargets);
- persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
+ userState.updateShortcutTargetsLocked(mergedTargets, shortcutType);
+ persistColonDelimitedSetToSettingLocked(ShortcutUtils.convertToKey(shortcutType),
UserHandle.USER_SYSTEM, mergedTargets, str -> str);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
onUserStateChangedLocked(userState);
}
}
- /**
- * Merges the old and restored value of
- * {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}.
- *
- * <p>Also clears out {@link R.string#config_defaultAccessibilityService} from
- * the merged set if it was not present before restoring.
- */
- private void restoreAccessibilityShortcutTargetService(
- String oldValue, String restoredValue) {
- final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedStringToSet(oldValue, str -> str,
- targetsFromSetting, /*doMerge=*/false);
- final String defaultService =
- mContext.getString(R.string.config_defaultAccessibilityService);
- final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService)
- ? null : ComponentName.unflattenFromString(defaultService);
- boolean shouldClearDefaultService = defaultServiceComponent != null
- && !stringSetContainsComponentName(targetsFromSetting, defaultServiceComponent);
- readColonDelimitedStringToSet(restoredValue, str -> str,
- targetsFromSetting, /*doMerge=*/true);
- if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()) {
- if (shouldClearDefaultService && stringSetContainsComponentName(
- targetsFromSetting, defaultServiceComponent)) {
- Slog.i(LOG_TAG, "Removing default service " + defaultService
- + " from restore of "
- + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
- targetsFromSetting.removeIf(str ->
- defaultServiceComponent.equals(ComponentName.unflattenFromString(str)));
- }
- if (targetsFromSetting.isEmpty()) {
- return;
- }
- }
- synchronized (mLock) {
- final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
- final Set<String> shortcutTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
- shortcutTargets.clear();
- shortcutTargets.addAll(targetsFromSetting);
- persistColonDelimitedSetToSettingLocked(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- UserHandle.USER_SYSTEM, targetsFromSetting, str -> str);
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- onUserStateChangedLocked(userState);
- }
+ private String getRawShortcutSetting(int userId, @UserShortcutType int shortcutType) {
+ return Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ ShortcutUtils.convertToKey(shortcutType), userId);
}
+
/**
* Returns {@code true} if the set contains the provided non-null {@link ComponentName}.
*
@@ -2263,7 +2228,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void showAccessibilityTargetsSelection(int displayId,
@UserShortcutType int shortcutType) {
final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
- final String chooserClassName = (shortcutType == UserShortcutType.HARDWARE)
+ final String chooserClassName = (shortcutType == HARDWARE)
? AccessibilityShortcutChooserActivity.class.getName()
: AccessibilityButtonChooserActivity.class.getName();
intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
@@ -3236,9 +3201,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
somethingChanged |= readAudioDescriptionEnabledSettingLocked(userState);
somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
somethingChanged |= readAutoclickEnabledSettingLocked(userState);
- somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState);
- somethingChanged |= readAccessibilityQsTargetsLocked(userState);
- somethingChanged |= readAccessibilityButtonTargetsLocked(userState);
+ somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, HARDWARE);
+ somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS);
+ somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState);
somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
@@ -3386,60 +3351,34 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userState.setSendMotionEventsEnabled(sendMotionEvents);
}
- private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
- final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId);
+ /**
+ * Throws an exception for {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} types.
+ */
+ private boolean readAccessibilityShortcutTargetsLocked(AccessibilityUserState userState,
+ @UserShortcutType int shortcutType) {
+ assertNoTapShortcut(shortcutType);
+ final String settingValue = getRawShortcutSetting(userState.mUserId, shortcutType);
final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false);
- // Fall back to device's default a11y service, only when setting is never updated.
- if (settingValue == null) {
+ // If dealing with an empty hardware shortcut, fall back to the default value.
+ if (shortcutType == HARDWARE && settingValue == null) {
final String defaultService = mContext.getString(
R.string.config_defaultAccessibilityService);
if (!TextUtils.isEmpty(defaultService)) {
- targetsFromSetting.add(defaultService);
+ // Convert to component name to reformat the target if it has a relative path.
+ ComponentName name = ComponentName.unflattenFromString(defaultService);
+ if (name != null) {
+ targetsFromSetting.add(name.flattenToString());
+ }
}
+ } else {
+ readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false);
}
- final Set<String> currentTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
- if (targetsFromSetting.equals(currentTargets)) {
- return false;
- }
- currentTargets.clear();
- currentTargets.addAll(targetsFromSetting);
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- return true;
- }
-
- private boolean readAccessibilityQsTargetsLocked(AccessibilityUserState userState) {
- final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
- userState.mUserId, str -> str, targetsFromSetting);
-
- final Set<String> currentTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
- if (targetsFromSetting.equals(currentTargets)) {
- return false;
- }
- userState.updateA11yQsTargetLocked(targetsFromSetting);
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- return true;
- }
-
- private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
- final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
- userState.mUserId, str -> str, targetsFromSetting);
-
- final Set<String> currentTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE);
- if (targetsFromSetting.equals(currentTargets)) {
- return false;
+ if (userState.updateShortcutTargetsLocked(targetsFromSetting, shortcutType)) {
+ scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+ return true;
}
- currentTargets.clear();
- currentTargets.addAll(targetsFromSetting);
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- return true;
+ return false;
}
private boolean readAccessibilityButtonTargetComponentLocked(AccessibilityUserState userState) {
@@ -3487,14 +3426,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
private void updateAccessibilityShortcutKeyTargetsLocked(AccessibilityUserState userState) {
final Set<String> currentTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
- final int lastSize = currentTargets.size();
- if (lastSize == 0) {
- return;
- }
+ userState.getShortcutTargetsLocked(HARDWARE);
currentTargets.removeIf(
name -> !userState.isShortcutTargetInstalledLocked(name));
- if (lastSize == currentTargets.size()) {
+ if (!userState.updateShortcutTargetsLocked(currentTargets, HARDWARE)) {
return;
}
@@ -3680,13 +3615,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final Set<String> currentTargets =
userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE);
- final int lastSize = currentTargets.size();
- if (lastSize == 0) {
- return;
- }
currentTargets.removeIf(
name -> !userState.isShortcutTargetInstalledLocked(name));
- if (lastSize == currentTargets.size()) {
+ if (!userState.updateShortcutTargetsLocked(currentTargets, SOFTWARE)) {
return;
}
@@ -3719,8 +3650,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
final Set<String> buttonTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE);
- int lastSize = buttonTargets.size();
+ userState.getShortcutTargetsLocked(SOFTWARE);
buttonTargets.removeIf(name -> {
if (packageName != null && name != null && !name.contains(packageName)) {
return false;
@@ -3752,13 +3682,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
return false;
});
- boolean changed = (lastSize != buttonTargets.size());
- lastSize = buttonTargets.size();
final Set<String> shortcutKeyTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
+ userState.getShortcutTargetsLocked(HARDWARE);
final Set<String> qsShortcutTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
+ userState.getShortcutTargetsLocked(QUICK_SETTINGS);
userState.mEnabledServices.forEach(componentName -> {
if (packageName != null && componentName != null
&& !packageName.equals(componentName.getPackageName())) {
@@ -3790,8 +3718,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
+ " should be assign to the button or shortcut.");
buttonTargets.add(serviceName);
});
- changed |= (lastSize != buttonTargets.size());
- if (!changed) {
+ if (!userState.updateShortcutTargetsLocked(buttonTargets, SOFTWARE)) {
return;
}
@@ -3815,10 +3742,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
final Set<String> targets =
- userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
+ userState.getShortcutTargetsLocked(QUICK_SETTINGS);
// Removes the targets that are no longer installed on the device.
- boolean somethingChanged = targets.removeIf(
+ targets.removeIf(
name -> !userState.isShortcutTargetInstalledLocked(name));
// Add the target if the a11y service is enabled and the tile exist in QS panel
Set<ComponentName> enabledServices = userState.getEnabledServicesLocked();
@@ -3829,14 +3756,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
ComponentName tileService =
a11yFeatureToTileService.getOrDefault(enabledService, null);
if (tileService != null && currentA11yTilesInQsPanel.contains(tileService)) {
- somethingChanged |= targets.add(enabledService.flattenToString());
+ targets.add(enabledService.flattenToString());
}
}
- if (!somethingChanged) {
+ if (!userState.updateShortcutTargetsLocked(targets, QUICK_SETTINGS)) {
return;
}
- userState.updateA11yQsTargetLocked(targets);
// Update setting key with new value.
persistColonDelimitedSetToSettingLocked(
@@ -3862,14 +3788,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final List<Pair<Integer, String>> shortcutTypeAndShortcutSetting = new ArrayList<>(3);
shortcutTypeAndShortcutSetting.add(
- new Pair<>(UserShortcutType.HARDWARE,
+ new Pair<>(HARDWARE,
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE));
shortcutTypeAndShortcutSetting.add(
new Pair<>(UserShortcutType.SOFTWARE,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS));
if (android.view.accessibility.Flags.a11yQsShortcut()) {
shortcutTypeAndShortcutSetting.add(
- new Pair<>(UserShortcutType.QUICK_SETTINGS,
+ new Pair<>(QUICK_SETTINGS,
Settings.Secure.ACCESSIBILITY_QS_TARGETS));
}
@@ -3883,7 +3809,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
shortcutSettingName,
userState.mUserId, currentTargets, str -> str);
- if (shortcutType != UserShortcutType.QUICK_SETTINGS) {
+ if (shortcutType != QUICK_SETTINGS) {
continue;
}
@@ -3959,7 +3885,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@EnforcePermission(MANAGE_ACCESSIBILITY)
@Override
- public void performAccessibilityShortcut(String targetName) {
+ public void performAccessibilityShortcut(
+ int displayId, @UserShortcutType int shortcutType, String targetName) {
performAccessibilityShortcut_enforcePermission();
if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".performAccessibilityShortcut",
@@ -3968,7 +3895,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::performAccessibilityShortcutInternal, this,
- Display.DEFAULT_DISPLAY, UserShortcutType.HARDWARE, targetName));
+ displayId, shortcutType, targetName));
}
/**
@@ -4115,7 +4042,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final boolean requestA11yButton = (installedServiceInfo.flags
& FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
// Turns on / off the accessibility service
- if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == UserShortcutType.HARDWARE)
+ if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == HARDWARE)
|| (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) {
if (serviceConnection == null) {
logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
@@ -4129,7 +4056,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
return true;
}
- if (shortcutType == UserShortcutType.HARDWARE && targetSdk > Build.VERSION_CODES.Q
+ if (shortcutType == HARDWARE && targetSdk > Build.VERSION_CODES.Q
&& requestA11yButton) {
if (!userState.getEnabledServicesLocked().contains(assignedTarget)) {
enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
@@ -4222,7 +4149,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
validNewTargets = newTargets;
// filter out targets that doesn't have qs shortcut
- if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+ if (shortcutType == QUICK_SETTINGS) {
validNewTargets = newTargets.stream().filter(target -> {
ComponentName targetComponent = ComponentName.unflattenFromString(target);
return featureToTileMap.containsKey(targetComponent);
@@ -4240,10 +4167,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
/* defaultEmptyString= */ ""
);
- if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+ if (shortcutType == QUICK_SETTINGS) {
int numOfFeatureChanged = Math.abs(currentTargets.size() - validNewTargets.size());
logMetricForQsShortcutConfiguration(enable, numOfFeatureChanged);
- userState.updateA11yQsTargetLocked(validNewTargets);
+ userState.updateShortcutTargetsLocked(validNewTargets, QUICK_SETTINGS);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
onUserStateChangedLocked(userState);
}
@@ -4257,7 +4184,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
// Add or Remove tile in QS Panel
- if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+ if (shortcutType == QUICK_SETTINGS) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::updateA11yTileServicesInQuickSettingsPanel,
this, validNewTargets, currentTargets, userId));
@@ -4266,7 +4193,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (!enable) {
return;
}
- if (shortcutType == UserShortcutType.HARDWARE) {
+ if (shortcutType == HARDWARE) {
skipVolumeShortcutDialogTimeoutRestriction(userId);
if (com.android.server.accessibility.Flags.enableHardwareShortcutDisablesWarning()) {
persistIntToSetting(
@@ -4461,6 +4388,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
shortcutTargets.add(serviceName);
}
}
+ userState.updateShortcutTargetsLocked(Set.copyOf(shortcutTargets), shortcutType);
return shortcutTargets;
}
}
@@ -5672,7 +5600,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|| mShowImeWithHardKeyboardUri.equals(uri)) {
userState.reconcileSoftKeyboardModeWithSettingsLocked();
} else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
- if (readAccessibilityShortcutKeySettingLocked(userState)) {
+ if (readAccessibilityShortcutTargetsLocked(userState, HARDWARE)) {
onUserStateChangedLocked(userState);
}
} else if (mAccessibilityButtonComponentIdUri.equals(uri)) {
@@ -5680,7 +5608,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
onUserStateChangedLocked(userState);
}
} else if (mAccessibilityButtonTargetsUri.equals(uri)) {
- if (readAccessibilityButtonTargetsLocked(userState)) {
+ if (readAccessibilityShortcutTargetsLocked(userState, SOFTWARE)) {
onUserStateChangedLocked(userState);
}
} else if (mUserNonInteractiveUiTimeoutUri.equals(uri)
@@ -6505,4 +6433,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
String metricId = enable ? METRIC_ID_QS_SHORTCUT_ADD : METRIC_ID_QS_SHORTCUT_REMOVE;
Counter.logIncrementWithUid(metricId, Binder.getCallingUid(), numOfFeatures);
}
+
+ private void assertNoTapShortcut(@UserShortcutType int shortcutType) {
+ if ((shortcutType & (TRIPLETAP | TWOFINGER_DOUBLETAP)) != 0) {
+ throw new IllegalArgumentException("Tap shortcuts are not supported.");
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index a37a1841fcc0..de1c86a1ac06 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -777,12 +777,15 @@ class AccessibilityUserState {
* @return The array set of the strings
*/
public ArraySet<String> getShortcutTargetsLocked(@UserShortcutType int shortcutType) {
+ return new ArraySet<>(getShortcutTargetsInternalLocked(shortcutType));
+ }
+ private ArraySet<String> getShortcutTargetsInternalLocked(@UserShortcutType int shortcutType) {
if (shortcutType == UserShortcutType.HARDWARE) {
return mAccessibilityShortcutKeyTargets;
} else if (shortcutType == UserShortcutType.SOFTWARE) {
return mAccessibilityButtonTargets;
} else if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
- return getA11yQsTargets();
+ return mAccessibilityQsTargets;
} else if ((shortcutType == UserShortcutType.TRIPLETAP
&& isMagnificationSingleFingerTripleTapEnabledLocked()) || (
shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP
@@ -795,6 +798,32 @@ class AccessibilityUserState {
}
/**
+ * Updates the corresponding shortcut targets with the provided set.
+ * Tap shortcuts don't operate using sets of targets,
+ * so trying to update {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP}
+ * will instead throw an {@code IllegalArgumentException}
+ * @param newTargets set of targets to replace the existing set.
+ * @param shortcutType type to be replaced.
+ * @return {@code true} if the set was changed, or {@code false} if the elements are the same.
+ * @throws IllegalArgumentException if {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} is used.
+ */
+ boolean updateShortcutTargetsLocked(
+ Set<String> newTargets, @UserShortcutType int shortcutType) {
+ final int mask = UserShortcutType.TRIPLETAP | UserShortcutType.TWOFINGER_DOUBLETAP;
+ if ((shortcutType & mask) != 0) {
+ throw new IllegalArgumentException("Tap shortcuts cannot be updated with target sets.");
+ }
+
+ final Set<String> currentTargets = getShortcutTargetsInternalLocked(shortcutType);
+ if (newTargets.equals(currentTargets)) {
+ return false;
+ }
+ currentTargets.clear();
+ currentTargets.addAll(newTargets);
+ return true;
+ }
+
+ /**
* Whether or not the given shortcut target is installed in device.
*
* @param name The shortcut target name
@@ -844,8 +873,9 @@ class AccessibilityUserState {
);
}
- Set<String> targets = getShortcutTargetsLocked(shortcutType);
- boolean result = targets.removeIf(name -> {
+ // getting internal set lets us directly modify targets, as it's not a copy.
+ Set<String> targets = getShortcutTargetsInternalLocked(shortcutType);
+ return targets.removeIf(name -> {
ComponentName componentName;
if (name == null
|| (componentName = ComponentName.unflattenFromString(name)) == null) {
@@ -853,11 +883,6 @@ class AccessibilityUserState {
}
return componentName.equals(target);
});
- if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
- updateA11yQsTargetLocked(targets);
- }
-
- return result;
}
/**
@@ -1114,11 +1139,6 @@ class AccessibilityUserState {
);
}
- public void updateA11yQsTargetLocked(Set<String> targets) {
- mAccessibilityQsTargets.clear();
- mAccessibilityQsTargets.addAll(targets);
- }
-
/**
* Returns a copy of the targets which has qs shortcut turned on
*/
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index d7649dc4802e..76f6d178845e 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -345,6 +345,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
LocalServices.addService(AppWidgetManagerInternal.class, new AppWidgetManagerLocal());
}
+ @Override
+ public int getMaxBitmapMemory() {
+ return mMaxWidgetBitmapMemory;
+ }
+
void systemServicesReady() {
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 70af49c88433..d091ce89e885 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -31,6 +31,7 @@ import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualRotaryEncoderScrollEvent;
import android.hardware.input.VirtualStylusButtonEvent;
import android.hardware.input.VirtualStylusMotionEvent;
import android.hardware.input.VirtualTouchEvent;
@@ -76,6 +77,7 @@ class InputController {
static final String PHYS_TYPE_TOUCHSCREEN = "Touchscreen";
static final String PHYS_TYPE_NAVIGATION_TOUCHPAD = "NavigationTouchpad";
static final String PHYS_TYPE_STYLUS = "Stylus";
+ static final String PHYS_TYPE_ROTARY_ENCODER = "RotaryEncoder";
@StringDef(prefix = { "PHYS_TYPE_" }, value = {
PHYS_TYPE_DPAD,
PHYS_TYPE_KEYBOARD,
@@ -83,6 +85,7 @@ class InputController {
PHYS_TYPE_TOUCHSCREEN,
PHYS_TYPE_NAVIGATION_TOUCHPAD,
PHYS_TYPE_STYLUS,
+ PHYS_TYPE_ROTARY_ENCODER,
})
@Retention(RetentionPolicy.SOURCE)
@interface PhysType {
@@ -206,6 +209,15 @@ class InputController {
height, width));
}
+ void createRotaryEncoder(@NonNull String deviceName, int vendorId, int productId,
+ @NonNull IBinder deviceToken, int displayId) throws DeviceCreationException {
+ final String phys = createPhys(PHYS_TYPE_ROTARY_ENCODER);
+ createDeviceInternal(InputDeviceDescriptor.TYPE_ROTARY_ENCODER, deviceName, vendorId,
+ productId, deviceToken, displayId, phys,
+ () -> mNativeWrapper.openUinputRotaryEncoder(deviceName, vendorId, productId,
+ phys));
+ }
+
void unregisterInputDevice(@NonNull IBinder token) {
synchronized (mLock) {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.remove(
@@ -403,6 +415,20 @@ class InputController {
}
}
+ boolean sendRotaryEncoderScrollEvent(@NonNull IBinder token,
+ @NonNull VirtualRotaryEncoderScrollEvent event) {
+ synchronized (mLock) {
+ final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+ token);
+ if (inputDeviceDescriptor == null) {
+ return false;
+ }
+ return mNativeWrapper.writeRotaryEncoderScrollEvent(
+ inputDeviceDescriptor.getNativePointer(), event.getScrollAmount(),
+ event.getEventTimeNanos());
+ }
+ }
+
public void dump(@NonNull PrintWriter fout) {
fout.println(" InputController: ");
synchronized (mLock) {
@@ -449,6 +475,9 @@ class InputController {
int productId, String phys, int height, int width);
private static native long nativeOpenUinputStylus(String deviceName, int vendorId,
int productId, String phys, int height, int width);
+ private static native long nativeOpenUinputRotaryEncoder(String deviceName, int vendorId,
+ int productId, String phys);
+
private static native void nativeCloseUinput(long ptr);
private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action,
long eventTimeNanos);
@@ -467,6 +496,8 @@ class InputController {
int locationX, int locationY, int pressure, int tiltX, int tiltY, long eventTimeNanos);
private static native boolean nativeWriteStylusButtonEvent(long ptr, int buttonCode, int action,
long eventTimeNanos);
+ private static native boolean nativeWriteRotaryEncoderScrollEvent(long ptr, float scrollAmount,
+ long eventTimeNanos);
/** Wrapper around the static native methods for tests. */
@VisibleForTesting
@@ -495,6 +526,11 @@ class InputController {
return nativeOpenUinputStylus(deviceName, vendorId, productId, phys, height, width);
}
+ public long openUinputRotaryEncoder(String deviceName, int vendorId, int productId,
+ String phys) {
+ return nativeOpenUinputRotaryEncoder(deviceName, vendorId, productId, phys);
+ }
+
public void closeUinput(long ptr) {
nativeCloseUinput(ptr);
}
@@ -542,6 +578,11 @@ class InputController {
long eventTimeNanos) {
return nativeWriteStylusButtonEvent(ptr, buttonCode, action, eventTimeNanos);
}
+
+ public boolean writeRotaryEncoderScrollEvent(long ptr, float scrollAmount,
+ long eventTimeNanos) {
+ return nativeWriteRotaryEncoderScrollEvent(ptr, scrollAmount, eventTimeNanos);
+ }
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -553,6 +594,7 @@ class InputController {
static final int TYPE_DPAD = 4;
static final int TYPE_NAVIGATION_TOUCHPAD = 5;
static final int TYPE_STYLUS = 6;
+ static final int TYPE_ROTARY_ENCODER = 7;
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_KEYBOARD,
TYPE_MOUSE,
@@ -560,6 +602,7 @@ class InputController {
TYPE_DPAD,
TYPE_NAVIGATION_TOUCHPAD,
TYPE_STYLUS,
+ TYPE_ROTARY_ENCODER,
})
@Retention(RetentionPolicy.SOURCE)
@interface Type {
@@ -821,6 +864,8 @@ class InputController {
return "virtual_devices.value_virtual_navigationtouchpad_created_count";
case InputDeviceDescriptor.TYPE_STYLUS:
return "virtual_devices.value_virtual_stylus_created_count";
+ case InputDeviceDescriptor.TYPE_ROTARY_ENCODER:
+ return "virtual_devices.value_virtual_rotary_created_count";
default:
Log.e(TAG, "No metric known for input type: " + type);
return null;
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 6704049e3612..04c428438602 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -77,6 +77,8 @@ import android.hardware.input.VirtualMouseConfig;
import android.hardware.input.VirtualMouseRelativeEvent;
import android.hardware.input.VirtualMouseScrollEvent;
import android.hardware.input.VirtualNavigationTouchpadConfig;
+import android.hardware.input.VirtualRotaryEncoderConfig;
+import android.hardware.input.VirtualRotaryEncoderScrollEvent;
import android.hardware.input.VirtualStylusButtonEvent;
import android.hardware.input.VirtualStylusConfig;
import android.hardware.input.VirtualStylusMotionEvent;
@@ -812,6 +814,26 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override // Binder call
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void createVirtualRotaryEncoder(@NonNull VirtualRotaryEncoderConfig config,
+ @NonNull IBinder deviceToken) {
+ super.createVirtualRotaryEncoder_enforcePermission();
+ Objects.requireNonNull(config);
+ Objects.requireNonNull(deviceToken);
+ checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mInputController.createRotaryEncoder(config.getInputDeviceName(), config.getVendorId(),
+ config.getProductId(), deviceToken,
+ getTargetDisplayIdForInput(config.getAssociatedDisplayId()));
+ } catch (InputController.DeviceCreationException e) {
+ throw new IllegalArgumentException(e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void unregisterInputDevice(IBinder token) {
super.unregisterInputDevice_enforcePermission();
final long ident = Binder.clearCallingIdentity();
@@ -947,6 +969,19 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override // Binder call
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public boolean sendRotaryEncoderScrollEvent(@NonNull IBinder token,
+ @NonNull VirtualRotaryEncoderScrollEvent event) {
+ super.sendRotaryEncoderScrollEvent_enforcePermission();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mInputController.sendRotaryEncoderScrollEvent(token, event);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void setShowPointerIcon(boolean showPointerIcon) {
super.setShowPointerIcon_enforcePermission();
final long ident = Binder.clearCallingIdentity();
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 53730e317ac6..d9962c7b02d5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -239,6 +239,9 @@ java_library_static {
],
lint: {
baseline_filename: "lint-baseline.xml",
+ warning_checks: [
+ "FlaggedApi",
+ ],
},
}
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 44aea15b2bde..e2ab0d9f2683 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -1603,7 +1603,7 @@ public class SystemConfig {
} else {
mPackageToSharedUidAllowList.put(pkgName, sharedUid);
}
- }
+ } break;
case "asl-file": {
String packageName = parser.getAttributeValue(null, "package");
String path = parser.getAttributeValue(null, "path");
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6bd7445fdd50..0fa5260a2e89 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -127,6 +127,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_EXECUTING;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
import android.Manifest;
import android.annotation.IntDef;
@@ -4304,7 +4305,7 @@ public final class ActiveServices {
// queued up in the app side as they're one way calls. And we'll also hold off
// the service timeout timer until the process is unfrozen.
mAm.mOomAdjuster.updateAppFreezeStateLSP(callerApp, OOM_ADJ_REASON_BIND_SERVICE,
- true);
+ true, UNKNOWN_ADJ);
}
final boolean wasStopped = hostApp == null ? wasStopped(s) : false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 45ab62dde880..b23f5f23f647 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10015,8 +10015,9 @@ public class ActivityManagerService extends IActivityManager.Stub
if (crashInfo != null && crashInfo.stackTrace != null) {
sb.append(crashInfo.stackTrace);
}
- boolean shouldAddLogs = logcatLines > 0 || kernelLogLines > 0;
- if (!runSynchronously && shouldAddLogs) {
+ boolean shouldAddLogs = (logcatLines > 0 || kernelLogLines > 0)
+ && (Flags.collectLogcatOnRunSynchronously() || !runSynchronously);
+ if (shouldAddLogs) {
sb.append("\n");
if (logcatLines > 0) {
fetchLogcatBuffers(sb, logcatLines, LOGCAT_TIMEOUT_SEC,
@@ -10237,6 +10238,19 @@ public class ActivityManagerService extends IActivityManager.Stub
addStartInfoTimestampInternal(key, timestampNs, userId, callingUid);
}
+ @Override
+ public void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs,
+ long framePresentedTimeNs) {
+ int callingUid = Binder.getCallingUid();
+ int userId = UserHandle.getUserId(callingUid);
+ addStartInfoTimestampInternal(
+ ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME,
+ renderThreadDrawStartTimeNs, userId, callingUid);
+ addStartInfoTimestampInternal(
+ ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE,
+ framePresentedTimeNs, userId, callingUid);
+ }
+
private void addStartInfoTimestampInternal(int key, long timestampNs, int userId, int uid) {
mProcessList.getAppStartInfoTracker().addTimestampToStart(
Settings.getPackageNameForUid(mContext, uid),
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 3042b2a50d50..4a7ad31cba3e 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -1195,21 +1195,8 @@ public final class AppStartInfoTracker {
// Records are sorted newest to oldest, grab record at index 0.
ApplicationStartInfo startInfo = mInfos.get(0);
- int startupState = startInfo.getStartupState();
- // If startup state is error then don't accept any further timestamps.
- if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) {
- if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps.");
- return;
- }
-
- // If startup state is first frame drawn then only accept fully drawn timestamp.
- if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN
- && key != ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN) {
- if (DEBUG) {
- Slog.d(TAG, "Startup state is first frame drawn and timestamp is not fully "
- + "drawn, not accepting new timestamps.");
- }
+ if (!isAddTimestampAllowed(startInfo, key, timestampNs)) {
return;
}
@@ -1222,6 +1209,55 @@ public final class AppStartInfoTracker {
}
}
+ private boolean isAddTimestampAllowed(ApplicationStartInfo startInfo, int key,
+ long timestampNs) {
+ int startupState = startInfo.getStartupState();
+
+ // If startup state is error then don't accept any further timestamps.
+ if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) {
+ if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps.");
+ return false;
+ }
+
+ Map<Integer, Long> timestamps = startInfo.getStartupTimestamps();
+
+ if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) {
+ switch (key) {
+ case ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN:
+ // Allowed, continue to confirm it's not already added.
+ break;
+ case ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME:
+ Long firstFrameTimeNs = timestamps
+ .get(ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME);
+ if (firstFrameTimeNs == null) {
+ // This should never happen. State can't be first frame drawn if first
+ // frame timestamp was not provided.
+ return false;
+ }
+
+ if (timestampNs > firstFrameTimeNs) {
+ // Initial renderthread frame has to occur before first frame.
+ return false;
+ }
+
+ // Allowed, continue to confirm it's not already added.
+ break;
+ case ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE:
+ // Allowed, continue to confirm it's not already added.
+ break;
+ default:
+ return false;
+ }
+ }
+
+ if (timestamps.get(key) != null) {
+ // Timestamp should not occur more than once for a given start.
+ return false;
+ }
+
+ return true;
+ }
+
@GuardedBy("mLock")
void dumpLocked(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
if (mMonitoringModeEnabled) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 211f952551d9..6433f2c1c25d 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -83,6 +83,8 @@ import com.android.internal.os.ProcLocksReader;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ServiceThread;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
@@ -98,8 +100,6 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
-import dalvik.annotation.optimization.NeverCompile;
-
public final class CachedAppOptimizer {
// Flags stored in the DeviceConfig API.
@@ -248,7 +248,7 @@ public final class CachedAppOptimizer {
private static final int COMPACT_ACTION_ANON_FLAG = 2;
private static final String ATRACE_COMPACTION_TRACK = "Compaction";
- private static final String ATRACE_FREEZER_TRACK = "Freezer";
+ public static final String ATRACE_FREEZER_TRACK = "Freezer";
private static final int FREEZE_BINDER_TIMEOUT_MS = 0;
private static final int FREEZE_DEADLOCK_TIMEOUT_MS = 1000;
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c9e0666add42..b058bd8eb3d6 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1653,8 +1653,7 @@ public class OomAdjuster {
new ComputeOomAdjWindowCallback();
/** These methods are called inline during computeOomAdjLSP(), on the same thread */
- final class ComputeOomAdjWindowCallback
- implements WindowProcessController.ComputeOomAdjCallback {
+ final class ComputeOomAdjWindowCallback {
ProcessRecord app;
int adj;
@@ -1684,8 +1683,7 @@ public class OomAdjuster {
this.mState = app.mState;
}
- @Override
- public void onVisibleActivity() {
+ void onVisibleActivity(int flags) {
// App has a visible activity; only upgrade adjustment.
if (adj > VISIBLE_APP_ADJ) {
adj = VISIBLE_APP_ADJ;
@@ -1705,12 +1703,17 @@ public class OomAdjuster {
if (schedGroup < SCHED_GROUP_DEFAULT) {
schedGroup = SCHED_GROUP_DEFAULT;
}
+ if ((flags & WindowProcessController.ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN) != 0) {
+ // Another side of split should be the current global top. Use the same top
+ // priority for this non-top split.
+ schedGroup = SCHED_GROUP_TOP_APP;
+ mAdjType = "resumed-split-screen-activity";
+ }
foregroundActivities = true;
mHasVisibleActivities = true;
}
- @Override
- public void onPausedActivity() {
+ void onPausedActivity() {
if (adj > PERCEPTIBLE_APP_ADJ) {
adj = PERCEPTIBLE_APP_ADJ;
mAdjType = "pause-activity";
@@ -1733,8 +1736,7 @@ public class OomAdjuster {
mHasVisibleActivities = false;
}
- @Override
- public void onStoppingActivity(boolean finishing) {
+ void onStoppingActivity(boolean finishing) {
if (adj > PERCEPTIBLE_APP_ADJ) {
adj = PERCEPTIBLE_APP_ADJ;
mAdjType = "stop-activity";
@@ -1764,8 +1766,7 @@ public class OomAdjuster {
mHasVisibleActivities = false;
}
- @Override
- public void onOtherActivity() {
+ void onOtherActivity() {
if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
procState = PROCESS_STATE_CACHED_ACTIVITY;
mAdjType = "cch-act";
@@ -1832,7 +1833,8 @@ public class OomAdjuster {
state.setNoKillOnBgRestrictedAndIdle(false);
// If this UID is currently allowlisted, it should not be frozen.
final UidRecord uidRec = app.getUidRecord();
- app.mOptRecord.setShouldNotFreeze(uidRec != null && uidRec.isCurAllowListed());
+ app.mOptRecord.setShouldNotFreeze(uidRec != null && uidRec.isCurAllowListed(),
+ ProcessCachedOptimizerRecord.SHOULD_NOT_FREEZE_REASON_UID_ALLOWLISTED, mAdjSeq);
}
final int appUid = app.info.uid;
@@ -2672,7 +2674,9 @@ public class OomAdjuster {
if (client.mOptRecord.shouldNotFreeze()) {
// Propagate the shouldNotFreeze flag down the bindings.
- if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+ if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
+ app.mOptRecord.shouldNotFreezeReason()
+ | client.mOptRecord.shouldNotFreezeReason(), mAdjSeq)) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
@@ -2739,7 +2743,10 @@ public class OomAdjuster {
if (cr.hasFlag(Context.BIND_ALLOW_OOM_MANAGEMENT)) {
// Similar to BIND_WAIVE_PRIORITY, keep it unfrozen.
if (clientAdj < CACHED_APP_MIN_ADJ) {
- if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+ if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
+ app.mOptRecord.shouldNotFreezeReason()
+ | ProcessCachedOptimizerRecord
+ .SHOULD_NOT_FREEZE_REASON_BINDER_ALLOW_OOM_MANAGEMENT, mAdjSeq)) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
@@ -2976,7 +2983,10 @@ public class OomAdjuster {
// bound by an unfrozen app via a WPRI binding has to remain
// unfrozen.
if (clientAdj < CACHED_APP_MIN_ADJ) {
- if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+ if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
+ app.mOptRecord.shouldNotFreezeReason()
+ | ProcessCachedOptimizerRecord
+ .SHOULD_NOT_FREEZE_REASON_BIND_WAIVE_PRIORITY, mAdjSeq)) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
@@ -3117,7 +3127,9 @@ public class OomAdjuster {
}
if (client.mOptRecord.shouldNotFreeze()) {
// Propagate the shouldNotFreeze flag down the bindings.
- if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+ if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
+ app.mOptRecord.shouldNotFreezeReason()
+ | client.mOptRecord.shouldNotFreezeReason(), mAdjSeq)) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
@@ -3476,7 +3488,7 @@ public class OomAdjuster {
changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES;
}
- updateAppFreezeStateLSP(app, oomAdjReson, false);
+ updateAppFreezeStateLSP(app, oomAdjReson, false, oldOomAdj);
if (state.getReportedProcState() != state.getCurProcState()) {
state.setReportedProcState(state.getCurProcState());
@@ -3873,16 +3885,37 @@ public class OomAdjuster {
@GuardedBy({"mService", "mProcLock"})
void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason,
- boolean immediate) {
+ boolean immediate, int oldOomAdj) {
if (!mCachedAppOptimizer.useFreezer()) {
return;
}
+ final ProcessCachedOptimizerRecord opt = app.mOptRecord;
+ final ProcessStateRecord state = app.mState;
+ if (Flags.traceUpdateAppFreezeStateLsp()) {
+ final boolean oomAdjChanged =
+ (state.getCurAdj() >= FREEZER_CUTOFF_ADJ ^ oldOomAdj >= FREEZER_CUTOFF_ADJ)
+ || oldOomAdj == UNKNOWN_ADJ;
+ final boolean shouldNotFreezeChanged = opt.shouldNotFreezeAdjSeq() == mAdjSeq;
+ if ((oomAdjChanged || shouldNotFreezeChanged)
+ && Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ CachedAppOptimizer.ATRACE_FREEZER_TRACK,
+ "updateAppFreezeStateLSP " + app.processName
+ + " isFreezeExempt: " + opt.isFreezeExempt()
+ + " isFrozen: " + opt.isFrozen()
+ + " shouldNotFreeze: " + opt.shouldNotFreeze()
+ + " shouldNotFreezeReason: " + opt.shouldNotFreezeReason()
+ + " curAdj: " + state.getCurAdj()
+ + " oldOomAdj: " + oldOomAdj
+ + " immediate: " + immediate);
+ }
+ }
+
if (app.mOptRecord.isFreezeExempt()) {
return;
}
- final ProcessCachedOptimizerRecord opt = app.mOptRecord;
// if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze
if (opt.isFrozen() && opt.shouldNotFreeze()) {
mCachedAppOptimizer.unfreezeAppLSP(app,
@@ -3890,7 +3923,6 @@ public class OomAdjuster {
return;
}
- final ProcessStateRecord state = app.mState;
// Use current adjustment when freezing, set adjustment when unfreezing.
if (state.getCurAdj() >= FREEZER_CUTOFF_ADJ && !opt.isFrozen()
&& !opt.shouldNotFreeze()) {
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index ea925716b864..53643b867b9e 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.annotation.IntDef;
import android.annotation.UptimeMillisLong;
import android.app.ActivityManagerInternal.OomAdjReason;
import android.util.TimeUtils;
@@ -23,14 +24,31 @@ import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import java.io.PrintWriter;
-
import dalvik.annotation.optimization.NeverCompile;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* The state info of app when it's cached, used by the optimizer.
*/
final class ProcessCachedOptimizerRecord {
+
+ static final int SHOULD_NOT_FREEZE_REASON_NONE = 1;
+ static final int SHOULD_NOT_FREEZE_REASON_UID_ALLOWLISTED = 1 << 1;
+ static final int SHOULD_NOT_FREEZE_REASON_BINDER_ALLOW_OOM_MANAGEMENT = 1 << 2;
+ static final int SHOULD_NOT_FREEZE_REASON_BIND_WAIVE_PRIORITY = 1 << 3;
+
+ @IntDef(flag = true, prefix = {"SHOULD_NOT_FREEZE_REASON_"}, value = {
+ SHOULD_NOT_FREEZE_REASON_NONE,
+ SHOULD_NOT_FREEZE_REASON_UID_ALLOWLISTED,
+ SHOULD_NOT_FREEZE_REASON_BINDER_ALLOW_OOM_MANAGEMENT,
+ SHOULD_NOT_FREEZE_REASON_BIND_WAIVE_PRIORITY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ShouldNotFreezeReason {}
+
private final ProcessRecord mApp;
private final ActivityManagerGlobalLock mProcLock;
@@ -112,6 +130,18 @@ final class ProcessCachedOptimizerRecord {
private boolean mShouldNotFreeze;
/**
+ * Reason for mShouldNotFreeze being set to a particular value.
+ */
+ @GuardedBy("mProcLock")
+ private @ShouldNotFreezeReason int mShouldNotFreezeReason;
+
+ /**
+ * The value of adjSeq when last time mShouldNotFreeze was set.
+ */
+ @GuardedBy("mProcLock")
+ private int mShouldNotFreezeAdjSeq;
+
+ /**
* Exempt from freezer (now for system apps with INSTALL_PACKAGES permission)
*/
@GuardedBy("mProcLock")
@@ -275,8 +305,19 @@ final class ProcessCachedOptimizerRecord {
}
@GuardedBy("mProcLock")
- void setShouldNotFreeze(boolean shouldNotFreeze) {
- setShouldNotFreeze(shouldNotFreeze, false);
+ @ShouldNotFreezeReason int shouldNotFreezeReason() {
+ return mShouldNotFreezeReason;
+ }
+
+ @GuardedBy("mProcLock")
+ int shouldNotFreezeAdjSeq() {
+ return mShouldNotFreezeAdjSeq;
+ }
+
+ @GuardedBy("mProcLock")
+ void setShouldNotFreeze(boolean shouldNotFreeze, @ShouldNotFreezeReason int reason,
+ int adjSeq) {
+ setShouldNotFreeze(shouldNotFreeze, false, reason, adjSeq);
}
/**
@@ -284,10 +325,23 @@ final class ProcessCachedOptimizerRecord {
* if it was a real run.
*/
@GuardedBy("mProcLock")
- boolean setShouldNotFreeze(boolean shouldNotFreeze, boolean dryRun) {
+ boolean setShouldNotFreeze(boolean shouldNotFreeze, boolean dryRun,
+ @ShouldNotFreezeReason int reason, int adjSeq) {
if (dryRun) {
return mFrozen && !shouldNotFreeze;
}
+ if (Flags.traceUpdateAppFreezeStateLsp()) {
+ if (shouldNotFreeze) {
+ reason &= ~SHOULD_NOT_FREEZE_REASON_NONE;
+ } else {
+ reason = SHOULD_NOT_FREEZE_REASON_NONE;
+ }
+
+ if (reason != mShouldNotFreezeReason || shouldNotFreeze != mShouldNotFreeze) {
+ mShouldNotFreezeAdjSeq = adjSeq;
+ }
+ mShouldNotFreezeReason = reason;
+ }
mShouldNotFreeze = shouldNotFreeze;
return false;
}
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 7c64298a6053..bc990d9c5ef9 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -27,6 +27,11 @@ import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_STARTED_SERV
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
import static com.android.server.am.ProcessRecord.TAG;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
import android.annotation.ElapsedRealtimeLong;
import android.app.ActivityManager;
@@ -1108,6 +1113,7 @@ final class ProcessStateRecord {
return mCachedCompatChanges[cachedCompatChangeId] == VALUE_TRUE;
}
+ /** This is only called if the process contains activities and is not the global top. */
@GuardedBy("mService")
void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback,
int adj, boolean foregroundActivities, boolean hasVisibleActivities, int procState,
@@ -1117,8 +1123,17 @@ final class ProcessStateRecord {
}
callback.initialize(mApp, adj, foregroundActivities, hasVisibleActivities, procState,
schedGroup, appUid, logUid, processCurTop);
- final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX,
- mApp.getWindowProcessController().computeOomAdjFromActivities(callback));
+ final int flags = mApp.getWindowProcessController().getActivityStateFlags();
+
+ if ((flags & ACTIVITY_STATE_FLAG_IS_VISIBLE) != 0) {
+ callback.onVisibleActivity(flags);
+ } else if ((flags & ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED) != 0) {
+ callback.onPausedActivity();
+ } else if ((flags & ACTIVITY_STATE_FLAG_IS_STOPPING) != 0) {
+ callback.onStoppingActivity((flags & ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING) != 0);
+ } else {
+ callback.onOtherActivity();
+ }
mCachedAdj = callback.adj;
mCachedForegroundActivities = callback.foregroundActivities;
@@ -1128,6 +1143,8 @@ final class ProcessStateRecord {
mCachedAdjType = callback.mAdjType;
if (mCachedAdj == ProcessList.VISIBLE_APP_ADJ) {
+ final int taskLayer = flags & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
+ final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX, taskLayer);
mCachedAdj += minLayer;
}
}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index bb52857a99c4..0dfa3302adfd 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -125,6 +125,14 @@ flag {
}
flag {
+ name: "trace_update_app_freeze_state_lsp"
+ namespace: "backstage_power"
+ description: "Output process oom adj and other metadata to trace in updateAppFreezeStateLSP"
+ bug: "345547733"
+ is_fixed_read_only: true
+}
+
+flag {
name: "simplify_process_traversal"
namespace: "backstage_power"
description: "Simplify the OomAdjuster's process traversal mechanism."
@@ -152,3 +160,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "collect_logcat_on_run_synchronously"
+ namespace: "dropbox"
+ description: "Allow logcat collection on synchronous dropbox collection"
+ bug: "324222683"
+ is_fixed_read_only: true
+} \ 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 6d1983e75388..c89992d6b57c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -11698,7 +11698,7 @@ public class AudioService extends IAudioService.Stub
static final int LOG_NB_EVENTS_VOLUME = 100;
static final int LOG_NB_EVENTS_DYN_POLICY = 10;
static final int LOG_NB_EVENTS_SPATIAL = 30;
- static final int LOG_NB_EVENTS_SOUND_DOSE = 30;
+ static final int LOG_NB_EVENTS_SOUND_DOSE = 50;
static final int LOG_NB_EVENTS_LOUDNESS_CODEC = 30;
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 749044e05cbc..8ea28bed30a2 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -577,6 +577,7 @@ public class AudioServiceEvents {
static final int DOSE_REPEAT_5X = 2;
static final int DOSE_ACCUMULATION_START = 3;
static final int LOWER_VOLUME_TO_RS1 = 4;
+ static final int UPDATE_ABS_VOLUME_ATTENUATION = 5;
final int mEventType;
final float mFloatValue;
@@ -608,6 +609,10 @@ public class AudioServiceEvents {
return new SoundDoseEvent(LOWER_VOLUME_TO_RS1, 0 /*ignored*/, 0 /*ignored*/);
}
+ static SoundDoseEvent getAbsVolumeAttenuationEvent(float attenuation, int device) {
+ return new SoundDoseEvent(UPDATE_ABS_VOLUME_ATTENUATION, attenuation, device);
+ }
+
@Override
public String eventToString() {
switch (mEventType) {
@@ -623,6 +628,10 @@ public class AudioServiceEvents {
return "CSD accumulating: RS2 entered";
case LOWER_VOLUME_TO_RS1:
return "CSD lowering volume to RS1";
+ case UPDATE_ABS_VOLUME_ATTENUATION:
+ return String.format(java.util.Locale.US,
+ "Updating CSD absolute volume attenuation on device %d with %.2f dB ",
+ mLongValue, mFloatValue);
}
return new StringBuilder("FIXME invalid event type:").append(mEventType).toString();
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index d26ba489a350..88268cd19a38 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -662,8 +662,7 @@ public final class PlaybackActivityMonitor
synchronized(mPlayerLock) {
pw.println("\n playback listeners:");
for (PlayMonitorClient pmc : mClients) {
- pw.print(" " + (pmc.isPrivileged() ? "(S)" : "(P)")
- + pmc.toString());
+ pw.println(" " + pmc);
}
pw.println("\n");
// all players
@@ -1031,7 +1030,8 @@ public final class PlaybackActivityMonitor
if (pcdb == null) {
return;
}
- final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
+ final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged,
+ Binder.getCallingUid(), Binder.getCallingPid());
if (pmc.init()) {
mClients.add(pmc);
}
@@ -1103,10 +1103,22 @@ public final class PlaybackActivityMonitor
private boolean mIsReleased = false;
@GuardedBy("this")
private int mErrorCount = 0;
+ private final int mUid;
+ private final int mPid;
- PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
+ PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged,
+ int uid, int pid) {
mDispatcherCb = pcdb;
mIsPrivileged = isPrivileged;
+ mUid = uid;
+ mPid = pid;
+ }
+
+ @Override
+ public String toString() {
+ return "PlayMonitorClient:"
+ + (isPrivileged() ? "S" : "P")
+ + " uid:" + mUid + " pid:" + mPid;
}
@Override
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index e28ae952e65a..5c74304cabd5 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -39,6 +39,7 @@ import android.media.AudioSystem;
import android.media.ISoundDose;
import android.media.ISoundDoseCallback;
import android.media.SoundDoseRecord;
+import android.media.VolumeInfo;
import android.os.Binder;
import android.os.Message;
import android.os.RemoteException;
@@ -895,6 +896,8 @@ public class SoundDoseHelper {
try {
if (!isAbsoluteVolume) {
+ mLogger.enqueue(
+ SoundDoseEvent.getAbsVolumeAttenuationEvent(/*attenuation=*/0.f, device));
// remove any possible previous attenuation
soundDose.updateAttenuation(/* attenuationDB= */0.f, device);
@@ -903,10 +906,11 @@ public class SoundDoseHelper {
if (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC
&& safeDevicesContains(device)) {
- soundDose.updateAttenuation(
- -AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
- (newIndex + 5) / 10,
- device), device);
+ float attenuationDb = -AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
+ (newIndex + 5) / 10, device);
+ mLogger.enqueue(
+ SoundDoseEvent.getAbsVolumeAttenuationEvent(attenuationDb, device));
+ soundDose.updateAttenuation(attenuationDb, device);
}
} catch (RemoteException e) {
Log.e(TAG, "Could not apply the attenuation for MEL calculation with volume index "
@@ -1313,22 +1317,30 @@ public class SoundDoseHelper {
/** Called when handling MSG_LOWER_VOLUME_TO_RS1 */
private void onLowerVolumeToRs1() {
- mLogger.enqueue(SoundDoseEvent.getLowerVolumeToRs1Event());
final ArrayList<AudioDeviceAttributes> devices = mAudioService.getDevicesForAttributesInt(
- new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(), true);
- final int nativeDeviceType;
- final AudioDeviceAttributes ada;
- if (!devices.isEmpty()) {
- ada = devices.get(0);
- nativeDeviceType = ada.getInternalType();
+ new AudioAttributes.Builder().setUsage(
+ AudioAttributes.USAGE_MEDIA).build(), /*forVolume=*/true);
+ if (devices.isEmpty()) {
+ Log.e(TAG, "Cannot lower the volume to RS1, no devices registered for USAGE_MEDIA");
+ return;
+ }
+ final AudioDeviceAttributes ada = devices.get(0);
+ final int nativeDeviceType = ada.getInternalType();
+ final int index = safeMediaVolumeIndex(nativeDeviceType) / 10;
+ final VolumeInfo curVolume = mAudioService.getDeviceVolume(
+ new VolumeInfo.Builder(STREAM_MUSIC).build(), ada,
+ /*callingPackage=*/"sounddosehelper");
+
+ if (index < curVolume.getVolumeIndex()) {
+ mLogger.enqueue(SoundDoseEvent.getLowerVolumeToRs1Event());
+ mAudioService.setStreamVolumeWithAttributionInt(STREAM_MUSIC, index, /*flags*/ 0, ada,
+ mContext.getOpPackageName(), /*attributionTag=*/null,
+ /*canChangeMuteAndUpdateController=*/true);
} else {
- nativeDeviceType = AudioSystem.DEVICE_OUT_USB_HEADSET;
- ada = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_USB_HEADSET, "");
+ Log.i(TAG, "The current volume " + curVolume.getVolumeIndex()
+ + " for device type " + nativeDeviceType
+ + " is already smaller or equal to the safe index volume " + index);
}
- final int index = safeMediaVolumeIndex(nativeDeviceType);
- mAudioService.setStreamVolumeWithAttributionInt(STREAM_MUSIC, index / 10, /*flags*/ 0, ada,
- mContext.getOpPackageName(), /*attributionTag=*/null,
- true /*canChangeMuteAndUpdateController*/);
}
// StreamVolumeCommand contains the information needed to defer the process of
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index 9467d6f6fd92..a3c68f9dc827 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -590,15 +590,9 @@ final class ConversionUtils {
|| isVendorIdentifierType(id.type);
}
- private static boolean isValidHalProgramInfo(ProgramInfo info) {
- return isValidHalProgramSelector(info.selector)
- && isValidLogicallyTunedTo(info.logicallyTunedTo)
- && isValidPhysicallyTunedTo(info.physicallyTunedTo);
- }
-
@Nullable
static RadioManager.ProgramInfo programInfoFromHalProgramInfo(ProgramInfo info) {
- if (!isValidHalProgramInfo(info)) {
+ if (!isValidHalProgramSelector(info.selector)) {
return null;
}
Collection<ProgramSelector.Identifier> relatedContent = new ArrayList<>();
@@ -624,6 +618,15 @@ final class ConversionUtils {
);
}
+ @Nullable
+ static RadioManager.ProgramInfo tunedProgramInfoFromHalProgramInfo(ProgramInfo info) {
+ if (!isValidLogicallyTunedTo(info.logicallyTunedTo)
+ || !isValidPhysicallyTunedTo(info.physicallyTunedTo)) {
+ return null;
+ }
+ return programInfoFromHalProgramInfo(info);
+ }
+
static ProgramFilter filterToHalProgramFilter(@Nullable ProgramList.Filter filter) {
if (filter == null) {
filter = new ProgramList.Filter();
@@ -686,8 +689,10 @@ final class ConversionUtils {
if (!programSelectorMeetsSdkVersionRequirement(info.getSelector(), uid)) {
return false;
}
- if (!identifierMeetsSdkVersionRequirement(info.getLogicallyTunedTo(), uid)
- || !identifierMeetsSdkVersionRequirement(info.getPhysicallyTunedTo(), uid)) {
+ if ((info.getLogicallyTunedTo() != null
+ && !identifierMeetsSdkVersionRequirement(info.getLogicallyTunedTo(), uid))
+ || (info.getPhysicallyTunedTo() != null
+ && !identifierMeetsSdkVersionRequirement(info.getPhysicallyTunedTo(), uid))) {
return false;
}
Iterator<ProgramSelector.Identifier> relatedContentIt = info.getRelatedContent().iterator();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index 03e347a903b5..4edd4417c0aa 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -121,7 +121,7 @@ final class RadioModule {
public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
fireLater(() -> {
RadioManager.ProgramInfo currentProgramInfo =
- ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
+ ConversionUtils.tunedProgramInfoFromHalProgramInfo(halProgramInfo);
Objects.requireNonNull(currentProgramInfo,
"Program info from AIDL HAL is invalid");
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
index 651828b6b9e2..7d26004b15c5 100644
--- a/services/core/java/com/android/server/display/BrightnessSetting.java
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -131,6 +131,25 @@ public class BrightnessSetting {
}
/**
+ * Sets the brightness. Does not send update event to listeners.
+ * @param brightness The value to which the brightness is to be set.
+ */
+ public void setBrightnessNoNotify(float brightness) {
+ if (Float.isNaN(brightness)) {
+ Slog.w(TAG, "Attempting to init invalid brightness");
+ return;
+ }
+ synchronized (mSyncRoot) {
+ if (brightness != mBrightness) {
+ mPersistentDataStore.setBrightness(mLogicalDisplay.getPrimaryDisplayDeviceLocked(),
+ brightness, mUserSerial
+ );
+ }
+ mBrightness = brightness;
+ }
+ }
+
+ /**
* @return The brightness for the default display in nits. Used when the underlying display
* device has changed but we want to persist the nit value.
*/
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c298bbf5a551..de9715ac812d 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1567,7 +1567,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// even if they range changes what it means in absolute terms.
mDisplayBrightnessController.updateScreenBrightnessSetting(
MathUtils.constrain(unthrottledBrightnessState,
- clampedState.getMinBrightness(), clampedState.getMaxBrightness()));
+ clampedState.getMinBrightness(), clampedState.getMaxBrightness()),
+ Math.min(mBrightnessRangeController.getCurrentBrightnessMax(),
+ clampedState.getMaxBrightness()));
}
// The current brightness to use has been calculated at this point, and HbmController should
@@ -2471,12 +2473,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Override
public void setBrightness(float brightness) {
- mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness));
+ // After HBMController and NBMController migration to Clampers framework
+ // currentBrightnessMax should be taken from clampers controller
+ // TODO(b/263362199)
+ mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness),
+ mBrightnessRangeController.getCurrentBrightnessMax());
}
@Override
public void setBrightness(float brightness, int userSerial) {
- mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness), userSerial);
+ // After HBMController and NBMController migration to Clampers framework
+ // currentBrightnessMax should be taken from clampers controller
+ // TODO(b/263362199)
+ mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness), userSerial,
+ mBrightnessRangeController.getCurrentBrightnessMax());
}
@Override
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 44846f310348..6e0b9cf2f234 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -49,6 +49,7 @@ import android.view.RoundedCorners;
import android.view.SurfaceControl;
import com.android.internal.R;
+import com.android.internal.annotations.KeepForWeakReference;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -1442,8 +1443,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
public static class Injector {
- // Native callback.
+ // Ensure the callback is kept to preserve native weak reference lifecycle semantics.
@SuppressWarnings("unused")
+ @KeepForWeakReference
private ProxyDisplayEventReceiver mReceiver;
public void setDisplayEventListenerLocked(Looper looper, DisplayEventListener listener) {
mReceiver = new ProxyDisplayEventReceiver(looper, listener);
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index 4982a0b0b836..c632e77fe685 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -313,13 +313,18 @@ public final class DisplayBrightnessController {
/**
* Notifies the brightnessSetting to persist the supplied brightness value.
*/
- public void setBrightness(float brightnessValue) {
+ public void setBrightness(float brightnessValue, float maxBrightness) {
// Update the setting, which will eventually call back into DPC to have us actually
// update the display with the new value.
mBrightnessSetting.setBrightness(brightnessValue);
if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
float nits = convertToNits(brightnessValue);
- if (nits >= 0) {
+ float currentlyStoredNits = mBrightnessSetting.getBrightnessNitsForDefaultDisplay();
+ // Don't override settings if the brightness is set to max, but the currently
+ // stored value is greater. On multi-screen device, when switching between a
+ // screen with a wider brightness range and one with a narrower brightness range,
+ // the stored value shouldn't change.
+ if (nits >= 0 && !(brightnessValue == maxBrightness && currentlyStoredNits > nits)) {
mBrightnessSetting.setBrightnessNitsForDefaultDisplay(nits);
}
}
@@ -328,15 +333,15 @@ public final class DisplayBrightnessController {
/**
* Notifies the brightnessSetting to persist the supplied brightness value for a user.
*/
- public void setBrightness(float brightnessValue, int userSerial) {
+ public void setBrightness(float brightnessValue, int userSerial, float maxBrightness) {
mBrightnessSetting.setUserSerial(userSerial);
- setBrightness(brightnessValue);
+ setBrightness(brightnessValue, maxBrightness);
}
/**
* Sets the current screen brightness, and notifies the BrightnessSetting about the change.
*/
- public void updateScreenBrightnessSetting(float brightnessValue) {
+ public void updateScreenBrightnessSetting(float brightnessValue, float maxBrightness) {
synchronized (mLock) {
if (!BrightnessUtils.isValidBrightnessValue(brightnessValue)
|| brightnessValue == mCurrentScreenBrightness) {
@@ -345,7 +350,7 @@ public final class DisplayBrightnessController {
setCurrentScreenBrightnessLocked(brightnessValue);
}
notifyCurrentScreenBrightness();
- setBrightness(brightnessValue);
+ setBrightness(brightnessValue, maxBrightness);
}
/**
@@ -582,7 +587,7 @@ public final class DisplayBrightnessController {
float brightnessForDefaultDisplay = getBrightnessFromNits(
brightnessNitsForDefaultDisplay);
if (BrightnessUtils.isValidBrightnessValue(brightnessForDefaultDisplay)) {
- mBrightnessSetting.setBrightness(brightnessForDefaultDisplay);
+ mBrightnessSetting.setBrightnessNoNotify(brightnessForDefaultDisplay);
currentBrightnessSetting = brightnessForDefaultDisplay;
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 6e027c6d44c4..bbe7b2b038c9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -144,6 +144,10 @@ public class HdmiControlService extends SystemService {
private static final String TAG = "HdmiControlService";
private static final Locale HONG_KONG = new Locale("zh", "HK");
private static final Locale MACAU = new Locale("zh", "MO");
+ private static final String TAIWAN_HantLanguageTag = "zh-Hant-TW";
+ private static final String HONG_KONG_HantLanguageTag = "zh-Hant-HK";
+ private static final String HONG_KONG_YUE_HantLanguageTag = "yue-Hant-HK";
+ private static final String MACAU_HantLanguageTag = "zh-Hant-MO";
private static final Map<String, String> sTerminologyToBibliographicMap =
createsTerminologyToBibliographicMap();
@@ -174,7 +178,11 @@ public class HdmiControlService extends SystemService {
}
@VisibleForTesting static String localeToMenuLanguage(Locale locale) {
- if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) {
+ if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU) ||
+ locale.toLanguageTag().equals(TAIWAN_HantLanguageTag) ||
+ locale.toLanguageTag().equals(HONG_KONG_HantLanguageTag) ||
+ locale.toLanguageTag().equals(HONG_KONG_YUE_HantLanguageTag) ||
+ locale.toLanguageTag().equals(MACAU_HantLanguageTag)) {
// Android always returns "zho" for all Chinese variants.
// Use "bibliographic" code defined in CEC639-2 for traditional
// Chinese used in Taiwan/Hong Kong/Macau.
diff --git a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
index a6b07dedfa4a..41313fa1fb2c 100644
--- a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
+++ b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
@@ -33,8 +33,7 @@ final class HardwareKeyboardShortcutController {
@GuardedBy("ImfLock.class")
private final ArrayList<InputMethodSubtypeHandle> mSubtypeHandles = new ArrayList<>();
- HardwareKeyboardShortcutController(@NonNull InputMethodSettings settings) {
- update(settings);
+ HardwareKeyboardShortcutController() {
}
@GuardedBy("ImfLock.class")
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 440343d1d0f4..2d7761026239 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -377,11 +377,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
@SharedByAllUsersField
private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>();
- // TODO: Instantiate mSwitchingController for each user.
- @NonNull
- @MultiUserUnawareField
- private InputMethodSubtypeSwitchingController mSwitchingController;
-
@Nullable
private StatusBarManagerInternal mStatusBarManagerInternal;
@SharedByAllUsersField
@@ -1295,9 +1290,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
}
final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final var userData = getUserData(mCurrentUserId);
+ userData.mSwitchingController.resetCircularListLocked(mContext, settings);
+ userData.mHardwareKeyboardShortcutController.update(settings);
- mSwitchingController = new InputMethodSubtypeSwitchingController(context, settings);
- getUserData(mCurrentUserId).mHardwareKeyboardShortcutController.update(settings);
mMenuController = new InputMethodMenuController(this);
mVisibilityStateComputer = new ImeVisibilityStateComputer(this);
mVisibilityApplier = new DefaultImeVisibilityApplier(this);
@@ -2928,7 +2924,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
* {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated.
* </li>
* <li>{@link InputMethodBindingController#getDeviceIdToShowIme()} is ignored.</li>
- * <li>{@link #mSwitchingController} is ignored.</li>
* <li>{@link #mPreventImeStartupUnlessTextEditor} is ignored.</li>
* <li>and so on.</li>
* </ul>
@@ -2963,6 +2958,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
}
final var userData = getUserData(userId);
+ userData.mSwitchingController.resetCircularListLocked(mContext, settings);
userData.mHardwareKeyboardShortcutController.update(settings);
}
@@ -3039,13 +3035,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
resetCurrentMethodAndClientLocked(UnbindReason.NO_IME);
}
- // TODO: Instantiate mSwitchingController for each user.
- if (userId == mSwitchingController.getUserId()) {
- mSwitchingController.resetCircularListLocked(settings);
- } else {
- mSwitchingController = new InputMethodSubtypeSwitchingController(mContext, settings);
- }
- getUserData(userId).mHardwareKeyboardShortcutController.update(settings);
+ final var userData = getUserData(userId);
+ userData.mSwitchingController.resetCircularListLocked(mContext, settings);
+ userData.mHardwareKeyboardShortcutController.update(settings);
sendOnNavButtonFlagsChangedLocked();
}
@@ -4188,8 +4180,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
final int userId = mCurrentUserId;
final var bindingController = getInputMethodBindingController(userId);
final var currentImi = bindingController.getSelectedMethod();
- final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- onlyCurrentIme, currentImi, bindingController.getCurrentSubtype());
+ final ImeSubtypeListItem nextSubtype = getUserData(userId).mSwitchingController
+ .getNextInputMethodLocked(onlyCurrentIme, currentImi,
+ bindingController.getCurrentSubtype());
if (nextSubtype == null) {
return false;
}
@@ -4207,8 +4200,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
final int userId = mCurrentUserId;
final var bindingController = getInputMethodBindingController(userId);
final var currentImi = bindingController.getSelectedMethod();
- final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- false /* onlyCurrentIme */, currentImi, bindingController.getCurrentSubtype());
+ final ImeSubtypeListItem nextSubtype = getUserData(userId).mSwitchingController
+ .getNextInputMethodLocked(false /* onlyCurrentIme */, currentImi,
+ bindingController.getCurrentSubtype());
return nextSubtype != null;
}
}
@@ -4641,13 +4635,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
return;
}
final int userId = mCurrentUserId;
- if (userId != mSwitchingController.getUserId()) {
- return;
- }
- final var imi = getInputMethodBindingController(userId).getSelectedMethod();
+ final var bindingController = getInputMethodBindingController(userId);
+ final InputMethodInfo imi = bindingController.getSelectedMethod();
if (imi != null) {
- mSwitchingController.onUserActionLocked(imi,
- getInputMethodBindingController(userId).getCurrentSubtype());
+ getUserData(userId).mSwitchingController.onUserActionLocked(imi,
+ bindingController.getCurrentSubtype());
}
}
}
@@ -5308,13 +5300,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
updateDefaultVoiceImeIfNeededLocked();
- // TODO: Instantiate mSwitchingController for each user.
- if (userId == mSwitchingController.getUserId()) {
- mSwitchingController.resetCircularListLocked(settings);
- } else {
- mSwitchingController = new InputMethodSubtypeSwitchingController(mContext, settings);
- }
- getUserData(userId).mHardwareKeyboardShortcutController.update(settings);
+ final var userData = getUserData(userId);
+ userData.mSwitchingController.resetCircularListLocked(mContext, settings);
+ userData.mHardwareKeyboardShortcutController.update(settings);
sendOnNavButtonFlagsChangedLocked();
@@ -6115,6 +6103,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
p.println(" hasMainConnection="
+ u.mBindingController.hasMainConnection());
p.println(" isVisibleBound=" + u.mBindingController.isVisibleBound());
+ p.println(" mSwitchingController:");
+ u.mSwitchingController.dump(p, " ");
};
mUserDataRepository.forAllUserData(userDataDump);
@@ -6135,8 +6125,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
p.println(" mSettingsObserver=" + mSettingsObserver);
p.println(" mStylusIds=" + (mStylusIds != null
? Arrays.toString(mStylusIds.toArray()) : ""));
- p.println(" mSwitchingController:");
- mSwitchingController.dump(p, " ");
p.println(" mStartInputHistory:");
mStartInputHistory.dump(pw, " ");
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index f97a5165ebec..bb1b9df6cf4c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -16,10 +16,8 @@
package com.android.server.inputmethod;
-import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.content.Context;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -479,26 +477,10 @@ final class InputMethodSubtypeSwitchingController {
}
@NonNull
- private final Context mContext;
- @UserIdInt
- private final int mUserId;
- @NonNull
private ControllerImpl mController;
- InputMethodSubtypeSwitchingController(@NonNull Context context,
- @NonNull InputMethodSettings settings) {
- mContext = context;
- mUserId = settings.getUserId();
- mController = ControllerImpl.createFrom(null,
- getSortedInputMethodAndSubtypeList(
- false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
- false /* forImeMenu */, context, settings));
- }
-
- @AnyThread
- @UserIdInt
- int getUserId() {
- return mUserId;
+ InputMethodSubtypeSwitchingController() {
+ mController = ControllerImpl.createFrom(null, Collections.emptyList());
}
public void onUserActionLocked(@NonNull InputMethodInfo imi,
@@ -506,11 +488,12 @@ final class InputMethodSubtypeSwitchingController {
mController.onUserActionLocked(imi, subtype);
}
- public void resetCircularListLocked(@NonNull InputMethodSettings settings) {
+ public void resetCircularListLocked(@NonNull Context context,
+ @NonNull InputMethodSettings settings) {
mController = ControllerImpl.createFrom(mController,
getSortedInputMethodAndSubtypeList(
false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
- false /* forImeMenu */, mContext, settings));
+ false /* forImeMenu */, context, settings));
}
@Nullable
diff --git a/services/core/java/com/android/server/inputmethod/UserDataRepository.java b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
index 5da4e89c88ee..3da4a14b10be 100644
--- a/services/core/java/com/android/server/inputmethod/UserDataRepository.java
+++ b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
@@ -53,7 +53,8 @@ final class UserDataRepository {
}
}
- UserDataRepository(@NonNull Handler handler, @NonNull UserManagerInternal userManagerInternal,
+ UserDataRepository(
+ @NonNull Handler handler, @NonNull UserManagerInternal userManagerInternal,
@NonNull IntFunction<InputMethodBindingController> bindingControllerFactory) {
mBindingControllerFactory = bindingControllerFactory;
userManagerInternal.addUserLifecycleListener(
@@ -89,6 +90,9 @@ final class UserDataRepository {
final InputMethodBindingController mBindingController;
@NonNull
+ final InputMethodSubtypeSwitchingController mSwitchingController;
+
+ @NonNull
final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController;
/**
@@ -98,8 +102,8 @@ final class UserDataRepository {
@NonNull InputMethodBindingController bindingController) {
mUserId = userId;
mBindingController = bindingController;
- mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
- InputMethodSettings.createEmptyMap(userId));
+ mSwitchingController = new InputMethodSubtypeSwitchingController();
+ mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController();
}
@Override
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 363a4a7be3db..91a4d6f1707f 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -1126,7 +1126,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
}
}
- private void sendHostEndpointConnectedEvent() {
+ void sendHostEndpointConnectedEvent() {
HostEndpointInfo info = new HostEndpointInfo();
info.hostEndpointId = (char) mHostEndPointId;
info.packageName = mPackage;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index b3fb147a318b..7a722bc914f7 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -74,6 +74,7 @@ import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -158,10 +159,8 @@ public class ContextHubService extends IContextHubService.Stub {
// A queue of reliable message records for duplicate detection
private final PriorityQueue<ReliableMessageRecord> mReliableMessageRecordQueue =
- new PriorityQueue<ReliableMessageRecord>(
- (ReliableMessageRecord left, ReliableMessageRecord right) -> {
- return Long.compare(left.getTimestamp(), right.getTimestamp());
- });
+ new PriorityQueue<>(
+ Comparator.comparingLong(ReliableMessageRecord::getTimestamp));
// The test mode manager that manages behaviors during test mode.
private final TestModeManager mTestModeManager = new TestModeManager();
@@ -179,10 +178,10 @@ public class ContextHubService extends IContextHubService.Stub {
private boolean mIsBtMainEnabled = false;
// True if test mode is enabled for the Context Hub
- private AtomicBoolean mIsTestModeEnabled = new AtomicBoolean(false);
+ private final AtomicBoolean mIsTestModeEnabled = new AtomicBoolean(false);
// A hashmap used to record if a contexthub is waiting for daily query
- private Set<Integer> mMetricQueryPendingContextHubIds =
+ private final Set<Integer> mMetricQueryPendingContextHubIds =
Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
// Lock object for sendWifiSettingUpdate()
@@ -242,10 +241,14 @@ public class ContextHubService extends IContextHubService.Stub {
@Override
public void handleServiceRestart() {
- Log.i(TAG, "Starting Context Hub Service restart");
+ Log.i(TAG, "Recovering from Context Hub HAL restart...");
initExistingCallbacks();
resetSettings();
- Log.i(TAG, "Finished Context Hub Service restart");
+ if (Flags.reconnectHostEndpointsAfterHalRestart()) {
+ mClientManager.forEachClientOfHub(mContextHubId,
+ ContextHubClientBroker::sendHostEndpointConnectedEvent);
+ }
+ Log.i(TAG, "Finished recovering from Context Hub HAL restart");
}
@Override
@@ -317,11 +320,11 @@ public class ContextHubService extends IContextHubService.Stub {
*/
private static final int MAX_PROBABILITY_PERCENT = 100;
- private Random mRandom = new Random();
+ private final Random mRandom = new Random();
/**
- * @see ContextHubServiceCallback.handleNanoappMessage
* @return whether the message was handled
+ * @see ContextHubServiceCallback#handleNanoappMessage
*/
public boolean handleNanoappMessage(int contextHubId,
short hostEndpointId, NanoAppMessage message,
@@ -331,7 +334,8 @@ public class ContextHubService extends IContextHubService.Stub {
}
if (Flags.reliableMessageDuplicateDetectionService()
- && didEventHappen(MESSAGE_DUPLICATION_PROBABILITY_PERCENT)) {
+ && mRandom.nextInt(MAX_PROBABILITY_PERCENT)
+ < MESSAGE_DUPLICATION_PROBABILITY_PERCENT) {
Log.i(TAG, "[TEST MODE] Duplicating message ("
+ NUM_MESSAGES_TO_DUPLICATE
+ " sends) with message sequence number: "
@@ -344,16 +348,6 @@ public class ContextHubService extends IContextHubService.Stub {
}
return false;
}
-
- /**
- * Returns true if the event with percentPercent did happen.
- *
- * @param probabilityPercent the percent probability of the event.
- * @return true if the event happened, false otherwise.
- */
- private boolean didEventHappen(int probabilityPercent) {
- return mRandom.nextInt(MAX_PROBABILITY_PERCENT) < probabilityPercent;
- }
}
public ContextHubService(Context context, IContextHubWrapper contextHubWrapper) {
@@ -476,7 +470,7 @@ public class ContextHubService extends IContextHubService.Stub {
hubInfo = mContextHubWrapper.getHubs();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while getting Context Hub info", e);
- hubInfo = new Pair(Collections.emptyList(), Collections.emptyList());
+ hubInfo = new Pair<>(Collections.emptyList(), Collections.emptyList());
}
long bootTimeNs = SystemClock.elapsedRealtimeNanos() - startTimeNs;
@@ -536,6 +530,7 @@ public class ContextHubService extends IContextHubService.Stub {
for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
try {
mContextHubWrapper.registerExistingCallback(contextHubId);
+ Log.i(TAG, "Re-registered callback to context hub " + contextHubId);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while registering existing service callback for hub "
+ "(ID = " + contextHubId + ")", e);
@@ -647,7 +642,7 @@ public class ContextHubService extends IContextHubService.Stub {
mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
// If we are in HSUM mode, any user can change the microphone setting
- if (mUserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) {
+ if (UserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) {
Log.d(TAG, "User: " + userId + " mic privacy: " + enabled);
sendMicrophoneDisableSettingUpdate(enabled);
}
@@ -720,33 +715,30 @@ public class ContextHubService extends IContextHubService.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public int[] getContextHubHandles() throws RemoteException {
+ public int[] getContextHubHandles() {
super.getContextHubHandles_enforcePermission();
-
return ContextHubServiceUtil.createPrimitiveIntArray(mContextHubIdToInfoMap.keySet());
}
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
+ public ContextHubInfo getContextHubInfo(int contextHubHandle) {
super.getContextHubInfo_enforcePermission();
-
if (!mContextHubIdToInfoMap.containsKey(contextHubHandle)) {
Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in getContextHubInfo");
return null;
}
-
return mContextHubIdToInfoMap.get(contextHubHandle);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Returns a List of ContextHubInfo object describing the available hubs.
*
* @return the List of ContextHubInfo objects
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public List<ContextHubInfo> getContextHubs() throws RemoteException {
+ public List<ContextHubInfo> getContextHubs() {
super.getContextHubs_enforcePermission();
return mContextHubInfoList;
@@ -814,7 +806,7 @@ public class ContextHubService extends IContextHubService.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) throws RemoteException {
+ public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) {
super.loadNanoApp_enforcePermission();
if (mContextHubWrapper == null) {
@@ -843,7 +835,7 @@ public class ContextHubService extends IContextHubService.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public int unloadNanoApp(int nanoAppHandle) throws RemoteException {
+ public int unloadNanoApp(int nanoAppHandle) {
super.unloadNanoApp_enforcePermission();
if (mContextHubWrapper == null) {
@@ -870,7 +862,7 @@ public class ContextHubService extends IContextHubService.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) throws RemoteException {
+ public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
super.getNanoAppInstanceInfo_enforcePermission();
@@ -880,7 +872,7 @@ public class ContextHubService extends IContextHubService.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public int[] findNanoAppOnHub(
- int contextHubHandle, NanoAppFilter filter) throws RemoteException {
+ int contextHubHandle, NanoAppFilter filter) {
super.findNanoAppOnHub_enforcePermission();
@@ -895,20 +887,19 @@ public class ContextHubService extends IContextHubService.Stub {
int[] retArray = new int[foundInstances.size()];
for (int i = 0; i < foundInstances.size(); i++) {
- retArray[i] = foundInstances.get(i).intValue();
+ retArray[i] = foundInstances.get(i);
}
return retArray;
}
/**
* Performs a query at the specified hub.
- * <p>
- * This method should only be invoked internally by the service, either to update the service
+ *
+ * <p>This method should only be invoked internally by the service, either to update the service
* cache or as a result of an explicit query requested by a client through the sendMessage API.
*
* @param contextHubId the ID of the hub to do the query
* @return true if the query succeeded
- * @throws IllegalStateException if the transaction queue is full
*/
private boolean queryNanoAppsInternal(int contextHubId) {
if (mContextHubWrapper == null) {
@@ -1003,7 +994,7 @@ public class ContextHubService extends IContextHubService.Stub {
return;
}
- byte errorCode = ErrorCode.OK;
+ byte errorCode;
synchronized (mReliableMessageRecordQueue) {
Optional<ReliableMessageRecord> record =
findReliableMessageRecord(contextHubId,
@@ -1219,7 +1210,6 @@ public class ContextHubService extends IContextHubService.Stub {
return mContextHubIdToInfoMap.containsKey(contextHubId);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Creates and registers a client at the service for the specified Context Hub.
*
@@ -1232,10 +1222,11 @@ public class ContextHubService extends IContextHubService.Stub {
* @throws IllegalStateException if max number of clients have already registered
* @throws NullPointerException if clientCallback is null
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public IContextHubClient createClient(
int contextHubId, IContextHubClientCallback clientCallback,
- @Nullable String attributionTag, String packageName) throws RemoteException {
+ @Nullable String attributionTag, String packageName) {
super.createClient_enforcePermission();
if (!isValidContextHubId(contextHubId)) {
@@ -1250,7 +1241,6 @@ public class ContextHubService extends IContextHubService.Stub {
contextHubInfo, clientCallback, attributionTag, mTransactionManager, packageName);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Creates and registers a PendingIntent client at the service for the specified Context Hub.
*
@@ -1262,10 +1252,11 @@ public class ContextHubService extends IContextHubService.Stub {
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
* @throws IllegalStateException if there were too many registered clients at the service
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public IContextHubClient createPendingIntentClient(
int contextHubId, PendingIntent pendingIntent, long nanoAppId,
- @Nullable String attributionTag) throws RemoteException {
+ @Nullable String attributionTag) {
super.createPendingIntentClient_enforcePermission();
if (!isValidContextHubId(contextHubId)) {
@@ -1277,15 +1268,14 @@ public class ContextHubService extends IContextHubService.Stub {
contextHubInfo, pendingIntent, nanoAppId, attributionTag, mTransactionManager);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Loads a nanoapp binary at the specified Context hub.
*
* @param contextHubId the ID of the hub to load the binary
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppBinary the binary to load
- * @throws IllegalStateException if the transaction queue is full
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public void loadNanoAppOnHub(
int contextHubId, IContextHubTransactionCallback transactionCallback,
@@ -1308,15 +1298,14 @@ public class ContextHubService extends IContextHubService.Stub {
mTransactionManager.addTransaction(transaction);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Unloads a nanoapp from the specified Context Hub.
*
* @param contextHubId the ID of the hub to unload the nanoapp
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppId the ID of the nanoapp to unload
- * @throws IllegalStateException if the transaction queue is full
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public void unloadNanoAppFromHub(
int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
@@ -1333,19 +1322,17 @@ public class ContextHubService extends IContextHubService.Stub {
mTransactionManager.addTransaction(transaction);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Enables a nanoapp at the specified Context Hub.
*
* @param contextHubId the ID of the hub to enable the nanoapp
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppId the ID of the nanoapp to enable
- * @throws IllegalStateException if the transaction queue is full
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public void enableNanoApp(
- int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
- throws RemoteException {
+ int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) {
super.enableNanoApp_enforcePermission();
if (!checkHalProxyAndContextHubId(
@@ -1358,19 +1345,17 @@ public class ContextHubService extends IContextHubService.Stub {
mTransactionManager.addTransaction(transaction);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Disables a nanoapp at the specified Context Hub.
*
* @param contextHubId the ID of the hub to disable the nanoapp
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppId the ID of the nanoapp to disable
- * @throws IllegalStateException if the transaction queue is full
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public void disableNanoApp(
- int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
- throws RemoteException {
+ int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) {
super.disableNanoApp_enforcePermission();
if (!checkHalProxyAndContextHubId(
@@ -1383,17 +1368,16 @@ public class ContextHubService extends IContextHubService.Stub {
mTransactionManager.addTransaction(transaction);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Queries for a list of nanoapps from the specified Context hub.
*
* @param contextHubId the ID of the hub to query
* @param transactionCallback the client-facing transaction callback interface
- * @throws IllegalStateException if the transaction queue is full
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public void queryNanoApps(int contextHubId, IContextHubTransactionCallback transactionCallback)
- throws RemoteException {
+ public void queryNanoApps(int contextHubId,
+ IContextHubTransactionCallback transactionCallback) {
super.queryNanoApps_enforcePermission();
if (!checkHalProxyAndContextHubId(
@@ -1406,16 +1390,15 @@ public class ContextHubService extends IContextHubService.Stub {
mTransactionManager.addTransaction(transaction);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Queries for a list of preloaded nanoapp IDs from the specified Context Hub.
*
* @param hubInfo The Context Hub to query a list of nanoapps from.
* @return The list of 64-bit IDs of the preloaded nanoapps.
- * @throws NullPointerException if hubInfo is null
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) throws RemoteException {
+ public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) {
super.getPreloadedNanoAppIds_enforcePermission();
Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
@@ -1426,7 +1409,6 @@ public class ContextHubService extends IContextHubService.Stub {
return nanoappIds;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Puts the context hub in and out of test mode. Test mode is a clean state
* where tests can be executed in the same environment. If enable is true,
@@ -1442,6 +1424,7 @@ public class ContextHubService extends IContextHubService.Stub {
* test mode.
* @return If true, the operation was successful; false otherwise.
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public boolean setTestMode(boolean enable) {
super.setTestMode_enforcePermission();
@@ -1551,10 +1534,6 @@ public class ContextHubService extends IContextHubService.Stub {
}
}
- private void checkPermissions() {
- ContextHubServiceUtil.checkPermissions(mContext);
- }
-
private int onMessageReceiptOldApi(
int msgType, int contextHubHandle, int appInstance, byte[] data) {
if (data == null) {
@@ -1586,7 +1565,6 @@ public class ContextHubService extends IContextHubService.Stub {
callback.onMessageReceipt(contextHubHandle, appInstance, msg);
} catch (RemoteException e) {
Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ").");
- continue;
}
}
mCallbacksList.finishBroadcast();
@@ -1729,8 +1707,8 @@ public class ContextHubService extends IContextHubService.Stub {
* Hub.
*/
private void sendMicrophoneDisableSettingUpdateForCurrentUser() {
- boolean isEnabled = mSensorPrivacyManagerInternal == null ? false :
- mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
+ boolean isEnabled = mSensorPrivacyManagerInternal != null
+ && mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
getCurrentUserId(), SensorPrivacyManager.Sensors.MICROPHONE);
sendMicrophoneDisableSettingUpdate(isEnabled);
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 286e78951a03..19381509c7fe 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -68,7 +68,6 @@ import android.location.LocationManager;
import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.LocationResult.BadLocationException;
-import android.location.flags.Flags;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -1050,22 +1049,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
stopBatching();
if (mStarted && mGnssNative.getCapabilities().hasScheduling()) {
- if (Flags.gnssCallStopBeforeSetPositionMode()) {
- GnssPositionMode positionMode = new GnssPositionMode(mPositionMode,
- GNSS_POSITION_RECURRENCE_PERIODIC, mFixInterval,
- /* preferredAccuracy= */ 0,
- /* preferredTime= */ 0,
- mProviderRequest.isLowPower());
- if (!positionMode.equals(mLastPositionMode)) {
- stopNavigating();
- startNavigating();
- }
- } else {
- // change period and/or lowPowerMode
- if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
- mFixInterval, mProviderRequest.isLowPower())) {
- Log.e(TAG, "set_position_mode failed in updateRequirements");
- }
+ // change period and/or lowPowerMode
+ if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+ mFixInterval, mProviderRequest.isLowPower())) {
+ Log.e(TAG, "set_position_mode failed in updateRequirements");
}
} else if (!mStarted) {
// start GPS
@@ -1248,32 +1235,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
int interval = mGnssNative.getCapabilities().hasScheduling() ? mFixInterval : 1000;
-
- if (Flags.gnssCallStopBeforeSetPositionMode()) {
- boolean success = mGnssNative.setPositionMode(mPositionMode,
- GNSS_POSITION_RECURRENCE_PERIODIC, interval,
- /* preferredAccuracy= */ 0,
- /* preferredTime= */ 0,
- mProviderRequest.isLowPower());
- if (success) {
- mLastPositionMode = new GnssPositionMode(mPositionMode,
- GNSS_POSITION_RECURRENCE_PERIODIC, interval,
- /* preferredAccuracy= */ 0,
- /* preferredTime= */ 0,
- mProviderRequest.isLowPower());
- } else {
- mLastPositionMode = null;
- setStarted(false);
- Log.e(TAG, "set_position_mode failed in startNavigating()");
- return;
- }
- } else {
- if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
- interval, mProviderRequest.isLowPower())) {
- setStarted(false);
- Log.e(TAG, "set_position_mode failed in startNavigating()");
- return;
- }
+ if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+ interval, mProviderRequest.isLowPower())) {
+ setStarted(false);
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
}
if (!mGnssNative.start()) {
setStarted(false);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f2977082e095..a4f534eeba67 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8565,6 +8565,13 @@ public class NotificationManagerService extends SystemService {
*/
private boolean enqueueNotification() {
synchronized (mNotificationLock) {
+ if (android.app.Flags.secureAllowlistToken()) {
+ // allowlistToken is populated by unparceling, so it will be absent if the
+ // EnqueueNotificationRunnable is created directly by NMS (as we do for group
+ // summaries) instead of via notify(). Fix that.
+ r.getNotification().overrideAllowlistToken(ALLOWLIST_TOKEN);
+ }
+
final long snoozeAt =
mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
r.getUser().getIdentifier(),
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
new file mode 100644
index 000000000000..ec95298d0d13
--- /dev/null
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.media.AudioAttributes.USAGE_ALARM;
+
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioFocusInfo;
+import android.media.AudioManager;
+import android.media.AudioPlaybackConfiguration;
+import android.media.audiopolicy.AudioPolicy;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.Vibrator;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+public class BackgroundUserSoundNotifier {
+
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = BackgroundUserSoundNotifier.class.getSimpleName();
+ public static final String BUSN_CHANNEL_ID = "bg_user_sound_channel";
+ public static final String BUSN_CHANNEL_NAME = "BackgroundUserSound";
+ private static final String ACTION_MUTE_SOUND = "com.android.server.ACTION_MUTE_BG_USER";
+ private static final String EXTRA_NOTIFICATION_ID = "com.android.server.EXTRA_CLIENT_UID";
+ private static final String EXTRA_CURRENT_USER_ID = "com.android.server.EXTRA_CURRENT_USER_ID";
+ private static final String ACTION_SWITCH_USER = "com.android.server.ACTION_SWITCH_TO_USER";
+ /** ID of user with notification displayed, -1 if notification is not showing*/
+ private int mUserWithNotification = -1;
+ private final Context mSystemUserContext;
+ @VisibleForTesting
+ final NotificationManager mNotificationManager;
+ private final UserManager mUserManager;
+
+ /**
+ * Facilitates the display of notifications to current user when there is an alarm or timer
+ * going off on background user and allows to manage the sound through actions.
+ */
+ public BackgroundUserSoundNotifier(Context context) {
+ mSystemUserContext = context;
+ mNotificationManager = mSystemUserContext.getSystemService(NotificationManager.class);
+ mUserManager = mSystemUserContext.getSystemService(UserManager.class);
+ NotificationChannel channel = new NotificationChannel(BUSN_CHANNEL_ID, BUSN_CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_HIGH);
+ mNotificationManager.createNotificationChannel(channel);
+ setupFocusControlAudioPolicy();
+ }
+
+ private void setupFocusControlAudioPolicy() {
+ // Used to configure our audio policy to handle focus events.
+ // This gives us the ability to decide which audio focus requests to accept and bypasses
+ // the framework ducking logic.
+ ActivityManager am = mSystemUserContext.getSystemService(ActivityManager.class);
+
+ registerReceiver(am);
+ BackgroundUserListener bgUserListener = new BackgroundUserListener(mSystemUserContext);
+ AudioPolicy.Builder focusControlPolicyBuilder = new AudioPolicy.Builder(mSystemUserContext);
+ focusControlPolicyBuilder.setLooper(Looper.getMainLooper());
+
+ focusControlPolicyBuilder.setAudioPolicyFocusListener(bgUserListener);
+
+ AudioPolicy mFocusControlAudioPolicy = focusControlPolicyBuilder.build();
+ int status = mSystemUserContext.getSystemService(AudioManager.class)
+ .registerAudioPolicy(mFocusControlAudioPolicy);
+ if (status != AudioManager.SUCCESS) {
+ Log.w(LOG_TAG , "Could not register the service's focus"
+ + " control audio policy, error: " + status);
+ }
+ }
+
+ final class BackgroundUserListener extends AudioPolicy.AudioPolicyFocusListener {
+
+ Context mSystemContext;
+
+ BackgroundUserListener(Context systemContext) {
+ mSystemContext = systemContext;
+ }
+
+ @SuppressLint("MissingPermission")
+ public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {
+ try {
+ BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi,
+ mSystemContext.createContextAsUser(
+ UserHandle.of(ActivityManager.getCurrentUser()), 0));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {
+ BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary(afi);
+ }
+ }
+
+ /**
+ * Registers a BroadcastReceiver for actions related to background user sound notifications.
+ * When ACTION_MUTE_SOUND is received, it mutes a background user's alarm sound.
+ * When ACTION_SWITCH_USER is received, a switch to the background user with alarm is started.
+ */
+ private void registerReceiver(ActivityManager service) {
+ BroadcastReceiver backgroundUserNotificationBroadcastReceiver = new BroadcastReceiver() {
+ @SuppressLint("MissingPermission")
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!(intent.hasExtra(EXTRA_NOTIFICATION_ID)
+ && intent.hasExtra(EXTRA_CURRENT_USER_ID)
+ && intent.hasExtra(Intent.EXTRA_USER_ID))) {
+ return;
+ }
+ final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
+
+ if (DEBUG) {
+ Log.d(LOG_TAG,
+ "User with alarm id " + intent.getIntExtra(Intent.EXTRA_USER_ID,
+ -1) + " current user id " + intent.getIntExtra(
+ EXTRA_CURRENT_USER_ID, -1));
+ }
+ mNotificationManager.cancelAsUser(LOG_TAG, notificationId,
+ UserHandle.of(intent.getIntExtra(EXTRA_CURRENT_USER_ID, -1)));
+ if (ACTION_MUTE_SOUND.equals(intent.getAction())) {
+ final AudioManager audioManager =
+ mSystemUserContext.getSystemService(AudioManager.class);
+ if (audioManager != null) {
+ for (AudioPlaybackConfiguration apc :
+ audioManager.getActivePlaybackConfigurations()) {
+ if (apc.getAudioAttributes().getUsage() == USAGE_ALARM) {
+ if (apc.getPlayerProxy() != null) {
+ apc.getPlayerProxy().stop();
+ }
+ }
+ }
+ }
+ Vibrator vibrator = mSystemUserContext.getSystemService(Vibrator.class);
+ if (vibrator != null && vibrator.isVibrating()) {
+ vibrator.cancel();
+ }
+ } else if (ACTION_SWITCH_USER.equals(intent.getAction())) {
+ service.switchUser(intent.getIntExtra(Intent.EXTRA_USER_ID, -1));
+ }
+ }
+ };
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_MUTE_SOUND);
+ filter.addAction(ACTION_SWITCH_USER);
+ mSystemUserContext.registerReceiver(backgroundUserNotificationBroadcastReceiver, filter,
+ Context.RECEIVER_NOT_EXPORTED);
+ }
+
+ /**
+ * Check if sound is coming from background user and show notification is required.
+ */
+ @VisibleForTesting
+ void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi, Context
+ foregroundContext) throws RemoteException {
+ final int userId = UserHandle.getUserId(afi.getClientUid());
+ final int usage = afi.getAttributes().getUsage();
+ String userName = mUserManager.getUserInfo(userId).name;
+ if (userId != foregroundContext.getUserId()) {
+ //TODO: b/349138482 - Add handling of cases when usage == USAGE_NOTIFICATION_RINGTONE
+ if (usage == USAGE_ALARM) {
+ Intent muteIntent = createIntent(ACTION_MUTE_SOUND, afi, foregroundContext, userId);
+ PendingIntent mutePI = PendingIntent.getBroadcast(mSystemUserContext, 0,
+ muteIntent, PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE);
+ Intent switchIntent = createIntent(ACTION_SWITCH_USER, afi, foregroundContext,
+ userId);
+ PendingIntent switchPI = PendingIntent.getBroadcast(mSystemUserContext, 0,
+ switchIntent, PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE);
+
+ mUserWithNotification = foregroundContext.getUserId();
+ mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(),
+ createNotification(userName, mutePI, switchPI, foregroundContext),
+ foregroundContext.getUser());
+ }
+ }
+ }
+
+ /**
+ * If notification is present, dismisses it. To be called when the relevant sound loses focus.
+ */
+ private void dismissNotificationIfNecessary(AudioFocusInfo afi) {
+ if (mUserWithNotification >= 0) {
+ mNotificationManager.cancelAsUser(LOG_TAG, afi.getClientUid(),
+ UserHandle.of(mUserWithNotification));
+ }
+ mUserWithNotification = -1;
+ }
+
+ private Intent createIntent(String intentAction, AudioFocusInfo afi, Context fgUserContext,
+ int userId) {
+ final Intent intent = new Intent(intentAction);
+ intent.putExtra(EXTRA_CURRENT_USER_ID, fgUserContext.getUserId());
+ intent.putExtra(EXTRA_NOTIFICATION_ID, afi.getClientUid());
+ intent.putExtra(Intent.EXTRA_USER_ID, userId);
+ return intent;
+ }
+
+ private Notification createNotification(String userName, PendingIntent muteIntent,
+ PendingIntent switchIntent, Context fgContext) {
+ final String title = fgContext.getString(R.string.bg_user_sound_notification_title_alarm,
+ userName);
+ final int icon = R.drawable.ic_audio_alarm;
+ final Notification.Action mute = new Notification.Action.Builder(null,
+ fgContext.getString(R.string.bg_user_sound_notification_button_mute),
+ muteIntent).build();
+ final Notification.Action switchUser = new Notification.Action.Builder(null,
+ fgContext.getString(R.string.bg_user_sound_notification_button_switch_user),
+ switchIntent).build();
+ return new Notification.Builder(mSystemUserContext, BUSN_CHANNEL_ID)
+ .setSmallIcon(icon)
+ .setTicker(title)
+ .setWhen(0)
+ .setOngoing(true)
+ .setColor(fgContext.getColor(R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentIntent(muteIntent)
+ .setAutoCancel(true)
+ .setActions(mute, switchUser)
+ .setContentText(fgContext.getString(R.string.bg_user_sound_notification_message))
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .build();
+ }
+}
+
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index f59ae168bdc3..ee0159d722b1 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2207,11 +2207,17 @@ public class ComputerEngine implements Computer {
if (PackageManagerServiceUtils.isSystemOrRoot(callingUid)) {
return true;
}
- if (requireFullPermission) {
- return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ boolean permissionGranted = requireFullPermission ? hasPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ : (hasPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS));
+ if (!permissionGranted) {
+ if (Process.isIsolatedUid(callingUid) && isKnownIsolatedComputeApp(callingUid)) {
+ return checkIsolatedOwnerHasPermission(callingUid, requireFullPermission);
+ }
}
- return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ return permissionGranted;
}
/**
@@ -2227,6 +2233,24 @@ public class ComputerEngine implements Computer {
== PackageManager.PERMISSION_GRANTED;
}
+ private boolean hasPermission(String permission, int uid) {
+ return mContext.checkPermission(permission, Process.INVALID_PID, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Since isolated process cannot hold permissions, we check the permissions on the owner app
+ * for known isolated_compute_app cases because they belong to the same package.
+ */
+ private boolean checkIsolatedOwnerHasPermission(int callingUid, boolean requireFullPermission) {
+ int ownerUid = getIsolatedOwner(callingUid);
+ if (requireFullPermission) {
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, ownerUid);
+ }
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, ownerUid)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS, ownerUid);
+ }
+
public final boolean isCallerSameApp(String packageName, int uid) {
return isCallerSameApp(packageName, uid, false /* resolveIsolatedUid */);
}
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 90d6adc4fa52..630fcb614eb3 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -185,7 +185,8 @@ final class ReconcilePackageUtils {
removeAppKeySetData = true;
}
- if (!installRequest.isInstallSystem() && !isSystemPackage && !isApex
+ if ((installRequest.getScanFlags() & SCAN_BOOTING) == 0
+ && !installRequest.isInstallSystem() && !isSystemPackage && !isApex
&& signingDetails != null
&& systemPackage != null && systemPackage.getSigningDetails() != null
&& systemPackage.getSigningDetails().checkCapability(
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7f4a5cb7cf8f..e0e8a0365b0e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -334,6 +334,7 @@ public class UserManagerService extends IUserManager.Stub {
private final Context mContext;
private final PackageManagerService mPm;
+
/**
* Lock for packages. If using with {@link #mUsersLock}, {@link #mPackagesLock} should be
* acquired first.
@@ -1076,6 +1077,10 @@ public class UserManagerService extends IUserManager.Stub {
}
showHsumNotificationIfNeeded();
+
+ if (Flags.addUiForSoundsFromBackgroundUsers()) {
+ new BackgroundUserSoundNotifier(mContext);
+ }
}
private boolean isAutoLockingPrivateSpaceOnRestartsEnabled() {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index bf90a6c1926c..0cda30f3affc 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2090,8 +2090,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
"Home - Long Press");
switch (mLongPressOnHomeBehavior) {
case LONG_PRESS_HOME_ALL_APPS:
- logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
- launchAllAppsAction();
+ if (mHasFeatureLeanback) {
+ launchAllAppsAction();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
+ } else {
+ launchAllAppsViaA11y();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.ACCESSIBILITY_ALL_APPS);
+ }
break;
case LONG_PRESS_HOME_ASSIST:
logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT);
@@ -3683,12 +3688,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
break;
case KeyEvent.KEYCODE_ALL_APPS:
- if (!down) {
- mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
- Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
- msg.setAsynchronous(true);
- msg.sendToTarget();
- logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
+ if (firstDown) {
+ if (mHasFeatureLeanback) {
+ mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
+ Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
+ } else {
+ launchAllAppsViaA11y();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.ACCESSIBILITY_ALL_APPS);
+ }
}
return true;
case KeyEvent.KEYCODE_NOTIFICATION:
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 11b9e776204c..f85b8cc9c1bb 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -17,6 +17,7 @@
package com.android.server.power;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
@@ -197,9 +198,10 @@ public class Notifier {
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
Executor backgroundExecutor, PowerManagerFlags powerManagerFlags, Injector injector) {
mContext = context;
+ mInjector = (injector == null) ? new RealInjector() : injector;
mFlags = powerManagerFlags;
mBatteryStats = batteryStats;
- mAppOps = mContext.getSystemService(AppOpsManager.class);
+ mAppOps = mInjector.getAppOpsManager(context);
mSuspendBlocker = suspendBlocker;
mPolicy = policy;
mFaceDownDetector = faceDownDetector;
@@ -230,7 +232,6 @@ public class Notifier {
mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
- mInjector = (injector == null) ? new RealInjector() : injector;
mWakeLockLog = mInjector.getWakeLockLog(context);
// Initialize interactive state for battery stats.
try {
@@ -264,6 +265,7 @@ public class Notifier {
/**
* Called when a wake lock is acquired.
*/
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onWakeLockAcquired(int flags, String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
IWakeLockCallback callback) {
@@ -273,27 +275,28 @@ public class Notifier {
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
- notifyWakeLockListener(callback, tag, true, ownerUid, flags);
- final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
- if (monitorType >= 0) {
- try {
- final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID
- && (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
- if (workSource != null) {
- mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag,
- historyTag, monitorType, unimportantForLogging);
- } else {
- mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
- monitorType, unimportantForLogging);
- // XXX need to deal with disabled operations.
- mAppOps.startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+ notifyWakeLockListener(callback, tag, true, ownerUid, ownerPid, flags, workSource,
+ packageName, historyTag);
+ if (!mFlags.improveWakelockLatency()) {
+ final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ if (monitorType >= 0) {
+ try {
+ final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID
+ && (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
+ if (workSource != null) {
+ mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag,
+ historyTag, monitorType, unimportantForLogging);
+ } else {
+ mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
+ monitorType, unimportantForLogging);
+ // XXX need to deal with disabled operations.
+ mAppOps.startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName,
+ false, null, null);
+ }
+ } catch (RemoteException ex) {
+ // Ignore
}
- } catch (RemoteException ex) {
- // Ignore
}
- }
-
- if (!mFlags.improveWakelockLatency()) {
mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, /*eventTime=*/ -1);
}
mWakefulnessSessionObserver.onWakeLockAcquired(flags);
@@ -404,6 +407,7 @@ public class Notifier {
/**
* Called when a wake lock is released.
*/
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onWakeLockReleased(int flags, String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
IWakeLockCallback callback, int releaseReason) {
@@ -413,23 +417,24 @@ public class Notifier {
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
- notifyWakeLockListener(callback, tag, false, ownerUid, flags);
- final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
- if (monitorType >= 0) {
- try {
- if (workSource != null) {
- mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag,
- historyTag, monitorType);
- } else {
- mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
- historyTag, monitorType);
- mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+ notifyWakeLockListener(callback, tag, false, ownerUid, ownerPid, flags, workSource,
+ packageName, historyTag);
+ if (!mFlags.improveWakelockLatency()) {
+ final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ if (monitorType >= 0) {
+ try {
+ if (workSource != null) {
+ mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag,
+ historyTag, monitorType);
+ } else {
+ mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
+ historyTag, monitorType);
+ mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName, null);
+ }
+ } catch (RemoteException ex) {
+ // Ignore
}
- } catch (RemoteException ex) {
- // Ignore
}
- }
- if (!mFlags.improveWakelockLatency()) {
mWakeLockLog.onWakeLockReleased(tag, ownerUid, /*eventTime=*/ -1);
}
mWakefulnessSessionObserver.onWakeLockReleased(flags, releaseReason);
@@ -1049,24 +1054,75 @@ public class Notifier {
}
private void notifyWakeLockListener(IWakeLockCallback callback, String tag, boolean isEnabled,
- int ownerUid, int flags) {
- if (callback != null) {
- long currentTime = mInjector.currentTimeMillis();
- mHandler.post(() -> {
+ int ownerUid, int ownerPid, int flags, WorkSource workSource, String packageName,
+ String historyTag) {
+ mHandler.post(() -> {
+ if (mFlags.improveWakelockLatency()) {
+ long currentTime = mInjector.currentTimeMillis();
+ if (isEnabled) {
+ notifyWakelockAcquisition(tag, ownerUid, ownerPid, flags,
+ workSource, packageName, historyTag, currentTime);
+ } else {
+ notifyWakelockRelease(tag, ownerUid, ownerPid, flags,
+ workSource, packageName, historyTag, currentTime);
+ }
+ }
+
+ if (callback != null) {
try {
- if (mFlags.improveWakelockLatency()) {
- if (isEnabled) {
- mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);
- } else {
- mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
- }
- }
callback.onStateChanged(isEnabled);
} catch (RemoteException e) {
Slog.e(TAG, "Wakelock.mCallback [" + tag + "] is already dead.", e);
}
- });
+ }
+ });
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private void notifyWakelockAcquisition(String tag, int ownerUid, int ownerPid, int flags,
+ WorkSource workSource, String packageName, String historyTag, long currentTime) {
+ final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ if (monitorType >= 0) {
+ try {
+ final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID
+ && (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
+ if (workSource != null) {
+ mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag,
+ historyTag, monitorType, unimportantForLogging);
+ } else {
+ mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
+ monitorType, unimportantForLogging);
+ // XXX need to deal with disabled operations.
+ mAppOps.startOpNoThrow(
+ AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName,
+ false, null, null);
+ }
+ } catch (RemoteException ex) {
+ // Do Nothing
+ }
+ }
+ mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private void notifyWakelockRelease(String tag, int ownerUid, int ownerPid, int flags,
+ WorkSource workSource, String packageName, String historyTag, long currentTime) {
+ final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ if (monitorType >= 0) {
+ try {
+ if (workSource != null) {
+ mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag,
+ historyTag, monitorType);
+ } else {
+ mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
+ historyTag, monitorType);
+ mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName, null);
+ }
+ } catch (RemoteException ex) {
+ // Ignore
+ }
}
+ mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
}
private final class NotifierHandler extends Handler {
@@ -1114,6 +1170,11 @@ public class Notifier {
* Gets the WakeLockLog object
*/
WakeLockLog getWakeLockLog(Context context);
+
+ /**
+ * Gets the AppOpsManager system service
+ */
+ AppOpsManager getAppOpsManager(Context context);
}
static class RealInjector implements Injector {
@@ -1126,5 +1187,10 @@ public class Notifier {
public WakeLockLog getWakeLockLog(Context context) {
return new WakeLockLog(context);
}
+
+ @Override
+ public AppOpsManager getAppOpsManager(Context context) {
+ return context.getSystemService(AppOpsManager.class);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index ff1d2e402350..c6ef89dcff69 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -16,6 +16,8 @@
package com.android.server.power.feature;
+import android.os.Build;
+import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Slog;
@@ -82,7 +84,7 @@ public class PowerManagerFlags {
}
return mEnabled;
}
- mEnabled = mFlagFunction.get();
+ mEnabled = flagOrSystemProperty(mFlagFunction, mName);
if (DEBUG) {
Slog.d(TAG, mName + ": mEnabled. Flag value = " + mEnabled);
}
@@ -90,6 +92,15 @@ public class PowerManagerFlags {
return mEnabled;
}
+ private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
+ boolean flagValue = flagFunction.get();
+ if (Build.IS_ENG || Build.IS_USERDEBUG) {
+ return SystemProperties.getBoolean("persist.sys." + flagName + "-override",
+ flagValue);
+ }
+ return flagValue;
+ }
+
@Override
public String toString() {
// remove com.android.server.power.feature.flags. from the beginning of the name.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0a384e530d0d..6b25c84daf48 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -93,6 +93,7 @@ import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN;
import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS;
import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY;
@@ -8183,8 +8184,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
- if (mAppCompatController.getAppCompatCapability()
- .getAppCompatOrientationCapability()
+ if (mAppCompatController.getAppCompatOverrides()
+ .getAppCompatOrientationOverrides()
.shouldIgnoreRequestedOrientation(requestedOrientation)) {
return;
}
@@ -9921,6 +9922,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
info.getMinAspectRatio());
}
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_SMALL)) {
+ return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE,
+ info.getMinAspectRatio());
+ }
return info.getMinAspectRatio();
}
@@ -10821,7 +10827,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
proto.write(SHOULD_OVERRIDE_MIN_ASPECT_RATIO,
mLetterboxUiController.shouldOverrideMinAspectRatio());
proto.write(SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP,
- mAppCompatController.getAppCompatCapability().getAppCompatOrientationCapability()
+ mAppCompatController.getAppCompatOverrides().getAppCompatOrientationOverrides()
.shouldIgnoreOrientationRequestLoop());
proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP,
mLetterboxUiController.shouldOverrideForceResizeApp());
@@ -11154,8 +11160,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean cancel) {
// This override is just for getting metrics. allFinished needs to be checked before
// finish because finish resets all the states.
- final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
- if (syncGroup != null && group != getSyncGroup()) return;
+ if (isDifferentSyncGroup(group)) return;
mLastAllReadyAtSync = allSyncFinished();
super.finishSync(outMergedTransaction, group, cancel);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e9c08e0ac29d..717c3998aba4 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1649,7 +1649,8 @@ class ActivityStarter {
// activity, so this isn't just deliver-to-top
&& mMovedToTopActivity == null
&& !transitionController.hasOrderChanges()
- && !transitionController.isTransientHide(startedActivityRootTask)) {
+ && !transitionController.isTransientHide(startedActivityRootTask)
+ && !newTransition.hasChanged(mLastStartActivityRecord)) {
// We just delivered to top, so there isn't an actual transition here.
if (!forceTransientTransition) {
newTransition.abort();
@@ -1792,6 +1793,7 @@ class ActivityStarter {
activity.destroyIfPossible("Removes redundant singleInstance");
}
}
+ targetTaskTop.mTransitionController.collect(targetTaskTop);
recordTransientLaunchIfNeeded(targetTaskTop);
// Recycle the target task for this launch.
startResult =
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index b6e6991656f2..cd5576fca2f9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -952,7 +952,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// Schedule transaction.
if (shouldDispatchLaunchActivityItemIndependently(r.info.packageName, r.getUid())) {
// LaunchActivityItem has @UnsupportedAppUsage usages.
- // Guard the bundleClientTransactionFlag feature with targetSDK on Android 15+.
+ // Guard with targetSDK on Android 15+.
// To not bundle the transaction, dispatch the pending before schedule new
// transaction.
mService.getLifecycleManager().dispatchPendingTransaction(proc.getThread());
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 3ff0fcefd747..4b0d739af64a 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -27,18 +27,18 @@ class AppCompatController {
@NonNull
private final AppCompatOrientationPolicy mOrientationPolicy;
@NonNull
- private final AppCompatCapability mAppCompatCapability;
+ private final AppCompatOverrides mAppCompatOverrides;
AppCompatController(@NonNull WindowManagerService wmService,
@NonNull ActivityRecord activityRecord) {
mTransparentPolicy = new TransparentPolicy(activityRecord,
wmService.mLetterboxConfiguration);
- mAppCompatCapability = new AppCompatCapability(wmService, activityRecord,
+ mAppCompatOverrides = new AppCompatOverrides(wmService, activityRecord,
wmService.mLetterboxConfiguration);
// TODO(b/341903757) Remove BooleanSuppliers after fixing dependency with aspectRatio.
final LetterboxUiController tmpController = activityRecord.mLetterboxUiController;
mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord,
- mAppCompatCapability, tmpController::shouldApplyUserFullscreenOverride,
+ mAppCompatOverrides, tmpController::shouldApplyUserFullscreenOverride,
tmpController::shouldApplyUserMinAspectRatioOverride,
tmpController::isSystemOverrideToFullscreenEnabled);
}
@@ -54,7 +54,7 @@ class AppCompatController {
}
@NonNull
- AppCompatCapability getAppCompatCapability() {
- return mAppCompatCapability;
+ AppCompatOverrides getAppCompatOverrides() {
+ return mAppCompatOverrides;
}
}
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationCapability.java b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
index 10f3e833f78a..446a75caeb82 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationCapability.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
@@ -28,7 +28,7 @@ import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENT
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.AppCompatCapability.asLazy;
+import static com.android.server.wm.AppCompatOverrides.asLazy;
import android.annotation.NonNull;
import android.content.pm.ActivityInfo;
@@ -40,9 +40,14 @@ import com.android.server.wm.utils.OptPropFactory;
import java.util.function.BooleanSupplier;
import java.util.function.LongSupplier;
-class AppCompatOrientationCapability {
+/**
+ * Encapsulates all the configurations and overrides about orientation used by
+ * {@link AppCompatOrientationPolicy}.
+ */
+class AppCompatOrientationOverrides {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatCapability" : TAG_ATM;
+ private static final String TAG = TAG_WITH_CLASS_NAME
+ ? "AppCompatOrientationOverrides" : TAG_ATM;
@NonNull
private final ActivityRecord mActivityRecord;
@@ -53,13 +58,13 @@ class AppCompatOrientationCapability {
private final OptPropFactory.OptProp mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp;
@NonNull
- final OrientationCapabilityState mOrientationCapabilityState;
+ final OrientationOverridesState mOrientationOverridesState;
- AppCompatOrientationCapability(@NonNull OptPropFactory optPropBuilder,
+ AppCompatOrientationOverrides(@NonNull OptPropFactory optPropBuilder,
@NonNull LetterboxConfiguration letterboxConfiguration,
@NonNull ActivityRecord activityRecord) {
mActivityRecord = activityRecord;
- mOrientationCapabilityState = new OrientationCapabilityState(mActivityRecord,
+ mOrientationOverridesState = new OrientationOverridesState(mActivityRecord,
System::currentTimeMillis);
final BooleanSupplier isPolicyForIgnoringRequestedOrientationEnabled = asLazy(
letterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled);
@@ -101,7 +106,7 @@ class AppCompatOrientationCapability {
@ActivityInfo.ScreenOrientation int requestedOrientation) {
if (mIgnoreRequestedOrientationOptProp.shouldEnableWithOverrideAndProperty(
isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION))) {
- if (mOrientationCapabilityState.mIsRelaunchingAfterRequestedOrientationChanged) {
+ if (mOrientationOverridesState.mIsRelaunchingAfterRequestedOrientationChanged) {
Slog.w(TAG, "Ignoring orientation update to "
+ screenOrientationToString(requestedOrientation)
+ " due to relaunching after setRequestedOrientation for "
@@ -150,9 +155,9 @@ class AppCompatOrientationCapability {
.shouldEnableWithOptInOverrideAndOptOutProperty(loopDetectionEnabled)) {
return false;
}
- mOrientationCapabilityState.updateOrientationRequestLoopState();
+ mOrientationOverridesState.updateOrientationRequestLoopState();
- return mOrientationCapabilityState.shouldIgnoreRequestInLoop()
+ return mOrientationOverridesState.shouldIgnoreRequestInLoop()
&& !mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio();
}
@@ -161,17 +166,17 @@ class AppCompatOrientationCapability {
* android.app.Activity#setRequestedOrientation}.
*/
void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) {
- mOrientationCapabilityState
+ mOrientationOverridesState
.mIsRelaunchingAfterRequestedOrientationChanged = isRelaunching;
}
boolean getIsRelaunchingAfterRequestedOrientationChanged() {
- return mOrientationCapabilityState.mIsRelaunchingAfterRequestedOrientationChanged;
+ return mOrientationOverridesState.mIsRelaunchingAfterRequestedOrientationChanged;
}
@VisibleForTesting
int getSetOrientationRequestCounter() {
- return mOrientationCapabilityState.mSetOrientationRequestCounter;
+ return mOrientationOverridesState.mSetOrientationRequestCounter;
}
private boolean isCompatChangeEnabled(long overrideChangeId) {
@@ -192,7 +197,7 @@ class AppCompatOrientationCapability {
.isTreatmentEnabledForActivity(mActivityRecord);
}
- static class OrientationCapabilityState {
+ static class OrientationOverridesState {
// Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR
final boolean mIsOverrideToNosensorOrientationEnabled;
// Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
@@ -219,7 +224,7 @@ class AppCompatOrientationCapability {
@VisibleForTesting
LongSupplier mCurrentTimeMillisSupplier;
- OrientationCapabilityState(@NonNull ActivityRecord activityRecord,
+ OrientationOverridesState(@NonNull ActivityRecord activityRecord,
@NonNull LongSupplier currentTimeMillisSupplier) {
mCurrentTimeMillisSupplier = currentTimeMillisSupplier;
mIsOverrideToNosensorOrientationEnabled =
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index c505ff9de825..8e9a9e99d4e1 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -45,7 +45,7 @@ class AppCompatOrientationPolicy {
private final ActivityRecord mActivityRecord;
@NonNull
- private final AppCompatCapability mAppCompatCapability;
+ private final AppCompatOverrides mAppCompatOverrides;
@NonNull
private final BooleanSupplier mShouldApplyUserFullscreenOverride;
@@ -56,12 +56,12 @@ class AppCompatOrientationPolicy {
// TODO(b/341903757) Remove BooleanSuppliers after fixing dependency with spectRatio component
AppCompatOrientationPolicy(@NonNull ActivityRecord activityRecord,
- @NonNull AppCompatCapability appCompatCapability,
+ @NonNull AppCompatOverrides appCompatOverrides,
@NonNull BooleanSupplier shouldApplyUserFullscreenOverride,
@NonNull BooleanSupplier shouldApplyUserMinAspectRatioOverride,
@NonNull BooleanSupplier isSystemOverrideToFullscreenEnabled) {
mActivityRecord = activityRecord;
- mAppCompatCapability = appCompatCapability;
+ mAppCompatOverrides = appCompatOverrides;
mShouldApplyUserFullscreenOverride = shouldApplyUserFullscreenOverride;
mShouldApplyUserMinAspectRatioOverride = shouldApplyUserMinAspectRatioOverride;
mIsSystemOverrideToFullscreenEnabled = isSystemOverrideToFullscreenEnabled;
@@ -99,11 +99,11 @@ class AppCompatOrientationPolicy {
return SCREEN_ORIENTATION_PORTRAIT;
}
- if (mAppCompatCapability.isAllowOrientationOverrideOptOut()) {
+ if (mAppCompatOverrides.isAllowOrientationOverrideOptOut()) {
return candidate;
}
- if (displayContent != null && mAppCompatCapability
+ if (displayContent != null && mAppCompatOverrides
.isOverrideOrientationOnlyForCameraEnabled()
&& (displayContent.mDisplayRotationCompatPolicy == null
|| !displayContent.mDisplayRotationCompatPolicy
@@ -127,9 +127,9 @@ class AppCompatOrientationPolicy {
return SCREEN_ORIENTATION_USER;
}
- final AppCompatOrientationCapability.OrientationCapabilityState capabilityState =
- mAppCompatCapability.getAppCompatOrientationCapability()
- .mOrientationCapabilityState;
+ final AppCompatOrientationOverrides.OrientationOverridesState capabilityState =
+ mAppCompatOverrides.getAppCompatOrientationOverrides()
+ .mOrientationOverridesState;
if (capabilityState.mIsOverrideToReverseLandscapeOrientationEnabled
&& (isFixedOrientationLandscape(candidate)
diff --git a/services/core/java/com/android/server/wm/AppCompatCapability.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
index d4379e486ea1..794008ac7f12 100644
--- a/services/core/java/com/android/server/wm/AppCompatCapability.java
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -57,9 +57,9 @@ import java.util.function.BooleanSupplier;
/**
* Encapsulate logic related to operations guarded by an app override.
*/
-public class AppCompatCapability {
+public class AppCompatOverrides {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatCapability" : TAG_ATM;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatOverrides" : TAG_ATM;
@NonNull
private final LetterboxConfiguration mLetterboxConfiguration;
@@ -67,13 +67,6 @@ public class AppCompatCapability {
@NonNull
private final ActivityRecord mActivityRecord;
- // Corresponds to OVERRIDE_ANY_ORIENTATION_TO_USER
- private final boolean mIsSystemOverrideToFullscreenEnabled;
- // Corresponds to OVERRIDE_RESPECT_REQUESTED_ORIENTATION
- private final boolean mIsOverrideRespectRequestedOrientationEnabled;
- // Corresponds to OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA
- private final boolean mIsOverrideOrientationOnlyForCameraEnabled;
-
@NonNull
private final OptPropFactory.OptProp mFakeFocusOptProp;
@NonNull
@@ -95,9 +88,9 @@ public class AppCompatCapability {
@NonNull
private final OptPropFactory.OptProp mAllowUserAspectRatioFullscreenOverrideOptProp;
- private final AppCompatOrientationCapability mAppCompatOrientationCapability;
+ private final AppCompatOrientationOverrides mAppCompatOrientationOverrides;
- AppCompatCapability(@NonNull WindowManagerService wmService,
+ AppCompatOverrides(@NonNull WindowManagerService wmService,
@NonNull ActivityRecord activityRecord,
@NonNull LetterboxConfiguration letterboxConfiguration) {
mLetterboxConfiguration = letterboxConfiguration;
@@ -107,8 +100,8 @@ public class AppCompatCapability {
final OptPropFactory optPropBuilder = new OptPropFactory(packageManager,
activityRecord.packageName);
- mAppCompatOrientationCapability =
- new AppCompatOrientationCapability(optPropBuilder, mLetterboxConfiguration,
+ mAppCompatOrientationOverrides =
+ new AppCompatOrientationOverrides(optPropBuilder, mLetterboxConfiguration,
mActivityRecord);
mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS,
@@ -149,13 +142,6 @@ public class AppCompatCapability {
mAllowUserAspectRatioFullscreenOverrideOptProp = optPropBuilder.create(
PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
mLetterboxConfiguration::isUserAppAspectRatioFullscreenEnabled);
-
- mIsSystemOverrideToFullscreenEnabled =
- isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER);
- mIsOverrideRespectRequestedOrientationEnabled =
- isCompatChangeEnabled(OVERRIDE_RESPECT_REQUESTED_ORIENTATION);
- mIsOverrideOrientationOnlyForCameraEnabled =
- isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
}
/**
@@ -172,8 +158,8 @@ public class AppCompatCapability {
}
@NonNull
- AppCompatOrientationCapability getAppCompatOrientationCapability() {
- return mAppCompatOrientationCapability;
+ AppCompatOrientationOverrides getAppCompatOrientationOverrides() {
+ return mAppCompatOrientationOverrides;
}
/**
@@ -245,7 +231,7 @@ public class AppCompatCapability {
}
boolean isSystemOverrideToFullscreenEnabled(int userAspectRatio) {
- return mIsSystemOverrideToFullscreenEnabled
+ return isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER)
&& !mAllowOrientationOverrideOptProp.isFalse()
&& (userAspectRatio == USER_MIN_ASPECT_RATIO_UNSET
|| userAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN);
@@ -273,11 +259,11 @@ public class AppCompatCapability {
}
boolean isOverrideRespectRequestedOrientationEnabled() {
- return mIsOverrideRespectRequestedOrientationEnabled;
+ return isCompatChangeEnabled(OVERRIDE_RESPECT_REQUESTED_ORIENTATION);
}
boolean isOverrideOrientationOnlyForCameraEnabled() {
- return mIsOverrideOrientationOnlyForCameraEnabled;
+ return isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
}
/**
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 939babc4df41..c55a1003f402 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -256,7 +256,8 @@ public class AppTransitionController {
tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
if (mDisplayContent.mAtmService.mBackNavigationController
.removeIfContainsBackAnimationTargets(tmpOpenApps, tmpCloseApps)) {
- mDisplayContent.mAtmService.mBackNavigationController.clearBackAnimations();
+ mDisplayContent.mAtmService.mBackNavigationController
+ .clearBackAnimations(false /* cancel */);
}
}
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index e8faff621165..a8cc2ae161cf 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -348,6 +348,11 @@ class BLASTSyncEngine {
wc.setSyncGroup(this);
}
wc.prepareSync();
+ if (wc.mSyncState == WindowContainer.SYNC_STATE_NONE && wc.mSyncGroup != null) {
+ Slog.w(TAG, "addToSync: unset SyncGroup " + wc.mSyncGroup.mSyncId
+ + " for non-sync " + wc);
+ wc.mSyncGroup = null;
+ }
if (mReady) {
mWm.mWindowPlacerLocked.requestTraversal();
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 5699fdd15c31..14ae918129f7 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -751,7 +751,7 @@ class BackNavigationController {
mObserver.sendResult(null /* result */);
}
if (isMonitorAnimationOrTransition()) {
- clearBackAnimations();
+ clearBackAnimations(true /* cancel */);
}
cancelPendingAnimation();
}
@@ -843,24 +843,23 @@ class BackNavigationController {
* Cleanup animation, this can either happen when legacy transition ready, or when the Shell
* transition finish.
*/
- void clearBackAnimations() {
- mAnimationHandler.clearBackAnimateTarget();
+ void clearBackAnimations(boolean cancel) {
+ mAnimationHandler.clearBackAnimateTarget(cancel);
mNavigationMonitor.stopMonitorTransition();
mWaitTransitionFinish = null;
}
/**
- * Called when a transition finished.
- * Handle the pending animation when the running transition finished.
+ * Handle the pending animation when the running transition finished, all the visibility change
+ * has applied so ready to start pending predictive back animation.
* @param targets The final animation targets derived in transition.
* @param finishedTransition The finished transition target.
*/
void onTransitionFinish(ArrayList<Transition.ChangeInfo> targets,
@NonNull Transition finishedTransition) {
if (finishedTransition == mWaitTransitionFinish) {
- clearBackAnimations();
+ clearBackAnimations(false /* cancel */);
}
-
if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) {
return;
}
@@ -994,7 +993,7 @@ class BackNavigationController {
mCloseAdaptor = createAdaptor(close, false, mSwitchType);
if (mCloseAdaptor.mAnimationTarget == null) {
Slog.w(TAG, "composeNewAnimations fail, skip");
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
return;
}
@@ -1013,7 +1012,7 @@ class BackNavigationController {
mOpenAnimAdaptor = new BackWindowAnimationAdaptorWrapper(true, mSwitchType, open);
if (!mOpenAnimAdaptor.isValid()) {
Slog.w(TAG, "compose animations fail, skip");
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
return;
}
mOpenActivities = openingActivities;
@@ -1025,7 +1024,7 @@ class BackNavigationController {
Slog.e(TAG, "Previous animation is running " + this);
return false;
}
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
if (close == null || open == null || open.length == 0 || open.length > 2) {
Slog.e(TAG, "reset animation with null target close: "
+ close + " open: " + Arrays.toString(open));
@@ -1114,7 +1113,19 @@ class BackNavigationController {
return false;
}
- void finishPresentAnimations() {
+ void finishPresentAnimations(boolean cancel) {
+ if (mOpenActivities != null) {
+ for (int i = mOpenActivities.length - 1; i >= 0; --i) {
+ final ActivityRecord resetActivity = mOpenActivities[i];
+ if (resetActivity.mDisplayContent.isFixedRotationLaunchingApp(resetActivity)) {
+ resetActivity.mDisplayContent
+ .continueUpdateOrientationForDiffOrienLaunchingApp();
+ }
+ if (resetActivity.mLaunchTaskBehind) {
+ restoreLaunchBehind(resetActivity, cancel);
+ }
+ }
+ }
if (mCloseAdaptor != null) {
mCloseAdaptor.mTarget.cancelAnimation();
mCloseAdaptor = null;
@@ -1123,15 +1134,6 @@ class BackNavigationController {
mOpenAnimAdaptor.cleanUp(mStartingSurfaceTargetMatch);
mOpenAnimAdaptor = null;
}
-
- if (mOpenActivities != null) {
- for (int i = mOpenActivities.length - 1; i >= 0; --i) {
- final ActivityRecord resetActivity = mOpenActivities[i];
- if (resetActivity.mLaunchTaskBehind) {
- restoreLaunchBehind(resetActivity);
- }
- }
- }
}
void markStartingSurfaceMatch(SurfaceControl.Transaction reparentTransaction) {
@@ -1142,10 +1144,10 @@ class BackNavigationController {
mOpenAnimAdaptor.reparentWindowlessSurfaceToTarget(reparentTransaction);
}
- void clearBackAnimateTarget() {
+ void clearBackAnimateTarget(boolean cancel) {
if (mComposed) {
mComposed = false;
- finishPresentAnimations();
+ finishPresentAnimations(cancel);
}
mWaitTransition = false;
mStartingSurfaceTargetMatch = false;
@@ -1661,7 +1663,7 @@ class BackNavigationController {
return;
}
if (!triggerBack) {
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
} else {
mWaitTransition = true;
}
@@ -1744,25 +1746,23 @@ class BackNavigationController {
true /* notifyClients */);
}
- private static void restoreLaunchBehind(@NonNull ActivityRecord activity) {
+ private static void restoreLaunchBehind(@NonNull ActivityRecord activity, boolean cancel) {
if (!activity.isAttached()) {
// The activity was detached from hierarchy.
return;
}
-
- if (activity.mDisplayContent.isFixedRotationLaunchingApp(activity)) {
- activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
- }
-
- // Restore the launch-behind state.
- activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
activity.mLaunchTaskBehind = false;
- // Ignore all change
- activity.mTransitionController.mSnapshotController
- .mActivitySnapshotController.clearOnBackPressedActivities();
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Setting Activity.mLauncherTaskBehind to false. Activity=%s",
activity);
+ if (cancel) {
+ // Restore the launch-behind state
+ // TODO b/347168362 Change status directly during collecting for a transition.
+ activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
+ // Ignore all change
+ activity.mTransitionController.mSnapshotController
+ .mActivitySnapshotController.clearOnBackPressedActivities();
+ }
}
void checkAnimationReady(WallpaperController wallpaperController) {
@@ -1782,7 +1782,7 @@ class BackNavigationController {
if (!mBackAnimationInProgress) {
// gesture is already finished, do not start animation
if (mPendingAnimation != null) {
- clearBackAnimations();
+ clearBackAnimations(true /* cancel */);
mPendingAnimation = null;
}
return;
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index e396f2b0e18f..bf347597ef0b 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -35,7 +35,6 @@ import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
/**
* Class that is able to combine multiple client lifecycle transition requests and/or callbacks,
@@ -72,9 +71,8 @@ class ClientLifecycleManager {
* @throws RemoteException
*
* @see ClientTransaction
- * @deprecated use {@link #scheduleTransactionItem(IApplicationThread, ClientTransactionItem)}.
*/
- @Deprecated
+ @VisibleForTesting
void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction.getClient();
try {
@@ -114,22 +112,13 @@ class ClientLifecycleManager {
*/
void scheduleTransactionItem(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem) throws RemoteException {
- // The behavior is different depending on the flag.
- // When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to
- // dispatch all pending transactions at once.
- if (Flags.bundleClientTransactionFlag()) {
- final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
- clientTransaction.addTransactionItem(transactionItem);
-
- onClientTransactionItemScheduled(clientTransaction,
- false /* shouldDispatchImmediately */);
- } else {
- // TODO(b/260873529): cleanup after launch.
- final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- clientTransaction.addTransactionItem(transactionItem);
+ // Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending
+ // transactions at once.
+ final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
+ clientTransaction.addTransactionItem(transactionItem);
- scheduleTransaction(clientTransaction);
- }
+ onClientTransactionItemScheduled(clientTransaction,
+ false /* shouldDispatchImmediately */);
}
void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client,
@@ -156,27 +145,18 @@ class ClientLifecycleManager {
@NonNull ClientTransactionItem transactionItem,
@NonNull ActivityLifecycleItem lifecycleItem,
boolean shouldDispatchImmediately) throws RemoteException {
- // The behavior is different depending on the flag.
- // When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to
- // dispatch all pending transactions at once.
- if (Flags.bundleClientTransactionFlag()) {
- final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
- clientTransaction.addTransactionItem(transactionItem);
- clientTransaction.addTransactionItem(lifecycleItem);
+ // Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending
+ // transactions at once.
+ final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
+ clientTransaction.addTransactionItem(transactionItem);
+ clientTransaction.addTransactionItem(lifecycleItem);
- onClientTransactionItemScheduled(clientTransaction, shouldDispatchImmediately);
- } else {
- // TODO(b/260873529): cleanup after launch.
- final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- clientTransaction.addTransactionItem(transactionItem);
- clientTransaction.addTransactionItem(lifecycleItem);
- scheduleTransaction(clientTransaction);
- }
+ onClientTransactionItemScheduled(clientTransaction, shouldDispatchImmediately);
}
/** Executes all the pending transactions. */
void dispatchPendingTransactions() {
- if (!Flags.bundleClientTransactionFlag() || mPendingTransactions.isEmpty()) {
+ if (mPendingTransactions.isEmpty()) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionsDispatched");
@@ -196,9 +176,6 @@ class ClientLifecycleManager {
/** Executes the pending transaction for the given client process. */
void dispatchPendingTransaction(@NonNull IApplicationThread client) {
- if (!Flags.bundleClientTransactionFlag()) {
- return;
- }
final ClientTransaction pendingTransaction = mPendingTransactions.remove(client.asBinder());
if (pendingTransaction != null) {
try {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ad711cb2af31..7ffffe97b646 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5047,9 +5047,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- if (!com.android.window.flags.Flags.removePrepareSurfaceInPlacement()) {
- prepareSurfaces();
- }
// This should be called after the insets have been dispatched to clients and we have
// committed finish drawing windows.
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index e0d2035118ad..85eeab14f4e9 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -103,9 +103,6 @@ final class LetterboxUiController {
private final ActivityRecord mActivityRecord;
- // TODO(b/265576778): Cache other overrides as well.
-
-
private boolean mShowWallpaperForLetterboxBackground;
// TODO(b/315140179): Make mUserAspectRatio final
@@ -153,7 +150,7 @@ final class LetterboxUiController {
@VisibleForTesting
int getSetOrientationRequestCounter() {
- return getAppCompatCapability().getAppCompatOrientationCapability()
+ return getAppCompatOverrides().getAppCompatOrientationOverrides()
.getSetOrientationRequestCounter();
}
@@ -170,7 +167,7 @@ final class LetterboxUiController {
* </ul>
*/
boolean shouldSendFakeFocus() {
- return getAppCompatCapability().shouldSendFakeFocus();
+ return getAppCompatOverrides().shouldSendFakeFocus();
}
/**
@@ -186,7 +183,7 @@ final class LetterboxUiController {
* </ul>
*/
boolean shouldOverrideMinAspectRatio() {
- return getAppCompatCapability().shouldOverrideMinAspectRatio();
+ return getAppCompatOverrides().shouldOverrideMinAspectRatio();
}
/**
@@ -203,7 +200,7 @@ final class LetterboxUiController {
* </ul>
*/
boolean shouldOverrideMinAspectRatioForCamera() {
- return getAppCompatCapability().shouldOverrideMinAspectRatioForCamera();
+ return getAppCompatOverrides().shouldOverrideMinAspectRatioForCamera();
}
/**
@@ -219,7 +216,7 @@ final class LetterboxUiController {
* </ul>
*/
boolean shouldOverrideForceResizeApp() {
- return getAppCompatCapability().shouldOverrideForceResizeApp();
+ return getAppCompatOverrides().shouldOverrideForceResizeApp();
}
/**
@@ -233,7 +230,7 @@ final class LetterboxUiController {
* </ul>
*/
boolean shouldOverrideForceNonResizeApp() {
- return getAppCompatCapability().shouldOverrideForceNonResizeApp();
+ return getAppCompatOverrides().shouldOverrideForceNonResizeApp();
}
/**
@@ -241,7 +238,7 @@ final class LetterboxUiController {
* android.app.Activity#setRequestedOrientation}.
*/
void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) {
- getAppCompatCapability().getAppCompatOrientationCapability()
+ getAppCompatOverrides().getAppCompatOrientationOverrides()
.setRelaunchingAfterRequestedOrientationChanged(isRelaunching);
}
@@ -257,7 +254,7 @@ final class LetterboxUiController {
}
boolean isOverrideRespectRequestedOrientationEnabled() {
- return getAppCompatCapability().isOverrideRespectRequestedOrientationEnabled();
+ return getAppCompatOverrides().isOverrideRespectRequestedOrientationEnabled();
}
/**
@@ -274,11 +271,11 @@ final class LetterboxUiController {
* </ul>
*/
boolean shouldUseDisplayLandscapeNaturalOrientation() {
- return getAppCompatCapability().shouldUseDisplayLandscapeNaturalOrientation();
+ return getAppCompatOverrides().shouldUseDisplayLandscapeNaturalOrientation();
}
boolean isOverrideOrientationOnlyForCameraEnabled() {
- return getAppCompatCapability().isOverrideOrientationOnlyForCameraEnabled();
+ return getAppCompatOverrides().isOverrideOrientationOnlyForCameraEnabled();
}
/**
@@ -293,7 +290,7 @@ final class LetterboxUiController {
* </ul>
*/
boolean shouldRefreshActivityForCameraCompat() {
- return getAppCompatCapability().shouldRefreshActivityForCameraCompat();
+ return getAppCompatOverrides().shouldRefreshActivityForCameraCompat();
}
/**
@@ -311,7 +308,7 @@ final class LetterboxUiController {
* </ul>
*/
boolean shouldRefreshActivityViaPauseForCameraCompat() {
- return getAppCompatCapability().shouldRefreshActivityViaPauseForCameraCompat();
+ return getAppCompatOverrides().shouldRefreshActivityViaPauseForCameraCompat();
}
/**
@@ -326,7 +323,7 @@ final class LetterboxUiController {
* </ul>
*/
boolean shouldForceRotateForCameraCompat() {
- return getAppCompatCapability().shouldForceRotateForCameraCompat();
+ return getAppCompatOverrides().shouldForceRotateForCameraCompat();
}
/**
@@ -344,7 +341,7 @@ final class LetterboxUiController {
* </ul>
*/
boolean shouldApplyFreeformTreatmentForCameraCompat() {
- return getAppCompatCapability().shouldApplyFreeformTreatmentForCameraCompat();
+ return getAppCompatOverrides().shouldApplyFreeformTreatmentForCameraCompat();
}
@FreeformCameraCompatMode
@@ -609,7 +606,7 @@ final class LetterboxUiController {
// Don't resize to split screen size when in book mode if letterbox position is centered
return (isBookMode && isNotCenteredHorizontally || isTabletopMode && isLandscape)
|| isCameraCompatSplitScreenAspectRatioAllowed()
- && getAppCompatCapability().isCameraCompatTreatmentActive();
+ && getAppCompatOverrides().isCameraCompatTreatmentActive();
}
private float getDefaultMinAspectRatioForUnresizableApps() {
@@ -711,7 +708,7 @@ final class LetterboxUiController {
* Whether we should enable users to resize the current app.
*/
boolean shouldEnableUserAspectRatioSettings() {
- return getAppCompatCapability().shouldEnableUserAspectRatioSettings();
+ return getAppCompatOverrides().shouldEnableUserAspectRatioSettings();
}
/**
@@ -741,11 +738,11 @@ final class LetterboxUiController {
}
boolean isUserFullscreenOverrideEnabled() {
- return getAppCompatCapability().isUserFullscreenOverrideEnabled();
+ return getAppCompatOverrides().isUserFullscreenOverrideEnabled();
}
boolean isSystemOverrideToFullscreenEnabled() {
- return getAppCompatCapability().isSystemOverrideToFullscreenEnabled(mUserAspectRatio);
+ return getAppCompatOverrides().isSystemOverrideToFullscreenEnabled(mUserAspectRatio);
}
boolean hasFullscreenOverride() {
@@ -941,8 +938,8 @@ final class LetterboxUiController {
}
// TODO(b/346264992): Remove after AppCompatController refactoring
- private AppCompatCapability getAppCompatCapability() {
- return mActivityRecord.mAppCompatController.getAppCompatCapability();
+ private AppCompatOverrides getAppCompatOverrides() {
+ return mActivityRecord.mAppCompatController.getAppCompatOverrides();
}
/**
@@ -983,7 +980,7 @@ final class LetterboxUiController {
@VisibleForTesting
boolean shouldShowLetterboxUi(WindowState mainWindow) {
- if (getAppCompatCapability().getAppCompatOrientationCapability()
+ if (getAppCompatOverrides().getAppCompatOrientationOverrides()
.getIsRelaunchingAfterRequestedOrientationChanged()) {
return mLastShouldShowLetterboxUi;
}
@@ -1144,7 +1141,7 @@ final class LetterboxUiController {
}
boolean getIsRelaunchingAfterRequestedOrientationChanged() {
- return getAppCompatCapability().getAppCompatOrientationCapability()
+ return getAppCompatOverrides().getAppCompatOrientationOverrides()
.getIsRelaunchingAfterRequestedOrientationChanged();
}
diff --git a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
index 4d3b95b72073..c22b07a4efa1 100644
--- a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
+++ b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
@@ -104,10 +104,9 @@ public class RemoteDisplayChangeController {
final IDisplayChangeWindowCallback remoteCallback = createCallback(callback);
try {
- mService.mH.removeCallbacks(mTimeoutRunnable);
- mService.mH.postDelayed(mTimeoutRunnable, REMOTE_DISPLAY_CHANGE_TIMEOUT_MS);
mService.mDisplayChangeController.onDisplayChange(mDisplayContent.mDisplayId,
fromRotation, toRotation, newDisplayAreaInfo, remoteCallback);
+ mService.mH.postDelayed(mTimeoutRunnable, callback, REMOTE_DISPLAY_CHANGE_TIMEOUT_MS);
return true;
} catch (RemoteException e) {
Slog.e(TAG, "Exception while dispatching remote display-change", e);
@@ -118,6 +117,7 @@ public class RemoteDisplayChangeController {
private void onContinueTimedOut() {
Slog.e(TAG, "RemoteDisplayChange timed-out, UI might get messed-up after this.");
+ mService.mH.removeCallbacks(mTimeoutRunnable);
// timed-out, so run all continue callbacks and clear the list
synchronized (mService.mGlobalLock) {
for (int i = 0; i < mCallbacks.size(); ++i) {
@@ -172,9 +172,6 @@ public class RemoteDisplayChangeController {
// The "toIndex" is exclusive, so it needs +1 to clear the current calling callback.
mCallbacks.subList(0, idx + 1).clear();
final boolean completed = mCallbacks.isEmpty();
- if (completed) {
- mService.mH.removeCallbacks(mTimeoutRunnable);
- }
callback.onContinueRemoteDisplayChange(transaction);
if (completed) {
onCompleted();
@@ -198,6 +195,7 @@ public class RemoteDisplayChangeController {
}
mService.mH.post(() -> RemoteDisplayChangeController.this
.continueDisplayChange(callback, t));
+ mService.mH.removeCallbacks(mTimeoutRunnable, callback);
}
}
};
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 54ba47eeb441..6abd4887645b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -152,7 +152,6 @@ import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.utils.Slogf;
-import com.android.window.flags.Flags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -789,13 +788,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- if (Flags.bundleClientTransactionFlag()) {
- // mWmService.mResizingWindows is populated in #applySurfaceChangesTransaction()
- handleResizingWindows();
+ // mWmService.mResizingWindows is populated in #applySurfaceChangesTransaction()
+ handleResizingWindows();
+ clearFrameChangingWindows();
- // Called after #handleResizingWindows to include WindowStateResizeItem if any.
- mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
- }
+ // Called after #handleResizingWindows to include WindowStateResizeItem if any.
+ mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
@@ -839,11 +837,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- if (!Flags.bundleClientTransactionFlag()) {
- handleResizingWindows();
- }
- clearFrameChangingWindows();
-
if (mWmService.mDisplayFrozen) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"With display frozen, orientationChangeComplete=%b",
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c72087bfbe5f..f9c53aa35bd8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -47,8 +47,6 @@ import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -3586,15 +3584,29 @@ class Task extends TaskFragment {
? null : new PictureInPictureParams(top.pictureInPictureArgs);
}
- Rect getDisplayCutoutInsets() {
- if (mDisplayContent == null || getDisplayInfo().displayCutout == null) return null;
+ /** @return The display cutout insets where the main window is not allowed to extend to. */
+ @NonNull Rect getDisplayCutoutInsets() {
+ final Rect displayCutoutInsets = new Rect();
+ if (mDisplayContent == null || getDisplayInfo().displayCutout == null) {
+ return displayCutoutInsets;
+ }
final WindowState w = getTopVisibleAppMainWindow();
- final int displayCutoutMode = w == null
- ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
- : w.getAttrs().layoutInDisplayCutoutMode;
- return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
- ? null : getDisplayInfo().displayCutout.getSafeInsets();
+ final Rect displayFrame;
+ if (w != null && w.mHaveFrame) {
+ displayFrame = w.getDisplayFrame();
+ } else {
+ displayFrame = mDisplayContent.getBounds();
+ displayFrame.inset(getDisplayInfo().displayCutout.getSafeInsets());
+ }
+ final Rect taskBounds = getBounds();
+ if (displayCutoutInsets.setIntersect(taskBounds, displayFrame)) {
+ displayCutoutInsets.set(
+ displayCutoutInsets.left - taskBounds.left,
+ displayCutoutInsets.top - taskBounds.top,
+ taskBounds.right - displayCutoutInsets.right,
+ taskBounds.bottom - displayCutoutInsets.bottom);
+ }
+ return displayCutoutInsets;
}
/**
@@ -4792,8 +4804,7 @@ class Task extends TaskFragment {
// task is still updated by core. Otherwise if the task is collected (e.g.
// rotation change) after leaving this scope, the visibility operation will be
// put in sync transaction, then it is not synced with reparent.
- if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement()
- && lastParentBeforePip.mSyncState == SYNC_STATE_NONE) {
+ if (lastParentBeforePip.mSyncState == SYNC_STATE_NONE) {
lastParentBeforePip.prepareSurfaces();
// If the moveToFront is a part of finishing transition, then make sure
// the z-order of tasks are up-to-date.
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index acdb66a49251..3cd071bd329e 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -82,7 +82,6 @@ import android.app.IApplicationThread;
import android.app.ResultInfo;
import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityResultItem;
-import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.ResumeActivityItem;
@@ -1604,9 +1603,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
try {
final IApplicationThread appThread = next.app.getThread();
- final ClientTransaction transaction = Flags.bundleClientTransactionFlag()
- ? null
- : ClientTransaction.obtain(appThread);
// Deliver all pending results.
final ArrayList<ResultInfo> a = next.results;
if (a != null) {
@@ -1617,24 +1613,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
next.token, a);
- if (transaction == null) {
- mAtmService.getLifecycleManager().scheduleTransactionItem(
- appThread, activityResultItem);
- } else {
- transaction.addTransactionItem(activityResultItem);
- }
+ mAtmService.getLifecycleManager().scheduleTransactionItem(
+ appThread, activityResultItem);
}
}
if (next.newIntents != null) {
final NewIntentItem newIntentItem = NewIntentItem.obtain(
next.token, next.newIntents, true /* resume */);
- if (transaction == null) {
- mAtmService.getLifecycleManager().scheduleTransactionItem(
- appThread, newIntentItem);
- } else {
- transaction.addTransactionItem(newIntentItem);
- }
+ mAtmService.getLifecycleManager().scheduleTransactionItem(
+ appThread, newIntentItem);
}
// Well the app will no longer be stopped.
@@ -1651,13 +1639,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(
next.token, topProcessState, dc.isNextTransitionForward(),
next.shouldSendCompatFakeFocus());
- if (transaction == null) {
- mAtmService.getLifecycleManager().scheduleTransactionItem(
- appThread, resumeActivityItem);
- } else {
- transaction.addTransactionItem(resumeActivityItem);
- mAtmService.getLifecycleManager().scheduleTransaction(transaction);
- }
+ mAtmService.getLifecycleManager().scheduleTransactionItem(
+ appThread, resumeActivityItem);
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next);
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index c4e932abecd3..26315f9cc2c3 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -171,11 +171,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
TaskFragmentOrganizerState(@NonNull ITaskFragmentOrganizer organizer, int pid, int uid,
boolean isSystemOrganizer) {
- if (Flags.bundleClientTransactionFlag()) {
- mAppThread = getAppThread(pid, uid);
- } else {
- mAppThread = null;
- }
+ mAppThread = getAppThread(pid, uid);
mOrganizer = organizer;
mOrganizerPid = pid;
mOrganizerUid = uid;
@@ -431,13 +427,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
return;
}
try {
- if (Flags.bundleClientTransactionFlag()) {
- // Dispatch through IApplicationThread to ensure the binder call is in order
- // with ClientTransaction.
- mAppThread.scheduleTaskFragmentTransaction(mOrganizer, transaction);
- } else {
- mOrganizer.onTransactionReady(transaction);
- }
+ // Dispatch through IApplicationThread to ensure the binder call is in order
+ // with ClientTransaction.
+ mAppThread.scheduleTaskFragmentTransaction(mOrganizer, transaction);
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
return;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 25721280d97e..e698a3d95841 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -68,6 +68,8 @@ import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
import android.annotation.IntDef;
@@ -1195,7 +1197,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
}
mController.mFinishingTransition = this;
-
if (mTransientHideTasks != null && !mTransientHideTasks.isEmpty()) {
// The transient hide tasks could be occluded now, e.g. returning to home. So trigger
// the update to make the activities in the tasks invisible-requested, then the next
@@ -1380,7 +1381,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// If the activity was just inserted to an invisible task, it will keep INITIALIZING
// state. Then no need to notify the callback to avoid clearing some states
// unexpectedly, e.g. launch-task-behind.
- if (ar.isVisibleRequested() || !ar.isState(ActivityRecord.State.INITIALIZING)) {
+ // However, skip dispatch to predictive back animation target, because it only set
+ // launch-task-behind to make the activity become visible.
+ if ((ar.isVisibleRequested() || !ar.isState(ActivityRecord.State.INITIALIZING))
+ && !ar.isAnimating(PARENTS, ANIMATION_TYPE_PREDICT_BACK)) {
mController.dispatchLegacyAppTransitionFinished(ar);
}
@@ -3231,6 +3235,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_WINDOW_MANAGER, TAG, cookie);
}
+ boolean hasChanged(WindowContainer wc) {
+ final ChangeInfo chg = mChanges.get(wc);
+ if (chg == null) return false;
+ return chg.hasChanged();
+ }
+
@VisibleForTesting
static class ChangeInfo {
private static final int FLAG_NONE = 0;
@@ -3513,10 +3523,15 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
Slog.e(TAG, "#" + mSyncId + " readiness timeout, used=" + mReadyTrackerOld.mUsed
+ " deferReadyDepth=" + mReadyTrackerOld.mDeferReadyDepth
+ " group=" + mReadyTrackerOld.mReadyGroups);
- return;
+ } else {
+ Slog.e(TAG, "#" + mSyncId + " met conditions: " + mReadyTracker.mMet);
+ Slog.e(TAG, "#" + mSyncId + " unmet conditions: " + mReadyTracker.mConditions);
+ }
+ // Make sure the pending display change can be applied (especially DC#mWaitingForConfig)
+ // in case shell hasn't called WindowOrganizerController#startTransition yet.
+ if (mState < STATE_STARTED && this == mController.getCollectingTransition()) {
+ applyDisplayChangeIfNeeded(new ArraySet<>());
}
- Slog.e(TAG, "#" + mSyncId + " met conditions: " + mReadyTracker.mMet);
- Slog.e(TAG, "#" + mSyncId + " unmet conditions: " + mReadyTracker.mConditions);
}
/**
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 86440ac3782d..43f7ecc6ab1c 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -335,13 +335,13 @@ class WallpaperController {
}
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
final WallpaperWindowToken token = mWallpaperTokens.get(i);
- token.setVisibility(false);
if (token.isVisible()) {
ProtoLog.d(WM_DEBUG_WALLPAPER,
"Hiding wallpaper %s from %s target=%s prev=%s callers=%s",
token, winGoingAway, mWallpaperTarget, mPrevWallpaperTarget,
Debug.getCallers(5));
}
+ token.setVisibility(false);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 6dbd259b67c0..1f31af68c693 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3986,6 +3986,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
+ * Returns {@code true} if this window container belongs to a different sync group than the
+ * given group.
+ */
+ boolean isDifferentSyncGroup(@Nullable BLASTSyncEngine.SyncGroup group) {
+ if (group == null) return false;
+ final BLASTSyncEngine.SyncGroup thisGroup = getSyncGroup();
+ if (thisGroup == null || group == thisGroup) return false;
+ Slog.d(TAG, this + " uses a different SyncGroup, current=" + thisGroup.mSyncId
+ + " given=" + group.mSyncId);
+ return true;
+ }
+
+ /**
* Recursively finishes/cleans-up sync state of this subtree and collects all the sync
* transactions into `outMergedTransaction`.
* @param outMergedTransaction A transaction to merge all the recorded sync operations into.
@@ -3994,10 +4007,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
void finishSync(Transaction outMergedTransaction, @Nullable BLASTSyncEngine.SyncGroup group,
boolean cancel) {
- if (mSyncState == SYNC_STATE_NONE) return;
- final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
- // If it's null, then we need to clean-up anyways.
- if (syncGroup != null && group != syncGroup) return;
+ if (mSyncState == SYNC_STATE_NONE) {
+ if (mSyncGroup != null) {
+ Slog.e(TAG, "finishSync: stale group " + mSyncGroup.mSyncId + " of " + this);
+ mSyncGroup = null;
+ }
+ return;
+ }
+ if (isDifferentSyncGroup(group)) return;
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "finishSync cancel=%b for %s", cancel, this);
outMergedTransaction.merge(mSyncTransaction);
for (int i = mChildren.size() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1c00fbbf5c92..b8780726f992 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
@@ -309,14 +310,15 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private volatile boolean mWasStoppedLogged;
// The bits used for mActivityStateFlags.
- private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
- private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
- private static final int ACTIVITY_STATE_FLAG_IS_STOPPING = 1 << 18;
- private static final int ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING = 1 << 19;
- private static final int ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE = 1 << 20;
- private static final int ACTIVITY_STATE_FLAG_HAS_RESUMED = 1 << 21;
- private static final int ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK = 1 << 22;
- private static final int ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER = 0x0000ffff;
+ public static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
+ public static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
+ public static final int ACTIVITY_STATE_FLAG_IS_STOPPING = 1 << 18;
+ public static final int ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING = 1 << 19;
+ public static final int ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE = 1 << 20;
+ public static final int ACTIVITY_STATE_FLAG_HAS_RESUMED = 1 << 21;
+ public static final int ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK = 1 << 22;
+ public static final int ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN = 1 << 23;
+ public static final int ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER = 0x0000ffff;
/**
* The state for oom-adjustment calculation. The higher 16 bits are the activity states, and the
@@ -1211,31 +1213,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
}
- public interface ComputeOomAdjCallback {
- void onVisibleActivity();
- void onPausedActivity();
- void onStoppingActivity(boolean finishing);
- void onOtherActivity();
- }
-
/**
- * Returns the minimum task layer rank. It should only be called if {@link #hasActivities}
- * returns {@code true}.
+ * Returns the current ACTIVITY_STATE_FLAG_* of this process. It should only be called if
+ * {@link #hasActivities} returns {@code true}.
*/
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
- public int computeOomAdjFromActivities(ComputeOomAdjCallback callback) {
- final int flags = mActivityStateFlags;
- if ((flags & ACTIVITY_STATE_FLAG_IS_VISIBLE) != 0) {
- callback.onVisibleActivity();
- } else if ((flags & ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED) != 0) {
- callback.onPausedActivity();
- } else if ((flags & ACTIVITY_STATE_FLAG_IS_STOPPING) != 0) {
- callback.onStoppingActivity(
- (flags & ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING) != 0);
- } else {
- callback.onOtherActivity();
- }
- return flags & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
+ public int getActivityStateFlags() {
+ return mActivityStateFlags;
}
void computeProcessActivityState() {
@@ -1256,14 +1240,25 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
stateFlags |= ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE;
}
final Task task = r.getTask();
- if (task != null && task.mLayerRank != Task.LAYER_RANK_INVISIBLE) {
+ if (task == null) {
+ Slog.e(TAG, "Unexpected detached " + r + " in " + this);
+ continue;
+ }
+ if (task.mLayerRank != Task.LAYER_RANK_INVISIBLE) {
stateFlags |= ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK;
}
if (r.isVisibleRequested()) {
if (r.isState(RESUMED)) {
stateFlags |= ACTIVITY_STATE_FLAG_HAS_RESUMED;
+ final int windowingMode = r.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_MULTI_WINDOW
+ && com.android.window.flags.Flags
+ .processPriorityPolicyForMultiWindowMode()
+ && task.getAdjacentTask() != null) {
+ stateFlags |= ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN;
+ }
}
- if (task != null && minTaskLayer > 0) {
+ if (minTaskLayer > 0) {
final int layer = task.mLayerRank;
if (layer >= 0 && minTaskLayer > layer) {
minTaskLayer = layer;
@@ -2107,6 +2102,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
pw.print("V|");
if ((stateFlags & ACTIVITY_STATE_FLAG_HAS_RESUMED) != 0) {
pw.print("R|");
+ if ((stateFlags & ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN) != 0) {
+ pw.print("RS|");
+ }
}
} else if ((stateFlags & ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED) != 0) {
pw.print("P|");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index dcd4bd68c3fc..f72e82a2c073 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -96,6 +96,7 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+import static android.util.SequenceUtils.getNextSeq;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
@@ -3652,6 +3653,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
outFrames.compatScale = getCompatScaleForClient();
+ outFrames.seq = getNextSeq(mLastReportedFrames.seq);
if (mLastReportedFrames != outFrames) {
mLastReportedFrames.setTo(outFrames);
}
@@ -3682,7 +3684,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void fillInsetsState(@NonNull InsetsState outInsetsState, boolean copySources) {
+ final int lastSeq = mLastReportedInsetsState.getSeq();
outInsetsState.set(getCompatInsetsState(), copySources);
+ outInsetsState.setSeq(getNextSeq(lastSeq));
if (outInsetsState != mLastReportedInsetsState) {
// No need to copy for the recorded.
mLastReportedInsetsState.set(outInsetsState, false /* copySources */);
@@ -3691,9 +3695,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void fillInsetsSourceControls(@NonNull InsetsSourceControl.Array outArray,
boolean copyControls) {
+ final int lastSeq = mLastReportedInsetsState.getSeq();
final InsetsSourceControl[] controls =
getDisplayContent().getInsetsStateController().getControlsForDispatch(this);
outArray.set(controls, copyControls);
+ outArray.setSeq(getNextSeq(lastSeq));
if (outArray != mLastReportedActiveControls) {
// No need to copy for the recorded.
mLastReportedActiveControls.setTo(outArray, false /* copyControls */);
@@ -3754,31 +3760,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final boolean isDragResizing = isDragResizing();
markRedrawForSyncReported();
-
- if (Flags.bundleClientTransactionFlag()) {
- getProcess().scheduleClientTransactionItem(
- WindowStateResizeItem.obtain(mClient, mLastReportedFrames, reportDraw,
- mLastReportedConfiguration, mLastReportedInsetsState, forceRelayout,
- alwaysConsumeSystemBars, displayId,
- syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
- mLastReportedActivityWindowInfo));
- onResizePostDispatched(drawPending, prevRotation, displayId);
- } else {
- // TODO(b/301870955): cleanup after launch
- try {
- mClient.resized(mLastReportedFrames, reportDraw, mLastReportedConfiguration,
- mLastReportedInsetsState, forceRelayout, alwaysConsumeSystemBars, displayId,
+ getProcess().scheduleClientTransactionItem(
+ WindowStateResizeItem.obtain(mClient, mLastReportedFrames, reportDraw,
+ mLastReportedConfiguration, mLastReportedInsetsState, forceRelayout,
+ alwaysConsumeSystemBars, displayId,
syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
- mLastReportedActivityWindowInfo);
- onResizePostDispatched(drawPending, prevRotation, displayId);
- } catch (RemoteException e) {
- // Cancel orientation change of this window to avoid blocking unfreeze display.
- setOrientationChanging(false);
- mLastFreezeDuration = (int) (SystemClock.elapsedRealtime()
- - mWmService.mDisplayFreezeTime);
- Slog.w(TAG, "Failed to report 'resized' to " + this + " due to " + e);
- }
- }
+ mLastReportedActivityWindowInfo));
+ onResizePostDispatched(drawPending, prevRotation, displayId);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -5008,12 +4996,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
final Point position = new Point();
- if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement()) {
- transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
- position);
- } else {
- position.set(mSurfacePosition);
- }
+ transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
+ position);
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, position, false /* canSkipFirstFrame */,
0 /* windowCornerRadius */),
@@ -5791,8 +5775,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
void finishSync(Transaction outMergedTransaction, BLASTSyncEngine.SyncGroup group,
boolean cancel) {
- final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
- if (syncGroup != null && group != syncGroup) return;
+ if (isDifferentSyncGroup(group)) return;
mPrepareSyncSeqId = 0;
if (cancel) {
// This is leaving sync so any buffers left in the sync have a chance of
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index 50d48b7d30e7..5c4db24767f9 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -45,6 +45,7 @@ enum class DeviceType {
TOUCHSCREEN,
DPAD,
STYLUS,
+ ROTARY_ENCODER,
};
static unique_fd invalidFd() {
@@ -114,6 +115,10 @@ static unique_fd openUinput(const char* readableName, jint vendorId, jint produc
ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
break;
+ case DeviceType::ROTARY_ENCODER:
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
+ break;
default:
ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType));
return invalidFd();
@@ -312,6 +317,13 @@ static jlong nativeOpenUinputStylus(JNIEnv* env, jobject thiz, jstring name, jin
return fd.ok() ? reinterpret_cast<jlong>(new VirtualStylus(std::move(fd))) : INVALID_PTR;
}
+static jlong nativeOpenUinputRotaryEncoder(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+ jint productId, jstring phys, jint height, jint width) {
+ auto fd = openUinputJni(env, name, vendorId, productId, phys, DeviceType::ROTARY_ENCODER,
+ /* screenHeight= */ 0, /* screenWidth= */ 0);
+ return fd.ok() ? reinterpret_cast<jlong>(new VirtualRotaryEncoder(std::move(fd))) : INVALID_PTR;
+}
+
static void nativeCloseUinput(JNIEnv* env, jobject thiz, jlong ptr) {
VirtualInputDevice* virtualInputDevice = reinterpret_cast<VirtualInputDevice*>(ptr);
delete virtualInputDevice;
@@ -381,6 +393,13 @@ static bool nativeWriteStylusButtonEvent(JNIEnv* env, jobject thiz, jlong ptr, j
std::chrono::nanoseconds(eventTimeNanos));
}
+static bool nativeWriteRotaryEncoderScrollEvent(JNIEnv* env, jobject thiz, jlong ptr,
+ jfloat scrollAmount, jlong eventTimeNanos) {
+ VirtualRotaryEncoder* virtualRotaryEncoder = reinterpret_cast<VirtualRotaryEncoder*>(ptr);
+ return virtualRotaryEncoder->writeScrollEvent(scrollAmount,
+ std::chrono::nanoseconds(eventTimeNanos));
+}
+
static JNINativeMethod methods[] = {
{"nativeOpenUinputDpad", "(Ljava/lang/String;IILjava/lang/String;)J",
(void*)nativeOpenUinputDpad},
@@ -392,6 +411,8 @@ static JNINativeMethod methods[] = {
(void*)nativeOpenUinputTouchscreen},
{"nativeOpenUinputStylus", "(Ljava/lang/String;IILjava/lang/String;II)J",
(void*)nativeOpenUinputStylus},
+ {"nativeOpenUinputRotaryEncoder", "(Ljava/lang/String;IILjava/lang/String;)J",
+ (void*)nativeOpenUinputRotaryEncoder},
{"nativeCloseUinput", "(J)V", (void*)nativeCloseUinput},
{"nativeWriteDpadKeyEvent", "(JIIJ)Z", (void*)nativeWriteDpadKeyEvent},
{"nativeWriteKeyEvent", "(JIIJ)Z", (void*)nativeWriteKeyEvent},
@@ -401,6 +422,8 @@ static JNINativeMethod methods[] = {
{"nativeWriteScrollEvent", "(JFFJ)Z", (void*)nativeWriteScrollEvent},
{"nativeWriteStylusMotionEvent", "(JIIIIIIIJ)Z", (void*)nativeWriteStylusMotionEvent},
{"nativeWriteStylusButtonEvent", "(JIIJ)Z", (void*)nativeWriteStylusButtonEvent},
+ {"nativeWriteRotaryEncoderScrollEvent", "(JFJ)Z",
+ (void*)nativeWriteRotaryEncoderScrollEvent},
};
int register_android_server_companion_virtual_InputController(JNIEnv* env) {
diff --git a/services/fakes/Android.bp b/services/fakes/Android.bp
index 148054b31e89..d44bb5ae302c 100644
--- a/services/fakes/Android.bp
+++ b/services/fakes/Android.bp
@@ -16,5 +16,5 @@ filegroup {
"java/**/*.java",
],
path: "java",
- visibility: ["//frameworks/base"],
+ visibility: ["//frameworks/base/ravenwood:__subpackages__"],
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
index 79943f6330ba..f9f45057f57f 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
@@ -65,6 +65,7 @@ public final class UserDataRepositoryTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
SecureSettingsWrapper.startTestMode();
+
mHandler = new Handler(Looper.getMainLooper());
mBindingControllerFactory = new IntFunction<InputMethodBindingController>() {
@@ -85,8 +86,8 @@ public final class UserDataRepositoryTest {
// Create UserDataRepository and capture the user lifecycle listener
final var captor = ArgumentCaptor.forClass(UserManagerInternal.UserLifecycleListener.class);
final var bindingControllerFactorySpy = spy(mBindingControllerFactory);
- final var repository = new UserDataRepository(mHandler, mMockUserManagerInternal,
- bindingControllerFactorySpy);
+ final var repository = new UserDataRepository(mHandler,
+ mMockUserManagerInternal, bindingControllerFactorySpy);
verify(mMockUserManagerInternal, times(1)).addUserLifecycleListener(captor.capture());
final var listener = captor.getValue();
@@ -112,7 +113,8 @@ public final class UserDataRepositoryTest {
public void testUserDataRepository_removesUserInfoOnUserRemovedEvent() {
// Create UserDataRepository and capture the user lifecycle listener
final var captor = ArgumentCaptor.forClass(UserManagerInternal.UserLifecycleListener.class);
- final var repository = new UserDataRepository(mHandler, mMockUserManagerInternal,
+ final var repository = new UserDataRepository(mHandler,
+ mMockUserManagerInternal,
userId -> new InputMethodBindingController(userId, mMockInputMethodManagerService));
verify(mMockUserManagerInternal, times(1)).addUserLifecycleListener(captor.capture());
@@ -134,8 +136,8 @@ public final class UserDataRepositoryTest {
@Test
public void testGetOrCreate() {
- final var repository = new UserDataRepository(mHandler, mMockUserManagerInternal,
- mBindingControllerFactory);
+ final var repository = new UserDataRepository(mHandler,
+ mMockUserManagerInternal, mBindingControllerFactory);
synchronized (ImfLock.class) {
final var userData = repository.getOrCreate(ANY_USER_ID);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index 88d3238861f5..d7936fece089 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -255,7 +256,8 @@ public final class DisplayBrightnessControllerTest {
@Test
public void setBrightnessSetsInBrightnessSetting() {
float brightnessValue = 0.3f;
- mDisplayBrightnessController.setBrightness(brightnessValue);
+ float maxBrightnessValue = 0.65f;
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightnessValue);
verify(mBrightnessSetting).setBrightness(brightnessValue);
}
@@ -266,20 +268,24 @@ public final class DisplayBrightnessControllerTest {
// Sets the appropriate value when valid, and not equal to the current brightness
float brightnessValue = 0.3f;
- mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue);
+ float maxBrightnessValue = 0.65f;
+ mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue,
+ maxBrightnessValue);
assertEquals(mDisplayBrightnessController.getCurrentBrightness(), brightnessValue, 0.0f);
verify(mBrightnessChangeExecutor).execute(mOnBrightnessChangeRunnable);
verify(mBrightnessSetting).setBrightness(brightnessValue);
// Does nothing if the value is invalid
- mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN);
+ clearInvocations(mBrightnessSetting);
+ mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN, maxBrightnessValue);
verifyNoMoreInteractions(mBrightnessChangeExecutor, mBrightnessSetting);
// Does nothing if the value is same as the current brightness
brightnessValue = 0.2f;
mDisplayBrightnessController.setAndNotifyCurrentScreenBrightness(brightnessValue);
verify(mBrightnessChangeExecutor, times(2)).execute(mOnBrightnessChangeRunnable);
- mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue);
+ mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue,
+ maxBrightnessValue);
verifyNoMoreInteractions(mBrightnessChangeExecutor, mBrightnessSetting);
}
@@ -366,7 +372,7 @@ public final class DisplayBrightnessControllerTest {
when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits);
mDisplayBrightnessController.setAutomaticBrightnessController(
automaticBrightnessController);
- verify(mBrightnessSetting).setBrightness(brightness);
+ verify(mBrightnessSetting).setBrightnessNoNotify(brightness);
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
clearInvocations(automaticBrightnessController, mBrightnessSetting);
@@ -378,7 +384,7 @@ public final class DisplayBrightnessControllerTest {
when(mBrightnessSetting.getBrightness()).thenReturn(brightness);
mDisplayBrightnessController.setAutomaticBrightnessController(
automaticBrightnessController);
- verify(mBrightnessSetting, never()).setBrightness(brightness);
+ verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
clearInvocations(automaticBrightnessController, mBrightnessSetting);
@@ -395,15 +401,73 @@ public final class DisplayBrightnessControllerTest {
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
verifyZeroInteractions(automaticBrightnessController);
verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
- verify(mBrightnessSetting, never()).setBrightness(brightness);
+ verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
}
@Test
+ public void testDoesNotSetBrightnessNits_settingMaxBrightnessAndStoredValueGreater() {
+ float brightnessValue = 0.65f;
+ float maxBrightness = 0.65f;
+ float nits = 200f;
+ float storedNits = 300f;
+
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(storedNits);
+ AutomaticBrightnessController automaticBrightnessController = mock(
+ AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue)).thenReturn(nits);
+ mDisplayBrightnessController.mAutomaticBrightnessController = automaticBrightnessController;
+
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightness);
+
+ verify(mBrightnessSetting, never()).setBrightnessNitsForDefaultDisplay(anyFloat());
+ }
+
+ @Test
+ public void testSetsBrightnessNits_storedValueLower() {
+ float brightnessValue = 0.65f;
+ float maxBrightness = 0.65f;
+ float nits = 200f;
+ float storedNits = 100f;
+
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(storedNits);
+ AutomaticBrightnessController automaticBrightnessController = mock(
+ AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue)).thenReturn(nits);
+ mDisplayBrightnessController.mAutomaticBrightnessController = automaticBrightnessController;
+
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightness);
+
+ verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits);
+ }
+
+ @Test
+ public void testSetsBrightnessNits_lowerThanMax() {
+ float brightnessValue = 0.60f;
+ float maxBrightness = 0.65f;
+ float nits = 200f;
+ float storedNits = 300f;
+
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(storedNits);
+ AutomaticBrightnessController automaticBrightnessController = mock(
+ AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue)).thenReturn(nits);
+ mDisplayBrightnessController.mAutomaticBrightnessController = automaticBrightnessController;
+
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightness);
+
+ verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits);
+ }
+
+
+ @Test
public void testChangeBrightnessNitsWhenUserChanges() {
float brightnessValue1 = 0.3f;
float nits1 = 200f;
+ int userSerial1 = 1;
float brightnessValue2 = 0.5f;
float nits2 = 300f;
+ int userSerial2 = 2;
+ float maxBrightness = 0.65f;
AutomaticBrightnessController automaticBrightnessController = mock(
AutomaticBrightnessController.class);
when(automaticBrightnessController.convertToNits(brightnessValue1)).thenReturn(nits1);
@@ -417,13 +481,13 @@ public final class DisplayBrightnessControllerTest {
verify(automaticBrightnessStrategy)
.setAutomaticBrightnessController(automaticBrightnessController);
- mDisplayBrightnessController.setBrightness(brightnessValue1, 1 /* user-serial */);
- verify(mBrightnessSetting).setUserSerial(1);
+ mDisplayBrightnessController.setBrightness(brightnessValue1, userSerial1, maxBrightness);
+ verify(mBrightnessSetting).setUserSerial(userSerial1);
verify(mBrightnessSetting).setBrightness(brightnessValue1);
verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits1);
- mDisplayBrightnessController.setBrightness(brightnessValue2, 2 /* user-serial */);
- verify(mBrightnessSetting).setUserSerial(2);
+ mDisplayBrightnessController.setBrightness(brightnessValue2, userSerial2, maxBrightness);
+ verify(mBrightnessSetting).setUserSerial(userSerial2);
verify(mBrightnessSetting).setBrightness(brightnessValue2);
verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits2);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 488ce66ca99b..6366f24a27e1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -72,14 +72,12 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.AdditionalAnswers.answer;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -486,27 +484,22 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).hasActivities();
- doAnswer(answer(callback -> {
- Field field = callback.getClass().getDeclaredField("adj");
- field.set(callback, VISIBLE_APP_ADJ);
- field = callback.getClass().getDeclaredField("foregroundActivities");
- field.set(callback, true);
- field = callback.getClass().getDeclaredField("procState");
- field.set(callback, PROCESS_STATE_TOP);
- field = callback.getClass().getDeclaredField("schedGroup");
- field.set(callback, SCHED_GROUP_TOP_APP);
- field = callback.getClass().getDeclaredField("mAdjType");
- field.set(callback, "vis-activity");
- return 0;
- })).when(wpc).computeOomAdjFromActivities(
- any(WindowProcessController.ComputeOomAdjCallback.class));
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE)
+ .when(wpc).getActivityStateFlags();
mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
+ assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT);
assertFalse(app.mState.isCached());
assertFalse(app.mState.isEmpty());
assertEquals("vis-activity", app.mState.getAdjType());
+
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+ | WindowProcessController.ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN)
+ .when(wpc).getActivityStateFlags();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
+ assertEquals("resumed-split-screen-activity", app.mState.getAdjType());
}
@SuppressWarnings("GuardedBy")
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
deleted file mode 100644
index c5e6824099ed..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.location.gnss;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.AlarmManager;
-import android.app.AppOpsManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.location.GnssCapabilities;
-import android.location.LocationManager;
-import android.location.LocationManagerInternal;
-import android.location.flags.Flags;
-import android.location.provider.ProviderRequest;
-import android.os.PowerManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.modules.utils.testing.ExtendedMockitoRule;
-import com.android.server.LocalServices;
-import com.android.server.location.gnss.hal.FakeGnssHal;
-import com.android.server.location.gnss.hal.GnssNative;
-import com.android.server.location.injector.Injector;
-import com.android.server.location.injector.TestInjector;
-import com.android.server.timedetector.TimeDetectorInternal;
-
-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.MockitoAnnotations;
-import org.mockito.quality.Strictness;
-
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-@Presubmit
-@androidx.test.filters.SmallTest
-@RunWith(AndroidJUnit4.class)
-public class GnssLocationProviderTest {
-
- @Rule
- public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
- .setStrictness(Strictness.WARN)
- .mockStatic(Settings.Global.class)
- .build();
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
- private @Mock Context mContext;
- private @Mock LocationManagerInternal mLocationManagerInternal;
- private @Mock LocationManager mLocationManager;
- private @Mock TimeDetectorInternal mTimeDetectorInternal;
- private @Mock GnssConfiguration mMockConfiguration;
- private @Mock GnssMetrics mGnssMetrics;
- private @Mock PowerManager mPowerManager;
- private @Mock TelephonyManager mTelephonyManager;
- private @Mock AppOpsManager mAppOpsManager;
- private @Mock AlarmManager mAlarmManager;
- private @Mock PowerManager.WakeLock mWakeLock;
- private @Mock ContentResolver mContentResolver;
- private @Mock UserManager mUserManager;
- private @Mock UserHandle mUserHandle;
- private Set<UserHandle> mUserHandleSet = new HashSet<>();
-
- private GnssNative mGnssNative;
-
- private GnssLocationProvider mTestProvider;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- doReturn("mypackage").when(mContext).getPackageName();
- doReturn("attribution").when(mContext).getAttributionTag();
- doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
- doReturn(mPowerManager).when(mContext).getSystemService("power");
- doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
- doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
- doReturn(mAlarmManager).when(mContext).getSystemService(Context.ALARM_SERVICE);
- doReturn(mLocationManager).when(mContext).getSystemService(LocationManager.class);
- doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
- mUserHandleSet.add(mUserHandle);
- doReturn(true).when(mLocationManager).isLocationEnabledForUser(eq(mUserHandle));
- doReturn(mUserHandleSet).when(mUserManager).getVisibleUsers();
- doReturn(mContentResolver).when(mContext).getContentResolver();
- doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
- LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal);
- LocalServices.addService(TimeDetectorInternal.class, mTimeDetectorInternal);
- FakeGnssHal fakeGnssHal = new FakeGnssHal();
- GnssNative.setGnssHalForTest(fakeGnssHal);
- Injector injector = new TestInjector(mContext);
- mGnssNative = spy(Objects.requireNonNull(
- GnssNative.create(injector, mMockConfiguration)));
- doReturn(true).when(mGnssNative).init();
- GnssCapabilities gnssCapabilities = new GnssCapabilities.Builder().setHasScheduling(
- true).build();
- doReturn(gnssCapabilities).when(mGnssNative).getCapabilities();
-
- mTestProvider = new GnssLocationProvider(mContext, mGnssNative, mGnssMetrics);
- mGnssNative.register();
- }
-
- @After
- public void tearDown() {
- LocalServices.removeServiceForTest(LocationManagerInternal.class);
- LocalServices.removeServiceForTest(TimeDetectorInternal.class);
- }
-
- @Test
- public void testStartNavigating() {
- ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
- 0).build();
-
- mTestProvider.onSetRequest(providerRequest);
- verify(mGnssNative).start();
- }
-
- @Test
- public void testUpdateRequirements_sameRequest() {
- mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
- ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
- 0).build();
-
- mTestProvider.onSetRequest(providerRequest);
- verify(mGnssNative).start();
-
- // set the same request
- mTestProvider.onSetRequest(providerRequest);
- verify(mGnssNative, never()).stop();
- verify(mGnssNative, times(1)).start();
- }
-
- @Test
- public void testUpdateRequirements_differentRequest() {
- mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
- ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
- 0).build();
-
- mTestProvider.onSetRequest(providerRequest);
- verify(mGnssNative).start();
-
- // set a different request
- providerRequest = new ProviderRequest.Builder().setIntervalMillis(2000).build();
- mTestProvider.onSetRequest(providerRequest);
- verify(mGnssNative).stop();
- verify(mGnssNative, times(2)).start();
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
new file mode 100644
index 000000000000..a82658b52a77
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.media.AudioAttributes;
+import android.media.AudioFocusInfo;
+import android.media.AudioManager;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArraySet;
+
+import org.junit.After;
+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;
+
+@RunWith(JUnit4.class)
+
+public class BackgroundUserSoundNotifierTest {
+ private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation()
+ .getTargetContext();
+ private Context mSpiedContext;
+ private BackgroundUserSoundNotifier mBackgroundUserSoundNotifier;
+
+ private UserManager mUserManager;
+ private ArraySet<Integer> mUsersToRemove;
+
+ @Mock
+ private NotificationManager mNotificationManager;
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mSpiedContext = spy(mRealContext);
+ mUsersToRemove = new ArraySet<>();
+ mUserManager = UserManager.get(mRealContext);
+ doReturn(mNotificationManager)
+ .when(mSpiedContext).getSystemService(NotificationManager.class);
+ mBackgroundUserSoundNotifier = new BackgroundUserSoundNotifier(mSpiedContext);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mUsersToRemove.stream().toList().forEach(this::removeUser);
+ }
+ @Test
+ public void testAlarmOnBackgroundUser_ForegroundUserNotified() throws RemoteException {
+ AudioAttributes aa = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ALARM).build();
+ UserInfo user = createUser("User",
+ UserManager.USER_TYPE_FULL_SECONDARY,
+ 0);
+ final int fgUserId = mSpiedContext.getUserId();
+ final int bgUserUid = user.id * 100000;
+ doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser();
+ AudioFocusInfo afi = new AudioFocusInfo(aa, bgUserUid, "",
+ /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
+ AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
+ clearInvocations(mNotificationManager);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ verify(mNotificationManager)
+ .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()),
+ eq(afi.getClientUid()), any(Notification.class),
+ eq(UserHandle.of(fgUserId)));
+ }
+
+ @Test
+ public void testMediaOnBackgroundUser_ForegroundUserNotNotified() throws RemoteException {
+ AudioAttributes aa = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA).build();
+ UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0);
+ final int bgUserUid = mSpiedContext.getUserId() * 100000;
+ AudioFocusInfo afi = new AudioFocusInfo(aa, bgUserUid, "",
+ /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
+ AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
+ clearInvocations(mNotificationManager);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ verifyZeroInteractions(mNotificationManager);
+ }
+
+ @Test
+ public void testAlarmOnForegroundUser_ForegroundUserNotNotified() throws RemoteException {
+ AudioAttributes aa = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ALARM).build();
+ final int fgUserId = mSpiedContext.getUserId();
+ final int fgUserUid = fgUserId * 100000;
+ doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser();
+ AudioFocusInfo afi = new AudioFocusInfo(aa, fgUserUid, "", /* packageName= */ "",
+ AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0,
+ Build.VERSION.SDK_INT);
+ clearInvocations(mNotificationManager);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ verifyZeroInteractions(mNotificationManager);
+ }
+
+
+ private UserInfo createUser(String name, String userType, int flags) {
+ UserInfo user = mUserManager.createUser(name, userType, flags);
+ if (user != null) {
+ mUsersToRemove.add(user.id);
+ }
+ return user;
+ }
+ private void removeUser(int userId) {
+ mUserManager.removeUser(userId);
+ }
+
+}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 4460c6af0691..ce2bb95b790a 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.SensorManager;
@@ -41,7 +42,6 @@ import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.os.test.TestLooper;
@@ -82,8 +82,12 @@ public class NotifierTest {
@Mock private StatusBarManagerInternal mStatusBarManagerInternal;
@Mock private WakeLockLog mWakeLockLog;
+ @Mock private IBatteryStats mBatteryStats;
+
@Mock private PowerManagerFlags mPowerManagerFlags;
+ @Mock private AppOpsManager mAppOpsManager;
+
private PowerManagerService mService;
private Context mContextSpy;
private Resources mResourcesSpy;
@@ -230,7 +234,7 @@ public class NotifierTest {
public void testOnWakeLockListener_RemoteException_NoRethrow() {
when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
createNotifier();
-
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
@Override public void onStateChanged(boolean enabled) throws RemoteException {
throw new RemoteException("Just testing");
@@ -245,6 +249,7 @@ public class NotifierTest {
verifyZeroInteractions(mWakeLockLog);
mTestLooper.dispatchAll();
verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
+
mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
"my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
exceptingCallback);
@@ -277,6 +282,115 @@ public class NotifierTest {
verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1);
}
+
+ @Test
+ public void testOnWakeLockListener_FullWakeLock_ProcessesOnHandler() throws RemoteException {
+ when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
+ createNotifier();
+
+ IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
+ @Override public void onStateChanged(boolean enabled) throws RemoteException {
+ throw new RemoteException("Just testing");
+ }
+ };
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ final int uid = 1234;
+ final int pid = 5678;
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+
+ // No interaction because we expect that to happen in async
+ verifyZeroInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ // Progressing the looper, and validating all the interactions
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
+ verify(mBatteryStats).noteStopWakelock(uid, pid, "wakelockTag", /* historyTag= */ null,
+ BatteryStats.WAKE_TYPE_FULL);
+ verify(mAppOpsManager).finishOp(AppOpsManager.OP_WAKE_LOCK, uid,
+ "my.package.name", null);
+
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ // Acquire the wakelock
+ mNotifier.onWakeLockAcquired(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+
+ // No interaction because we expect that to happen in async
+ verifyNoMoreInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ // Progressing the looper, and validating all the interactions
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, 1);
+ verify(mBatteryStats).noteStartWakelock(uid, pid, "wakelockTag", /* historyTag= */ null,
+ BatteryStats.WAKE_TYPE_FULL, false);
+ verify(mAppOpsManager).startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, uid,
+ "my.package.name", false, null, null);
+
+ // Test with improveWakelockLatency flag false, hence the wakelock log will run on the same
+ // thread
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+ when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(false);
+
+ mNotifier.onWakeLockAcquired(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, -1);
+
+ mNotifier.onWakeLockReleased(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1);
+ }
+
+ @Test
+ public void testOnWakeLockListener_FullWakeLock_ProcessesInSync() throws RemoteException {
+ createNotifier();
+
+ IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
+ @Override public void onStateChanged(boolean enabled) throws RemoteException {
+ throw new RemoteException("Just testing");
+ }
+ };
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ final int uid = 1234;
+ final int pid = 5678;
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1);
+ verify(mBatteryStats).noteStopWakelock(uid, pid, "wakelockTag", /* historyTag= */ null,
+ BatteryStats.WAKE_TYPE_FULL);
+ verify(mAppOpsManager).finishOp(AppOpsManager.OP_WAKE_LOCK, uid,
+ "my.package.name", null);
+
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ // Acquire the wakelock
+ mNotifier.onWakeLockAcquired(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, -1);
+ verify(mBatteryStats).noteStartWakelock(uid, pid, "wakelockTag", /* historyTag= */ null,
+ BatteryStats.WAKE_TYPE_FULL, false);
+ verify(mAppOpsManager).startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, uid,
+ "my.package.name", false, null, null);
+ }
+
private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
@@ -365,13 +479,17 @@ public class NotifierTest {
public WakeLockLog getWakeLockLog(Context context) {
return mWakeLockLog;
}
+
+ @Override
+ public AppOpsManager getAppOpsManager(Context context) {
+ return mAppOpsManager;
+ }
};
mNotifier = new Notifier(
mTestLooper.getLooper(),
mContextSpy,
- IBatteryStats.Stub.asInterface(ServiceManager.getService(
- BatteryStats.SERVICE_NAME)),
+ mBatteryStats,
mInjector.createSuspendBlocker(mService, "testBlocker"),
null,
null,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 4e8c75559f3b..a40bbd27fa8a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -25,6 +25,8 @@ import static android.view.accessibility.Flags.FLAG_SKIP_ACCESSIBILITY_WARNING_D
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG;
import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER;
@@ -801,6 +803,7 @@ public class AccessibilityManagerServiceTest {
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
mA11yms.performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE,
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
mTestableLooper.processAllMessages();
@@ -819,6 +822,7 @@ public class AccessibilityManagerServiceTest {
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
mA11yms.performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE,
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
mTestableLooper.processAllMessages();
@@ -1082,7 +1086,7 @@ public class AccessibilityManagerServiceTest {
mA11yms.enableShortcutsForTargets(
/* enable= */ true,
- UserShortcutType.HARDWARE,
+ HARDWARE,
List.of(target),
mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
@@ -1346,14 +1350,14 @@ public class AccessibilityManagerServiceTest {
mA11yms.enableShortcutsForTargets(
/* enable= */ true,
- UserShortcutType.HARDWARE,
+ HARDWARE,
List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()),
mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
assertThat(
ShortcutUtils.isComponentIdExistingInSettings(
- mTestableContext, ShortcutConstants.UserShortcutType.HARDWARE,
+ mTestableContext, HARDWARE,
TARGET_STANDARD_A11Y_SERVICE.flattenToString())
).isTrue();
}
@@ -1367,7 +1371,7 @@ public class AccessibilityManagerServiceTest {
mA11yms.enableShortcutsForTargets(
/* enable= */ false,
- UserShortcutType.HARDWARE,
+ HARDWARE,
List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()),
mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
@@ -1375,7 +1379,7 @@ public class AccessibilityManagerServiceTest {
assertThat(
ShortcutUtils.isComponentIdExistingInSettings(
mTestableContext,
- ShortcutConstants.UserShortcutType.HARDWARE,
+ HARDWARE,
TARGET_STANDARD_A11Y_SERVICE.flattenToString()))
.isFalse();
}
@@ -1390,14 +1394,14 @@ public class AccessibilityManagerServiceTest {
mA11yms.enableShortcutsForTargets(
/* enable= */ true,
- UserShortcutType.QUICK_SETTINGS,
+ QUICK_SETTINGS,
List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()),
mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
assertThat(
ShortcutUtils.isComponentIdExistingInSettings(
- mTestableContext, UserShortcutType.QUICK_SETTINGS,
+ mTestableContext, QUICK_SETTINGS,
TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString())
).isTrue();
verify(mStatusBarManagerInternal)
@@ -1417,14 +1421,14 @@ public class AccessibilityManagerServiceTest {
mA11yms.enableShortcutsForTargets(
/* enable= */ false,
- UserShortcutType.QUICK_SETTINGS,
+ QUICK_SETTINGS,
List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()),
mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
assertThat(
ShortcutUtils.isComponentIdExistingInSettings(
- mTestableContext, UserShortcutType.QUICK_SETTINGS,
+ mTestableContext, QUICK_SETTINGS,
TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString())
).isFalse();
verify(mStatusBarManagerInternal)
@@ -1614,44 +1618,49 @@ public class AccessibilityManagerServiceTest {
@Test
@EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
- public void restoreAccessibilityQsTargets_a11yQsTargetsRestored() {
+ public void restoreShortcutTargets_qs_a11yQsTargetsRestored() {
String daltonizerTile =
AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
String colorInversionTile =
AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString();
final AccessibilityUserState userState = new AccessibilityUserState(
UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
- userState.updateA11yQsTargetLocked(Set.of(daltonizerTile));
+ userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS);
mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
broadcastSettingRestored(
- Settings.Secure.ACCESSIBILITY_QS_TARGETS,
- /*previousValue=*/null,
+ ShortcutUtils.convertToKey(QUICK_SETTINGS),
/*newValue=*/colorInversionTile);
- assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM).getA11yQsTargets())
- .containsExactlyElementsIn(Set.of(daltonizerTile, colorInversionTile));
+ Set<String> expected = Set.of(daltonizerTile, colorInversionTile);
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS)))
+ .containsExactlyElementsIn(expected);
+ assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS))
+ .containsExactlyElementsIn(expected);
}
@Test
@DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
- public void restoreAccessibilityQsTargets_a11yQsTargetsNotRestored() {
+ public void restoreShortcutTargets_qs_a11yQsTargetsNotRestored() {
String daltonizerTile =
AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
String colorInversionTile =
AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString();
final AccessibilityUserState userState = new AccessibilityUserState(
UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
- userState.updateA11yQsTargetLocked(Set.of(daltonizerTile));
+ userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS);
+ putShortcutSettingForUser(QUICK_SETTINGS, daltonizerTile, userState.mUserId);
mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
broadcastSettingRestored(
- Settings.Secure.ACCESSIBILITY_QS_TARGETS,
- /*previousValue=*/null,
+ ShortcutUtils.convertToKey(QUICK_SETTINGS),
/*newValue=*/colorInversionTile);
- assertThat(userState.getA11yQsTargets())
- .containsExactlyElementsIn(Set.of(daltonizerTile));
+ Set<String> expected = Set.of(daltonizerTile);
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS)))
+ .containsExactlyElementsIn(expected);
+ assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS))
+ .containsExactlyElementsIn(expected);
}
@Test
@@ -1717,27 +1726,26 @@ public class AccessibilityManagerServiceTest {
@Test
@EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE)
- public void restoreA11yShortcutTargetService_targetsMerged() {
+ public void restoreShortcutTargets_hardware_targetsMerged() {
+ mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
final String servicePrevious = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
final String otherPrevious = TARGET_MAGNIFICATION;
- final String combinedPrevious = String.join(":", servicePrevious, otherPrevious);
final String serviceRestored = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
final AccessibilityUserState userState = new AccessibilityUserState(
UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
setupShortcutTargetServices(userState);
+ mA11yms.enableShortcutsForTargets(
+ true, HARDWARE, List.of(servicePrevious, otherPrevious), userState.mUserId);
broadcastSettingRestored(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- /*previousValue=*/combinedPrevious,
+ ShortcutUtils.convertToKey(HARDWARE),
/*newValue=*/serviceRestored);
final Set<String> expected = Set.of(servicePrevious, otherPrevious, serviceRestored);
- assertThat(readStringsFromSetting(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
.containsExactlyElementsIn(expected);
- assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
- .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+ assertThat(userState.getShortcutTargetsLocked(HARDWARE))
.containsExactlyElementsIn(expected);
}
@@ -1745,7 +1753,7 @@ public class AccessibilityManagerServiceTest {
@EnableFlags({
android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
- public void restoreA11yShortcutTargetService_alreadyHadDefaultService_doesNotClear() {
+ public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() {
final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
mTestableContext.getOrCreateTestableResources().addOverride(
R.string.config_defaultAccessibilityService, serviceDefault);
@@ -1754,17 +1762,18 @@ public class AccessibilityManagerServiceTest {
mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
setupShortcutTargetServices(userState);
+ // default is present in userState & setting, so it's not cleared
+ putShortcutSettingForUser(HARDWARE, serviceDefault, UserHandle.USER_SYSTEM);
+ userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE);
+
broadcastSettingRestored(
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- /*previousValue=*/serviceDefault,
/*newValue=*/serviceDefault);
final Set<String> expected = Set.of(serviceDefault);
- assertThat(readStringsFromSetting(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
.containsExactlyElementsIn(expected);
- assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
- .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+ assertThat(userState.getShortcutTargetsLocked(HARDWARE))
.containsExactlyElementsIn(expected);
}
@@ -1772,7 +1781,7 @@ public class AccessibilityManagerServiceTest {
@EnableFlags({
android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
- public void restoreA11yShortcutTargetService_didNotHaveDefaultService_clearsDefaultService() {
+ public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() {
final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
// Restored value from the broadcast contains both default and non-default service.
@@ -1784,18 +1793,45 @@ public class AccessibilityManagerServiceTest {
mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
setupShortcutTargetServices(userState);
- broadcastSettingRestored(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- /*previousValue=*/null,
+ broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE),
/*newValue=*/combinedRestored);
// The default service is cleared from the final restored value.
final Set<String> expected = Set.of(serviceRestored);
- assertThat(readStringsFromSetting(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
.containsExactlyElementsIn(expected);
- assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
- .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+ assertThat(userState.getShortcutTargetsLocked(HARDWARE))
+ .containsExactlyElementsIn(expected);
+ }
+
+ @Test
+ @EnableFlags({
+ android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
+ Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
+ public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() {
+ final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
+ final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
+ // Restored value from the broadcast contains both default and non-default service.
+ final String combinedRestored = String.join(":", serviceDefault, serviceRestored);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultAccessibilityService, serviceDefault);
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+ setupShortcutTargetServices(userState);
+
+ // UserState has default, but setting is null (this emulates a typical scenario in SUW).
+ userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE);
+ putShortcutSettingForUser(HARDWARE, null, UserHandle.USER_SYSTEM);
+
+ broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE),
+ /*newValue=*/combinedRestored);
+
+ // The default service is cleared from the final restored value.
+ final Set<String> expected = Set.of(serviceRestored);
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
+ .containsExactlyElementsIn(expected);
+ assertThat(userState.getShortcutTargetsLocked(HARDWARE))
.containsExactlyElementsIn(expected);
}
@@ -1806,11 +1842,10 @@ public class AccessibilityManagerServiceTest {
return result;
}
- private void broadcastSettingRestored(String setting, String previousValue, String newValue) {
+ private void broadcastSettingRestored(String setting, String newValue) {
Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.putExtra(Intent.EXTRA_SETTING_NAME, setting)
- .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue)
.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, newValue);
sendBroadcastToAccessibilityManagerService(intent);
mTestableLooper.processAllMessages();
@@ -1952,4 +1987,13 @@ public class AccessibilityManagerServiceTest {
private static boolean isSameCurrentUser(AccessibilityManagerService service, Context context) {
return service.getCurrentUserIdLocked() == context.getUserId();
}
+
+ private void putShortcutSettingForUser(@UserShortcutType int shortcutType,
+ String shortcutValue, int userId) {
+ Settings.Secure.putStringForUser(
+ mTestableContext.getContentResolver(),
+ ShortcutUtils.convertToKey(shortcutType),
+ shortcutValue,
+ userId);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b269beb90c75..9fad14d889fd 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -28,6 +28,7 @@ import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSI
import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
import static com.google.common.truth.Truth.assertThat;
@@ -429,20 +430,20 @@ public class AccessibilityUserStateTest {
}
@Test
- public void updateA11yQsTargetLocked_valueUpdated() {
+ public void updateShortcutTargetsLocked_quickSettings_valueUpdated() {
Set<String> newTargets = Set.of(
AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(),
AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString()
);
- mUserState.updateA11yQsTargetLocked(newTargets);
+ mUserState.updateShortcutTargetsLocked(newTargets, QUICK_SETTINGS);
assertThat(mUserState.getA11yQsTargets()).isEqualTo(newTargets);
}
@Test
public void getA11yQsTargets_returnsCopiedData() {
- updateA11yQsTargetLocked_valueUpdated();
+ updateShortcutTargetsLocked_quickSettings_valueUpdated();
Set<String> targets = mUserState.getA11yQsTargets();
targets.clear();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index c1d7afb304ed..c48d745b1dc0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -14335,6 +14335,29 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
+ public void enqueueNotification_directlyThroughRunnable_populatesAllowlistToken() {
+ Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentIntent(createPendingIntent("content"))
+ .build();
+ NotificationRecord record = new NotificationRecord(
+ mContext,
+ new StatusBarNotification(mPkg, mPkg, 1, "tag", mUid, 44, receivedWithoutParceling,
+ mUser, "groupKey", 0),
+ mTestNotificationChannel);
+ assertThat(record.getNotification().getAllowlistToken()).isNull();
+
+ mWorkerHandler.post(
+ mService.new EnqueueNotificationRunnable(mUserId, record, false, false,
+ mPostNotificationTrackerFactory.newTracker(null)));
+ waitForIdle();
+
+ assertThat(mService.mNotificationList).hasSize(1);
+ assertThat(mService.mNotificationList.get(0).getNotification().getAllowlistToken())
+ .isEqualTo(NotificationManagerService.ALLOWLIST_TOKEN);
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_rejectsOtherToken() throws RemoteException {
Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 2e571bb9eceb..aa28147a3973 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -132,8 +132,9 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase {
KeyboardLogEvent.VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN, 0},
{"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE},
KeyboardLogEvent.VOLUME_MUTE, KeyEvent.KEYCODE_VOLUME_MUTE, 0},
- {"ALL_APPS key -> Open App Drawer", new int[]{KeyEvent.KEYCODE_ALL_APPS},
- KeyboardLogEvent.ALL_APPS, KeyEvent.KEYCODE_ALL_APPS, 0},
+ {"ALL_APPS key -> Open App Drawer in Accessibility mode",
+ new int[]{KeyEvent.KEYCODE_ALL_APPS},
+ KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, KeyEvent.KEYCODE_ALL_APPS, 0},
{"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
KeyboardLogEvent.LAUNCH_SEARCH, KeyEvent.KEYCODE_SEARCH, 0},
{"LANGUAGE_SWITCH key -> Switch Keyboard Language",
@@ -259,14 +260,15 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase {
{"Long press META + H -> Launch assistant",
new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_ASSIST,
KeyboardLogEvent.LAUNCH_ASSISTANT, KeyEvent.KEYCODE_H, META_ON},
- {"Long press HOME key -> Open App Drawer",
+ {"Long press HOME key -> Open App Drawer in Accessibility mode",
new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ALL_APPS,
- KeyboardLogEvent.ALL_APPS, KeyEvent.KEYCODE_HOME, 0},
- {"Long press META + ENTER -> Open App Drawer",
+ KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, KeyEvent.KEYCODE_HOME, 0},
+ {"Long press META + ENTER -> Open App Drawer in Accessibility mode",
new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ALL_APPS,
- KeyboardLogEvent.ALL_APPS, KeyEvent.KEYCODE_ENTER, META_ON},
- {"Long press META + H -> Open App Drawer", new int[]{META_KEY, KeyEvent.KEYCODE_H},
- LONG_PRESS_HOME_ALL_APPS, KeyboardLogEvent.ALL_APPS,
+ KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, KeyEvent.KEYCODE_ENTER, META_ON},
+ {"Long press META + H -> Open App Drawer in Accessibility mode",
+ new int[]{META_KEY, KeyEvent.KEYCODE_H},
+ LONG_PRESS_HOME_ALL_APPS, KeyboardLogEvent.ACCESSIBILITY_ALL_APPS,
KeyEvent.KEYCODE_H, META_ON}};
}
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 44cabac586f2..ef3df6cdc523 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -263,16 +263,10 @@ public class ActivityRecordTests extends WindowTestsBase {
doAnswer((InvocationOnMock invocationOnMock) -> {
final ClientTransaction transaction = invocationOnMock.getArgument(0);
final List<ClientTransactionItem> items = transaction.getTransactionItems();
- if (items != null) {
- for (ClientTransactionItem item : items) {
- if (item instanceof PauseActivityItem) {
- pauseFound.value = true;
- break;
- }
- }
- } else {
- if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) {
+ for (ClientTransactionItem item : items) {
+ if (item instanceof PauseActivityItem) {
pauseFound.value = true;
+ break;
}
}
return null;
@@ -3357,14 +3351,8 @@ public class ActivityRecordTests extends WindowTestsBase {
// to client if the app didn't request IME visible.
assertFalse(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput);
- if (Flags.bundleClientTransactionFlag()) {
- verify(app2.getProcess(), atLeastOnce()).scheduleClientTransactionItem(
- isA(WindowStateResizeItem.class));
- } else {
- verify(app2.mClient, atLeastOnce()).resized(any(), anyBoolean(), any(),
- insetsStateCaptor.capture(), anyBoolean(), anyBoolean(), anyInt(), anyInt(),
- anyBoolean(), any());
- }
+ verify(app2.getProcess(), atLeastOnce()).scheduleClientTransactionItem(
+ isA(WindowStateResizeItem.class));
assertFalse(app2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime()));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
new file mode 100644
index 000000000000..5f2853ab15b0
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.wm.utils.TestComponentStack;
+
+import org.junit.Assert;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Robot implementation for {@link ActivityRecord}.
+ */
+class AppCompatActivityRobot {
+
+ private static final int DEFAULT_DISPLAY_WIDTH = 1000;
+ private static final int DEFAULT_DISPLAY_HEIGHT = 2000;
+
+ private static final float DELTA_ASPECT_RATIO_TOLERANCE = 0.0001f;
+ private static final float COMPAT_SCALE_TOLERANCE = 0.0001f;
+
+ private static final String TEST_COMPONENT_NAME = AppCompatActivityRobot.class.getName();
+
+ @NonNull
+ private final ActivityTaskManagerService mAtm;
+ @NonNull
+ private final ActivityTaskSupervisor mSupervisor;
+ @NonNull
+ private final TestComponentStack<ActivityRecord> mActivityStack;
+ @NonNull
+ private final TestComponentStack<Task> mTaskStack;
+
+ private final int mDisplayWidth;
+ private final int mDisplayHeight;
+
+ AppCompatActivityRobot(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor,
+ int displayWidth, int displayHeight) {
+ mAtm = atm;
+ mSupervisor = supervisor;
+ mDisplayWidth = displayWidth;
+ mDisplayHeight = displayHeight;
+ mActivityStack = new TestComponentStack<>();
+ mTaskStack = new TestComponentStack<>();
+ }
+
+ AppCompatActivityRobot(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor) {
+ this(wm, atm, supervisor, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
+ }
+
+ void createActivityWithComponent() {
+ createActivityWithComponentInNewTask(/* inNewTask */ mTaskStack.isEmpty());
+ }
+
+ void createActivityWithComponentInNewTask() {
+ createActivityWithComponentInNewTask(/* inNewTask */ true);
+ }
+
+ void configureTopActivity(float minAspect, float maxAspect, int screenOrientation,
+ boolean isUnresizable) {
+ prepareLimitedBounds(mActivityStack.top(), minAspect, maxAspect, screenOrientation,
+ isUnresizable);
+ }
+
+ void configureTopActivityIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
+ mActivityStack.top().mDisplayContent
+ .setIgnoreOrientationRequest(ignoreOrientationRequest);
+ }
+
+ void configureUnresizableTopActivity(@ActivityInfo.ScreenOrientation int screenOrientation) {
+ configureTopActivity(/* minAspect */ -1, /* maxAspect */ -1, screenOrientation,
+ /* isUnresizable */ true);
+ }
+
+ @NonNull
+ ActivityRecord top() {
+ return mActivityStack.top();
+ }
+
+ @NonNull
+ ActivityRecord getFromTop(int fromTop) {
+ return mActivityStack.getFromTop(fromTop);
+ }
+
+ void setTaskWindowingMode(@WindowConfiguration.WindowingMode int windowingMode) {
+ mTaskStack.top().setWindowingMode(windowingMode);
+ }
+
+ void setLetterboxedForFixedOrientationAndAspectRatio(boolean enabled) {
+ doReturn(enabled).when(mActivityStack.top())
+ .isLetterboxedForFixedOrientationAndAspectRatio();
+ }
+
+ void enableTreatmentForTopActivity(boolean enabled) {
+ doReturn(enabled).when(getTopDisplayRotationCompatPolicy())
+ .isTreatmentEnabledForActivity(eq(mActivityStack.top()));
+ }
+
+ void setTopActivityCameraActive(boolean enabled) {
+ doReturn(enabled).when(getTopDisplayRotationCompatPolicy())
+ .isCameraActive(eq(mActivityStack.top()), /* mustBeFullscreen= */ eq(true));
+ }
+
+ void setTopActivityEligibleForOrientationOverride(boolean enabled) {
+ doReturn(enabled).when(getTopDisplayRotationCompatPolicy())
+ .isActivityEligibleForOrientationOverride(eq(mActivityStack.top()));
+ }
+
+ void setShouldApplyUserMinAspectRatioOverride(boolean enabled) {
+ doReturn(enabled).when(mActivityStack.top()
+ .mLetterboxUiController).shouldApplyUserMinAspectRatioOverride();
+ }
+
+ void setShouldApplyUserFullscreenOverride(boolean enabled) {
+ doReturn(enabled).when(mActivityStack.top()
+ .mLetterboxUiController).shouldApplyUserFullscreenOverride();
+ }
+
+ void setGetUserMinAspectRatioOverrideCode(@PackageManager.UserMinAspectRatio int orientation) {
+ doReturn(orientation).when(mActivityStack.top()
+ .mLetterboxUiController).getUserMinAspectRatioOverrideCode();
+ }
+
+ void setIgnoreOrientationRequest(boolean enabled) {
+ mActivityStack.top().mDisplayContent.setIgnoreOrientationRequest(enabled);
+ }
+
+ void setTopActivityAsEmbedded(boolean embedded) {
+ doReturn(embedded).when(mActivityStack.top()).isEmbedded();
+ }
+
+ void destroyTopActivity() {
+ mActivityStack.top().removeImmediately();
+ }
+
+ void destroyActivity(int fromTop) {
+ mActivityStack.applyTo(/* fromTop */ fromTop, ActivityRecord::removeImmediately);
+ }
+
+ void createNewTask() {
+ final DisplayContent displayContent = new TestDisplayContent
+ .Builder(mAtm, mDisplayWidth, mDisplayHeight).build();
+ final Task newTask = new WindowTestsBase.TaskBuilder(mSupervisor)
+ .setDisplay(displayContent).build();
+ mTaskStack.push(newTask);
+ }
+
+ void createNewTaskWithBaseActivity() {
+ final DisplayContent displayContent = new TestDisplayContent
+ .Builder(mAtm, mDisplayWidth, mDisplayHeight).build();
+ final Task newTask = new WindowTestsBase.TaskBuilder(mSupervisor)
+ .setCreateActivity(true)
+ .setDisplay(displayContent).build();
+ mTaskStack.push(newTask);
+ pushActivity(newTask.getTopNonFinishingActivity());
+ }
+
+ void attachTopActivityToTask() {
+ mTaskStack.top().addChild(mActivityStack.top());
+ }
+
+ void applyToTopActivity(Consumer<ActivityRecord> consumer) {
+ consumer.accept(mActivityStack.top());
+ }
+
+ void applyToActivity(int fromTop, @NonNull Consumer<ActivityRecord> consumer) {
+ mActivityStack.applyTo(fromTop, consumer);
+ }
+
+ void applyToAllActivities(@NonNull Consumer<ActivityRecord> consumer) {
+ mActivityStack.applyToAll(consumer);
+ }
+
+ void rotateDisplayForTopActivity(@Surface.Rotation int rotation) {
+ rotateDisplay(mActivityStack.top().mDisplayContent, rotation);
+ }
+
+ void configureTopActivityFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
+ mActivityStack.applyToTop((topActivity) -> {
+ final DisplayRotation r = topActivity.mDisplayContent.getDisplayRotation();
+ doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
+ doReturn(false).when(r)
+ .isDeviceInPosture(any(DeviceStateController.DeviceState.class),
+ anyBoolean());
+ if (isHalfFolded) {
+ doReturn(true).when(r)
+ .isDeviceInPosture(DeviceStateController.DeviceState.HALF_FOLDED,
+ isTabletop);
+ }
+ topActivity.recomputeConfiguration();
+ });
+ }
+
+ private static void rotateDisplay(@Surface.Rotation DisplayContent display, int rotation) {
+ final Configuration c = new Configuration();
+ display.getDisplayRotation().setRotation(rotation);
+ display.computeScreenConfiguration(c);
+ display.onRequestedOverrideConfigurationChanged(c);
+ }
+
+ void assertTrueOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
+ mActivityStack.applyTo(fromTop, (activity) -> {
+ Assert.assertTrue(predicate.test(activity));
+ });
+ }
+
+ void assertFalseOnTopActivity(Predicate<ActivityRecord> predicate) {
+ Assert.assertFalse(predicate.test(mActivityStack.top()));
+ }
+
+ void assertFalseOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
+ mActivityStack.applyTo(fromTop, (activity) -> {
+ Assert.assertFalse(predicate.test(activity));
+ });
+ }
+
+ void assertNotNullOnTopActivity(Function<ActivityRecord, Object> getter) {
+ Assert.assertNotNull(getter.apply(mActivityStack.top()));
+ }
+
+ void assertNullOnTopActivity(Function<ActivityRecord, Object> getter) {
+ Assert.assertNull(getter.apply(mActivityStack.top()));
+ }
+
+ void checkTopActivityRecomputedConfiguration() {
+ verify(mActivityStack.top()).recomputeConfiguration();
+ }
+
+ void checkTopActivityConfigOrientation(@Configuration.Orientation int orientation) {
+ Assert.assertEquals(orientation, mActivityStack.top()
+ .getRequestedConfigurationOrientation());
+ }
+
+ void checkTopActivityAspectRatios(float minAspectRatio, float maxAspectRatio) {
+ final ActivityRecord topActivity = mActivityStack.top();
+ Assert.assertEquals(minAspectRatio, topActivity.getMinAspectRatio(),
+ DELTA_ASPECT_RATIO_TOLERANCE);
+ Assert.assertEquals(maxAspectRatio, topActivity.getMaxAspectRatio(),
+ DELTA_ASPECT_RATIO_TOLERANCE);
+ }
+
+ void checkTopActivityInSizeCompatMode(boolean inScm) {
+ final ActivityRecord topActivity = mActivityStack.top();
+ Assert.assertEquals(inScm, topActivity.inSizeCompatMode());
+ Assert.assertNotEquals(1f, topActivity.getCompatScale(), COMPAT_SCALE_TOLERANCE);
+ }
+
+ void launchActivity(float minAspectRatio, float maxAspectRatio,
+ @ActivityInfo.ScreenOrientation int orientation, boolean transparent,
+ boolean withComponent, boolean addToTask) {
+ final WindowTestsBase.ActivityBuilder
+ activityBuilder = new WindowTestsBase.ActivityBuilder(mAtm)
+ .setScreenOrientation(orientation)
+ .setLaunchedFromUid(0);
+ if (transparent) {
+ activityBuilder.setActivityTheme(android.R.style.Theme_Translucent);
+ }
+ if (withComponent) {
+ // Set the component to be that of the test class in order
+ // to enable compat changes
+ activityBuilder.setComponent(ComponentName.createRelative(mAtm.mContext,
+ TEST_COMPONENT_NAME));
+ }
+ if (minAspectRatio >= 0) {
+ activityBuilder.setMinAspectRatio(minAspectRatio);
+ }
+ if (maxAspectRatio >= 0) {
+ activityBuilder.setMaxAspectRatio(maxAspectRatio);
+ }
+ final ActivityRecord newActivity = activityBuilder.build();
+ if (addToTask) {
+ if (mTaskStack.isEmpty()) {
+ createNewTask();
+ }
+ mTaskStack.top().addChild(newActivity);
+ }
+ pushActivity(newActivity);
+ }
+
+ private void createActivityWithComponentInNewTask(boolean inNewTask) {
+ if (inNewTask) {
+ createNewTask();
+ }
+ final ActivityRecord activity = new WindowTestsBase.ActivityBuilder(mAtm)
+ .setOnTop(true)
+ .setTask(mTaskStack.top())
+ // Set the component to be that of the test class in order
+ // to enable compat changes
+ .setComponent(ComponentName.createRelative(mAtm.mContext, TEST_COMPONENT_NAME))
+ .build();
+ pushActivity(activity);
+ }
+
+ /**
+ * Setups activity with restriction on its bounds, such as maxAspect, minAspect,
+ * fixed orientation, and/or whether it is resizable.
+ */
+ private void prepareLimitedBounds(ActivityRecord activity, float minAspect, float maxAspect,
+ @ActivityInfo.ScreenOrientation int screenOrientation, boolean isUnresizable) {
+ activity.info.resizeMode = isUnresizable
+ ? RESIZE_MODE_UNRESIZEABLE
+ : RESIZE_MODE_RESIZEABLE;
+ final Task task = activity.getTask();
+ if (task != null) {
+ // Update the Task resize value as activity will follow the task.
+ task.mResizeMode = activity.info.resizeMode;
+ task.getRootActivity().info.resizeMode = activity.info.resizeMode;
+ }
+ activity.setVisibleRequested(true);
+ if (maxAspect >= 0) {
+ activity.info.setMaxAspectRatio(maxAspect);
+ }
+ if (minAspect >= 0) {
+ activity.info.setMinAspectRatio(minAspect);
+ }
+ if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+ activity.info.screenOrientation = screenOrientation;
+ activity.setRequestedOrientation(screenOrientation);
+ }
+ // Make sure to use the provided configuration to construct the size compat fields.
+ activity.clearSizeCompatMode();
+ activity.ensureActivityConfiguration();
+ // Make sure the display configuration reflects the change of activity.
+ if (activity.mDisplayContent.updateOrientation()) {
+ activity.mDisplayContent.sendNewConfiguration();
+ }
+ }
+
+ private DisplayRotationCompatPolicy getTopDisplayRotationCompatPolicy() {
+ return mActivityStack.top().mDisplayContent.mDisplayRotationCompatPolicy;
+ }
+
+ // We add the activity to the stack and spyOn() on its properties.
+ private void pushActivity(@NonNull ActivityRecord activity) {
+ mActivityStack.push(activity);
+ spyOn(activity);
+ spyOn(activity.mAppCompatController.getTransparentPolicy());
+ if (activity.mDisplayContent != null
+ && activity.mDisplayContent.mDisplayRotationCompatPolicy != null) {
+ spyOn(activity.mDisplayContent.mDisplayRotationCompatPolicy);
+ }
+ spyOn(activity.mLetterboxUiController);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
new file mode 100644
index 000000000000..d568eecfd1c5
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.content.pm.PackageManager;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Robot class for optIn/optOut properties.
+ */
+class AppCompatComponentPropRobot {
+ @NonNull
+ private final WindowManagerService mWm;
+
+ AppCompatComponentPropRobot(@NonNull WindowManagerService wm) {
+ mWm = wm;
+ }
+
+ void enable(@NonNull String propertyName) {
+ setPropertyValue(propertyName, /* enabled */ true);
+ }
+
+ void disable(@NonNull String propertyName) {
+ setPropertyValue(propertyName, /* enabled */ false);
+ }
+
+ private void setPropertyValue(@NonNull String propertyName, boolean enabled) {
+ final PackageManager.Property property = new PackageManager.Property(propertyName,
+ /* value */ enabled, /* packageName */ "", /* className */ "");
+ final PackageManager pm = mWm.mContext.getPackageManager();
+ spyOn(pm);
+ try {
+ doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
+ } catch (PackageManager.NameNotFoundException e) {
+ fail(e.getLocalizedMessage());
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java
new file mode 100644
index 000000000000..2ef77f6de74c
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.when;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Robot implementation for {@link LetterboxConfiguration}.
+ */
+class AppCompatLetterboxConfigurationRobot {
+
+ @NonNull
+ private final LetterboxConfiguration mLetterboxConfiguration;
+
+ AppCompatLetterboxConfigurationRobot(@NonNull LetterboxConfiguration letterboxConfiguration) {
+ mLetterboxConfiguration = letterboxConfiguration;
+ spyOn(mLetterboxConfiguration);
+ }
+
+ void enableTranslucentPolicy(boolean enabled) {
+ when(mLetterboxConfiguration.isTranslucentLetterboxingEnabled()).thenReturn(enabled);
+ }
+
+ void enablePolicyForIgnoringRequestedOrientation(boolean enabled) {
+ doReturn(enabled).when(mLetterboxConfiguration)
+ .isPolicyForIgnoringRequestedOrientationEnabled();
+ }
+
+ void enableCameraCompatTreatment(boolean enabled) {
+ doReturn(enabled).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
+ }
+
+ void enableCameraCompatTreatmentAtBuildTime(boolean enabled) {
+ doReturn(enabled).when(mLetterboxConfiguration)
+ .isCameraCompatTreatmentEnabledAtBuildTime();
+ }
+
+ void enableUserAppAspectRatioFullscreen(boolean enabled) {
+ doReturn(enabled).when(mLetterboxConfiguration).isUserAppAspectRatioFullscreenEnabled();
+ }
+
+ void enableUserAppAspectRatioSettings(boolean enabled) {
+ doReturn(enabled).when(mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationCapabilityTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationCapabilityTest.java
deleted file mode 100644
index f1cf866df4a5..000000000000
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationCapabilityTest.java
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.wm;
-
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
-import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.AppCompatOrientationCapability.OrientationCapabilityState.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP;
-import static com.android.server.wm.AppCompatOrientationCapability.OrientationCapabilityState.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-
-import android.compat.testing.PlatformCompatChangeRule;
-import android.content.ComponentName;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.annotation.NonNull;
-
-import com.android.server.wm.utils.TestComponentStack;
-
-import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-
-import java.util.function.Consumer;
-import java.util.function.IntConsumer;
-import java.util.function.LongSupplier;
-
-/**
- * Test class for {@link AppCompatOrientationCapability}.
- * <p>
- * Build/Install/Run:
- * atest WmTests:AppCompatOrientationCapabilityTest
- */
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class AppCompatOrientationCapabilityTest extends WindowTestsBase {
-
- @Rule
- public TestRule compatChangeRule = new PlatformCompatChangeRule();
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
- public void testShouldIgnoreRequestedOrientation_activityRelaunching_returnsTrue() {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
- robot.createActivityWithComponent();
- robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
-
- robot.checkShouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
- });
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
- public void testShouldIgnoreRequestedOrientation_cameraCompatTreatment_returnsTrue() {
- runTestScenario((robot) -> {
- robot.prepareIsCameraCompatTreatmentEnabled(true);
- robot.prepareIsCameraCompatTreatmentEnabledAtBuildTime(true);
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
-
- robot.createActivityWithComponentInNewTask();
- robot.prepareRelaunchingAfterRequestedOrientationChanged(false);
- robot.prepareIsTreatmentEnabledForTopActivity(true);
-
- robot.checkShouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
- });
- }
-
- @Test
- public void testShouldIgnoreRequestedOrientation_overrideDisabled_returnsFalse() {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
-
- robot.createActivityWithComponent();
- robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
-
- robot.checkShouldNotIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
- });
- }
-
- @Test
- public void testShouldIgnoreRequestedOrientation_propertyIsTrue_returnsTrue() {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
- robot.enableProperty(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION);
-
- robot.createActivityWithComponent();
- robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
-
- robot.checkShouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
- });
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
- public void testShouldIgnoreRequestedOrientation_propertyIsFalseAndOverride_returnsFalse()
- throws Exception {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
- robot.disableProperty(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION);
-
- robot.createActivityWithComponent();
- robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
-
- robot.checkShouldNotIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
- });
- }
-
- @Test
- public void testShouldIgnoreOrientationRequestLoop_overrideDisabled_returnsFalse() {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
- robot.createActivityWithComponent();
- robot.prepareIsLetterboxedForFixedOrientationAndAspectRatio(false);
-
- robot.checkRequestLoopExtended((i) -> {
- robot.checkShouldNotIgnoreOrientationLoop();
- robot.checkExpectedLoopCount(/* expectedCount */ 0);
- });
- });
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
- public void testShouldIgnoreOrientationRequestLoop_propertyIsFalseAndOverride_returnsFalse() {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
- robot.disableProperty(
- PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED);
- robot.createActivityWithComponent();
- robot.prepareIsLetterboxedForFixedOrientationAndAspectRatio(false);
-
- robot.checkRequestLoopExtended((i) -> {
- robot.checkShouldNotIgnoreOrientationLoop();
- robot.checkExpectedLoopCount(/* expectedCount */ 0);
- });
- });
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
- public void testShouldIgnoreOrientationRequestLoop_isLetterboxed_returnsFalse() {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
- robot.createActivityWithComponent();
- robot.prepareIsLetterboxedForFixedOrientationAndAspectRatio(true);
-
- robot.checkRequestLoopExtended((i) -> {
- robot.checkShouldNotIgnoreOrientationLoop();
- robot.checkExpectedLoopCount(/* expectedCount */ i);
- });
- });
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
- public void testShouldIgnoreOrientationRequestLoop_noLoop_returnsFalse() {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
- robot.createActivityWithComponent();
- robot.prepareIsLetterboxedForFixedOrientationAndAspectRatio(false);
-
- robot.checkShouldNotIgnoreOrientationLoop();
- robot.checkExpectedLoopCount(/* expectedCount */ 0);
- });
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
- public void testShouldIgnoreOrientationRequestLoop_timeout_returnsFalse() {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
- robot.createActivityWithComponent();
- robot.prepareIsLetterboxedForFixedOrientationAndAspectRatio(false);
-
- robot.prepareMockedTime();
- robot.checkRequestLoopExtended((i) -> {
- robot.checkShouldNotIgnoreOrientationLoop();
- robot.checkExpectedLoopCount(/* expectedCount */ 0);
- robot.delay();
- });
- });
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
- public void testShouldIgnoreOrientationRequestLoop_returnsTrue() {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
- robot.createActivityWithComponent();
- robot.prepareIsLetterboxedForFixedOrientationAndAspectRatio(false);
-
- robot.checkRequestLoop((i) -> {
- robot.checkShouldNotIgnoreOrientationLoop();
- robot.checkExpectedLoopCount(/* expectedCount */ i);
- });
- robot.checkShouldIgnoreOrientationLoop();
- robot.checkExpectedLoopCount(/* expectedCount */ MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP);
- });
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
- public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() {
- runTestScenario((robot) -> {
- robot.prepareIsPolicyForIgnoringRequestedOrientationEnabled(true);
- robot.createActivityWithComponent();
- robot.prepareIsLetterboxedForFixedOrientationAndAspectRatio(false);
-
- robot.checkShouldNotIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
- });
- }
-
- /**
- * Runs a test scenario providing a Robot.
- */
- void runTestScenario(@NonNull Consumer<OrientationCapabilityRobotTest> consumer) {
- spyOn(mWm.mLetterboxConfiguration);
- final OrientationCapabilityRobotTest robot =
- new OrientationCapabilityRobotTest(mWm, mAtm, mSupervisor);
- consumer.accept(robot);
- }
-
- private static class OrientationCapabilityRobotTest {
-
- @NonNull
- private final ActivityTaskManagerService mAtm;
- @NonNull
- private final WindowManagerService mWm;
- @NonNull
- private final ActivityTaskSupervisor mSupervisor;
- @NonNull
- private final LetterboxConfiguration mLetterboxConfiguration;
- @NonNull
- private final TestComponentStack<ActivityRecord> mActivityStack;
- @NonNull
- private final TestComponentStack<Task> mTaskStack;
- @NonNull
- private final CurrentTimeMillisSupplierTest mTestCurrentTimeMillisSupplier;
-
-
- OrientationCapabilityRobotTest(@NonNull WindowManagerService wm,
- @NonNull ActivityTaskManagerService atm,
- @NonNull ActivityTaskSupervisor supervisor) {
- mAtm = atm;
- mWm = wm;
- mSupervisor = supervisor;
- mActivityStack = new TestComponentStack<>();
- mTaskStack = new TestComponentStack<>();
- mLetterboxConfiguration = mWm.mLetterboxConfiguration;
- mTestCurrentTimeMillisSupplier = new CurrentTimeMillisSupplierTest();
- }
-
- void prepareRelaunchingAfterRequestedOrientationChanged(boolean enabled) {
- getTopOrientationCapability().setRelaunchingAfterRequestedOrientationChanged(enabled);
- }
-
- void prepareIsPolicyForIgnoringRequestedOrientationEnabled(boolean enabled) {
- doReturn(enabled).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
- }
-
- void prepareIsCameraCompatTreatmentEnabled(boolean enabled) {
- doReturn(enabled).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
- }
-
- void prepareIsCameraCompatTreatmentEnabledAtBuildTime(boolean enabled) {
- doReturn(enabled).when(mLetterboxConfiguration)
- .isCameraCompatTreatmentEnabledAtBuildTime();
- }
-
- void prepareIsTreatmentEnabledForTopActivity(boolean enabled) {
- final DisplayRotationCompatPolicy displayPolicy = mActivityStack.top()
- .mDisplayContent.mDisplayRotationCompatPolicy;
- spyOn(displayPolicy);
- doReturn(enabled).when(displayPolicy)
- .isTreatmentEnabledForActivity(eq(mActivityStack.top()));
- }
-
- // Useful to reduce timeout during tests
- void prepareMockedTime() {
- getTopOrientationCapability().mOrientationCapabilityState.mCurrentTimeMillisSupplier =
- mTestCurrentTimeMillisSupplier;
- }
-
- void delay() {
- mTestCurrentTimeMillisSupplier.delay(SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS);
- }
-
- void enableProperty(@NonNull String propertyName) {
- setPropertyValue(propertyName, /* enabled */ true);
- }
-
- void disableProperty(@NonNull String propertyName) {
- setPropertyValue(propertyName, /* enabled */ false);
- }
-
- void prepareIsLetterboxedForFixedOrientationAndAspectRatio(boolean enabled) {
- spyOn(mActivityStack.top());
- doReturn(enabled).when(mActivityStack.top())
- .isLetterboxedForFixedOrientationAndAspectRatio();
- }
-
- void createActivityWithComponent() {
- createActivityWithComponentInNewTask(/* inNewTask */ mTaskStack.isEmpty());
- }
-
- void createActivityWithComponentInNewTask() {
- createActivityWithComponentInNewTask(/* inNewTask */ true);
- }
-
- private void createActivityWithComponentInNewTask(boolean inNewTask) {
- if (inNewTask) {
- createNewTask();
- }
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setOnTop(true)
- .setTask(mTaskStack.top())
- // Set the component to be that of the test class in order
- // to enable compat changes
- .setComponent(ComponentName.createRelative(mAtm.mContext,
- com.android.server.wm.LetterboxUiControllerTest.class.getName()))
- .build();
- mActivityStack.push(activity);
- }
-
- void checkShouldIgnoreRequestedOrientation(
- @Configuration.Orientation int expectedOrientation) {
- assertTrue(getTopOrientationCapability()
- .shouldIgnoreRequestedOrientation(expectedOrientation));
- }
-
- void checkShouldNotIgnoreRequestedOrientation(
- @Configuration.Orientation int expectedOrientation) {
- assertFalse(getTopOrientationCapability()
- .shouldIgnoreRequestedOrientation(expectedOrientation));
- }
-
- void checkExpectedLoopCount(int expectedCount) {
- assertEquals(expectedCount, getTopOrientationCapability()
- .getSetOrientationRequestCounter());
- }
-
- void checkShouldNotIgnoreOrientationLoop() {
- assertFalse(getTopOrientationCapability().shouldIgnoreOrientationRequestLoop());
- }
-
- void checkShouldIgnoreOrientationLoop() {
- assertTrue(getTopOrientationCapability().shouldIgnoreOrientationRequestLoop());
- }
-
- void checkRequestLoop(IntConsumer consumer) {
- for (int i = 0; i < MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
- consumer.accept(i);
- }
- }
-
- void checkRequestLoopExtended(IntConsumer consumer) {
- for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
- consumer.accept(i);
- }
- }
-
- private AppCompatOrientationCapability getTopOrientationCapability() {
- return mActivityStack.top().mAppCompatController.getAppCompatCapability()
- .getAppCompatOrientationCapability();
- }
-
- private void createNewTask() {
- final DisplayContent displayContent = new TestDisplayContent
- .Builder(mAtm, /* dw */ 1000, /* dh */ 2000).build();
- final Task newTask = new TaskBuilder(mSupervisor).setDisplay(displayContent).build();
- mTaskStack.push(newTask);
- }
-
- private void setPropertyValue(@NonNull String propertyName, boolean enabled) {
- PackageManager.Property property = new PackageManager.Property(propertyName,
- /* value */ enabled, /* packageName */ "",
- /* className */ "");
- PackageManager pm = mWm.mContext.getPackageManager();
- spyOn(pm);
- try {
- doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
- } catch (PackageManager.NameNotFoundException e) {
- fail(e.getLocalizedMessage());
- }
- }
-
- private static class CurrentTimeMillisSupplierTest implements LongSupplier {
-
- private long mCurrenTimeMillis = System.currentTimeMillis();
-
- @Override
- public long getAsLong() {
- return mCurrenTimeMillis;
- }
-
- public void delay(long delay) {
- mCurrenTimeMillis += delay;
- }
- }
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
new file mode 100644
index 000000000000..1720b64f558b
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.AppCompatOrientationOverrides.OrientationOverridesState.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP;
+import static com.android.server.wm.AppCompatOrientationOverrides.OrientationOverridesState.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.res.Configuration;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.wm.utils.CurrentTimeMillisSupplierFake;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+
+/**
+ * Test class for {@link AppCompatOrientationOverrides}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatOrientationOverridesTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatOrientationOverridesTest extends WindowTestsBase {
+
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
+ public void testShouldIgnoreRequestedOrientation_activityRelaunching_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.activity().createActivityWithComponent();
+ robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ true,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
+ public void testShouldIgnoreRequestedOrientation_cameraCompatTreatment_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.applyOnConf((c) -> {
+ c.enableCameraCompatTreatment(true);
+ c.enableCameraCompatTreatmentAtBuildTime(true);
+ c.enablePolicyForIgnoringRequestedOrientation(true);
+ });
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponentInNewTask();
+ a.enableTreatmentForTopActivity(true);
+ });
+ robot.prepareRelaunchingAfterRequestedOrientationChanged(false);
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ true,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ public void testShouldIgnoreRequestedOrientation_overrideDisabled_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+
+ robot.activity().createActivityWithComponent();
+ robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ false,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ public void testShouldIgnoreRequestedOrientation_propertyIsTrue_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.prop().enable(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION);
+
+ robot.activity().createActivityWithComponent();
+ robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ true,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
+ public void testShouldIgnoreRequestedOrientation_propertyIsFalseAndOverride_returnsFalse()
+ throws Exception {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.prop().disable(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION);
+
+ robot.activity().createActivityWithComponent();
+ robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ false,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ public void testShouldIgnoreOrientationRequestLoop_overrideDisabled_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+ robot.checkRequestLoopExtended((i) -> {
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ 0);
+ });
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+ public void testShouldIgnoreOrientationRequestLoop_propertyIsFalseAndOverride_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.prop().disable(
+ PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+ robot.checkRequestLoopExtended((i) -> {
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ 0);
+ });
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+ public void testShouldIgnoreOrientationRequestLoop_isLetterboxed_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(true);
+ });
+ robot.checkRequestLoopExtended((i) -> {
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ i);
+ });
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+ public void testShouldIgnoreOrientationRequestLoop_noLoop_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ 0);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+ public void testShouldIgnoreOrientationRequestLoop_timeout_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+
+ robot.prepareMockedTime();
+ robot.checkRequestLoopExtended((i) -> {
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ 0);
+ robot.delay();
+ });
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+ public void testShouldIgnoreOrientationRequestLoop_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+
+ robot.checkRequestLoop((i) -> {
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ i);
+ });
+ robot.checkShouldIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
+ public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ false,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ void runTestScenario(@NonNull Consumer<OrientationOverridesRobotTest> consumer) {
+ spyOn(mWm.mLetterboxConfiguration);
+ final OrientationOverridesRobotTest robot =
+ new OrientationOverridesRobotTest(mWm, mAtm, mSupervisor);
+ consumer.accept(robot);
+ }
+
+ private static class OrientationOverridesRobotTest extends AppCompatRobotBase {
+
+ @NonNull
+ private final CurrentTimeMillisSupplierFake mTestCurrentTimeMillisSupplier;
+
+ OrientationOverridesRobotTest(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor) {
+ super(wm, atm, supervisor);
+ mTestCurrentTimeMillisSupplier = new CurrentTimeMillisSupplierFake();
+ }
+
+ void prepareRelaunchingAfterRequestedOrientationChanged(boolean enabled) {
+ getTopOrientationOverrides().setRelaunchingAfterRequestedOrientationChanged(enabled);
+ }
+
+ // Useful to reduce timeout during tests
+ void prepareMockedTime() {
+ getTopOrientationOverrides().mOrientationOverridesState.mCurrentTimeMillisSupplier =
+ mTestCurrentTimeMillisSupplier;
+ }
+
+ void delay() {
+ mTestCurrentTimeMillisSupplier.delay(SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS);
+ }
+
+ void checkShouldIgnoreRequestedOrientation(boolean expected,
+ @Configuration.Orientation int requestedOrientation) {
+ assertEquals(expected, getTopOrientationOverrides()
+ .shouldIgnoreRequestedOrientation(requestedOrientation));
+ }
+
+ void checkExpectedLoopCount(int expectedCount) {
+ assertEquals(expectedCount, getTopOrientationOverrides()
+ .getSetOrientationRequestCounter());
+ }
+
+ void checkShouldNotIgnoreOrientationLoop() {
+ assertFalse(getTopOrientationOverrides().shouldIgnoreOrientationRequestLoop());
+ }
+
+ void checkShouldIgnoreOrientationLoop() {
+ assertTrue(getTopOrientationOverrides().shouldIgnoreOrientationRequestLoop());
+ }
+
+ void checkRequestLoop(IntConsumer consumer) {
+ for (int i = 0; i < MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
+ consumer.accept(i);
+ }
+ }
+
+ void checkRequestLoopExtended(IntConsumer consumer) {
+ for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
+ consumer.accept(i);
+ }
+ }
+
+ private AppCompatOrientationOverrides getTopOrientationOverrides() {
+ return activity().top().mAppCompatController.getAppCompatOverrides()
+ .getAppCompatOrientationOverrides();
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
index 22609990cfeb..9885a2d37b07 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -34,24 +34,16 @@ import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import android.compat.testing.PlatformCompatChangeRule;
-import android.content.ComponentName;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
-import com.android.server.wm.utils.TestComponentStack;
-
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
import org.junit.Assert;
@@ -87,7 +79,7 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_returnsUser() {
runTestScenarioWithActivity((robot) -> {
- robot.configureSetIgnoreOrientationRequest(true);
+ robot.activity().setIgnoreOrientationRequest(true);
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
/* expected */ SCREEN_ORIENTATION_USER);
});
@@ -97,9 +89,11 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_optOut_isUnchanged() {
runTestScenario((robot) -> {
- robot.disableProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
- robot.createActivityWithComponent();
- robot.configureSetIgnoreOrientationRequest(true);
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ });
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
/* expected */ SCREEN_ORIENTATION_PORTRAIT);
@@ -110,12 +104,13 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
public void testOverrideOrientationIfNeeded_fullscreenOverrides_optOutSystem_returnsUser() {
runTestScenario((robot) -> {
- robot.disableProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
- robot.configureIsUserAppAspectRatioFullscreenEnabled(true);
-
- robot.createActivityWithComponent();
- robot.configureSetIgnoreOrientationRequest(true);
- robot.prepareGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+ robot.conf().enableUserAppAspectRatioFullscreen(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+ });
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
/* expected */ SCREEN_ORIENTATION_USER);
@@ -126,12 +121,13 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
public void testOverrideOrientationIfNeeded_fullscreenOverrides_optOutUser_returnsUser() {
runTestScenario((robot) -> {
- robot.disableProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
- robot.configureIsUserAppAspectRatioFullscreenEnabled(true);
-
- robot.createActivityWithComponent();
- robot.configureSetIgnoreOrientationRequest(true);
- robot.prepareGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
+ robot.conf().enableUserAppAspectRatioFullscreen(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+ });
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
/* expected */ SCREEN_ORIENTATION_USER);
@@ -143,7 +139,7 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_returnsUnchanged()
throws Exception {
runTestScenarioWithActivity((robot) -> {
- robot.configureSetIgnoreOrientationRequest(false);
+ robot.activity().setIgnoreOrientationRequest(false);
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
/* expected */ SCREEN_ORIENTATION_PORTRAIT);
@@ -154,11 +150,13 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
public void testOverrideOrientationIfNeeded_fullscreenAndUserOverrideEnabled_isUnchanged() {
runTestScenario((robot) -> {
- robot.prepareIsUserAppAspectRatioSettingsEnabled(true);
+ robot.conf().enableUserAppAspectRatioSettings(true);
- robot.createActivityWithComponent();
- robot.configureSetIgnoreOrientationRequest(true);
- robot.prepareGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+ });
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
/* expected */ SCREEN_ORIENTATION_PORTRAIT);
@@ -249,9 +247,9 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
public void testOverrideOrientationIfNeeded_propertyIsFalse_isUnchanged()
throws Exception {
runTestScenario((robot) -> {
- robot.disableProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
- robot.createActivityWithComponent();
+ robot.activity().createActivityWithComponent();
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
/* expected */ SCREEN_ORIENTATION_UNSPECIFIED);
@@ -263,11 +261,14 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
public void testOverrideOrientationIfNeeded_whenCameraNotActive_isUnchanged() {
runTestScenario((robot) -> {
- robot.configureIsCameraCompatTreatmentEnabled(true);
- robot.configureIsCameraCompatTreatmentEnabledAtBuildTime(true);
-
- robot.createActivityWithComponentInNewTask();
- robot.prepareIsTopActivityEligibleForOrientationOverride(false);
+ robot.applyOnConf((c)-> {
+ c.enableCameraCompatTreatment(true);
+ c.enableCameraCompatTreatmentAtBuildTime(true);
+ });
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponentInNewTask();
+ a.setTopActivityEligibleForOrientationOverride(false);
+ });
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
/* expected */ SCREEN_ORIENTATION_UNSPECIFIED);
@@ -279,11 +280,14 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
public void testOverrideOrientationIfNeeded_whenCameraActive_returnsPortrait() {
runTestScenario((robot) -> {
- robot.configureIsCameraCompatTreatmentEnabled(true);
- robot.configureIsCameraCompatTreatmentEnabledAtBuildTime(true);
-
- robot.createActivityWithComponentInNewTask();
- robot.prepareIsTopActivityEligibleForOrientationOverride(true);
+ robot.applyOnConf((c) -> {
+ c.enableCameraCompatTreatment(true);
+ c.enableCameraCompatTreatmentAtBuildTime(true);
+ });
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponentInNewTask();
+ a.setTopActivityEligibleForOrientationOverride(true);
+ });
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
/* expected */ SCREEN_ORIENTATION_PORTRAIT);
@@ -293,8 +297,10 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@Test
public void testOverrideOrientationIfNeeded_userFullscreenOverride_returnsUser() {
runTestScenarioWithActivity((robot) -> {
- robot.prepareShouldApplyUserFullscreenOverride(true);
- robot.configureSetIgnoreOrientationRequest(true);
+ robot.applyOnActivity((a) -> {
+ a.setShouldApplyUserFullscreenOverride(true);
+ a.setIgnoreOrientationRequest(true);
+ });
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
/* expected */ SCREEN_ORIENTATION_USER);
@@ -304,11 +310,14 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@Test
public void testOverrideOrientationIfNeeded_fullscreenOverride_cameraActivity_unchanged() {
runTestScenario((robot) -> {
- robot.configureIsCameraCompatTreatmentEnabled(true);
- robot.configureIsCameraCompatTreatmentEnabledAtBuildTime(true);
-
- robot.createActivityWithComponentInNewTask();
- robot.configureIsTopActivityCameraActive(false);
+ robot.applyOnConf((c) -> {
+ c.enableCameraCompatTreatment(true);
+ c.enableCameraCompatTreatmentAtBuildTime(true);
+ });
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponentInNewTask();
+ a.setTopActivityCameraActive(false);
+ });
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
/* expected */ SCREEN_ORIENTATION_PORTRAIT);
@@ -318,8 +327,10 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@Test
public void testOverrideOrientationIfNeeded_respectOrientationRequestOverUserFullScreen() {
runTestScenarioWithActivity((robot) -> {
- robot.prepareShouldApplyUserFullscreenOverride(true);
- robot.configureSetIgnoreOrientationRequest(false);
+ robot.applyOnActivity((a) -> {
+ a.setShouldApplyUserFullscreenOverride(true);
+ a.setIgnoreOrientationRequest(false);
+ });
robot.checkOverrideOrientationIsNot(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
/* notExpected */ SCREEN_ORIENTATION_USER);
@@ -330,8 +341,10 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
public void testOverrideOrientationIfNeeded_userFullScreenOverrideOverSystem_returnsUser() {
runTestScenarioWithActivity((robot) -> {
- robot.prepareShouldApplyUserFullscreenOverride(true);
- robot.configureSetIgnoreOrientationRequest(true);
+ robot.applyOnActivity((a) -> {
+ a.setShouldApplyUserFullscreenOverride(true);
+ a.setIgnoreOrientationRequest(true);
+ });
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
/* expected */ SCREEN_ORIENTATION_USER);
@@ -342,9 +355,10 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
public void testOverrideOrientationIfNeeded_respectOrientationReqOverUserFullScreenAndSystem() {
runTestScenarioWithActivity((robot) -> {
- robot.prepareShouldApplyUserFullscreenOverride(true);
- robot.configureSetIgnoreOrientationRequest(false);
-
+ robot.applyOnActivity((a) -> {
+ a.setShouldApplyUserFullscreenOverride(true);
+ a.setIgnoreOrientationRequest(false);
+ });
robot.checkOverrideOrientationIsNot(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
/* notExpected */ SCREEN_ORIENTATION_USER);
});
@@ -353,7 +367,7 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@Test
public void testOverrideOrientationIfNeeded_userFullScreenOverrideDisabled_returnsUnchanged() {
runTestScenarioWithActivity((robot) -> {
- robot.prepareShouldApplyUserFullscreenOverride(false);
+ robot.activity().setShouldApplyUserFullscreenOverride(false);
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
/* expected */ SCREEN_ORIENTATION_PORTRAIT);
@@ -363,7 +377,7 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@Test
public void testOverrideOrientationIfNeeded_userAspectRatioApplied_unspecifiedOverridden() {
runTestScenarioWithActivity((robot) -> {
- robot.prepareShouldApplyUserMinAspectRatioOverride(true);
+ robot.activity().setShouldApplyUserMinAspectRatioOverride(true);
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
/* expected */ SCREEN_ORIENTATION_PORTRAIT);
@@ -377,7 +391,7 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@Test
public void testOverrideOrientationIfNeeded_userAspectRatioNotApplied_isUnchanged() {
runTestScenarioWithActivity((robot) -> {
- robot.prepareShouldApplyUserFullscreenOverride(false);
+ robot.activity().setShouldApplyUserFullscreenOverride(false);
robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
/* expected */ SCREEN_ORIENTATION_UNSPECIFIED);
@@ -410,99 +424,24 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
consumer.accept(robot);
}
- private static class OrientationPolicyRobotTest {
+ private static class OrientationPolicyRobotTest extends AppCompatRobotBase{
- @NonNull
- private final ActivityTaskManagerService mAtm;
- @NonNull
private final WindowManagerService mWm;
- @NonNull
- private final LetterboxConfiguration mLetterboxConfiguration;
- @NonNull
- private final TestComponentStack<ActivityRecord> mActivityStack;
- @NonNull
- private final TestComponentStack<Task> mTaskStack;
-
- @NonNull
- private final ActivityTaskSupervisor mSupervisor;
OrientationPolicyRobotTest(@NonNull WindowManagerService wm,
@NonNull ActivityTaskManagerService atm,
@NonNull ActivityTaskSupervisor supervisor,
boolean withActivity) {
- mAtm = atm;
+ super(wm, atm, supervisor);
mWm = wm;
spyOn(mWm);
- mSupervisor = supervisor;
- mActivityStack = new TestComponentStack<>();
- mTaskStack = new TestComponentStack<>();
- mLetterboxConfiguration = mWm.mLetterboxConfiguration;
if (withActivity) {
- createActivityWithComponent();
+ activity().createActivityWithComponent();
}
}
- void configureSetIgnoreOrientationRequest(boolean enabled) {
- mActivityStack.top().mDisplayContent.setIgnoreOrientationRequest(enabled);
- }
-
- void configureIsUserAppAspectRatioFullscreenEnabled(boolean enabled) {
- doReturn(enabled).when(mLetterboxConfiguration).isUserAppAspectRatioFullscreenEnabled();
- }
-
- void configureIsCameraCompatTreatmentEnabled(boolean enabled) {
- doReturn(enabled).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
- }
-
- void configureIsCameraCompatTreatmentEnabledAtBuildTime(boolean enabled) {
- doReturn(enabled).when(mLetterboxConfiguration)
- .isCameraCompatTreatmentEnabledAtBuildTime();
- }
-
- void prepareGetUserMinAspectRatioOverrideCode(int orientation) {
- spyOn(mActivityStack.top().mLetterboxUiController);
- doReturn(orientation).when(mActivityStack.top()
- .mLetterboxUiController).getUserMinAspectRatioOverrideCode();
- }
-
- void prepareShouldApplyUserFullscreenOverride(boolean enabled) {
- spyOn(mActivityStack.top().mLetterboxUiController);
- doReturn(enabled).when(mActivityStack.top()
- .mLetterboxUiController).shouldApplyUserFullscreenOverride();
- }
-
- void prepareShouldApplyUserMinAspectRatioOverride(boolean enabled) {
- spyOn(mActivityStack.top().mLetterboxUiController);
- doReturn(enabled).when(mActivityStack.top()
- .mLetterboxUiController).shouldApplyUserMinAspectRatioOverride();
- }
-
- void prepareIsUserAppAspectRatioSettingsEnabled(boolean enabled) {
- doReturn(enabled).when(mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
- }
-
- void prepareIsTopActivityEligibleForOrientationOverride(boolean enabled) {
- final DisplayRotationCompatPolicy displayPolicy =
- mActivityStack.top().mDisplayContent.mDisplayRotationCompatPolicy;
- spyOn(displayPolicy);
- doReturn(enabled).when(displayPolicy)
- .isActivityEligibleForOrientationOverride(eq(mActivityStack.top()));
- }
-
- void configureIsTopActivityCameraActive(boolean enabled) {
- final DisplayRotationCompatPolicy displayPolicy =
- mActivityStack.top().mDisplayContent.mDisplayRotationCompatPolicy;
- spyOn(displayPolicy);
- doReturn(enabled).when(displayPolicy)
- .isCameraActive(eq(mActivityStack.top()), /* mustBeFullscreen= */ eq(true));
- }
-
- void disableProperty(@NonNull String propertyName) {
- setPropertyValue(propertyName, /* enabled */ false);
- }
-
int overrideOrientationIfNeeded(@ActivityInfo.ScreenOrientation int candidate) {
- return mActivityStack.top().mAppCompatController.getOrientationPolicy()
+ return activity().top().mAppCompatController.getOrientationPolicy()
.overrideOrientationIfNeeded(candidate);
}
@@ -519,52 +458,5 @@ public class AppCompatOrientationPolicyTest extends WindowTestsBase {
@ActivityInfo.ScreenOrientation int notExpected) {
Assert.assertNotEquals(notExpected, overrideOrientationIfNeeded(candidate));
}
-
- private void createActivityWithComponent() {
- if (mTaskStack.isEmpty()) {
- final DisplayContent displayContent = new TestDisplayContent
- .Builder(mAtm, /* dw */ 1000, /* dh */ 2000).build();
- final Task task = new TaskBuilder(mSupervisor).setDisplay(displayContent).build();
- mTaskStack.push(task);
- }
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setOnTop(true)
- .setTask(mTaskStack.top())
- // Set the component to be that of the test class in order
- // to enable compat changes
- .setComponent(ComponentName.createRelative(mAtm.mContext,
- com.android.server.wm.LetterboxUiControllerTest.class.getName()))
- .build();
- mActivityStack.push(activity);
- }
-
- private void createActivityWithComponentInNewTask() {
- final DisplayContent displayContent = new TestDisplayContent
- .Builder(mAtm, /* dw */ 1000, /* dh */ 2000).build();
- final Task task = new TaskBuilder(mSupervisor).setDisplay(displayContent).build();
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setOnTop(true)
- .setTask(task)
- // Set the component to be that of the test class in order
- // to enable compat changes
- .setComponent(ComponentName.createRelative(mAtm.mContext,
- com.android.server.wm.LetterboxUiControllerTest.class.getName()))
- .build();
- mTaskStack.push(task);
- mActivityStack.push(activity);
- }
-
- private void setPropertyValue(@NonNull String propertyName, boolean enabled) {
- PackageManager.Property property = new PackageManager.Property(propertyName,
- /* value */ enabled, /* packageName */ "",
- /* className */ "");
- PackageManager pm = mWm.mContext.getPackageManager();
- spyOn(pm);
- try {
- doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
- } catch (PackageManager.NameNotFoundException e) {
- fail(e.getLocalizedMessage());
- }
- }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
new file mode 100644
index 000000000000..de16e3888022
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import androidx.annotation.NonNull;
+
+import java.util.function.Consumer;
+
+/**
+ * Base class for the Robots related to App Compat tests.
+ */
+abstract class AppCompatRobotBase {
+
+ private static final int DEFAULT_DISPLAY_WIDTH = 1000;
+ private static final int DEFAULT_DISPLAY_HEIGHT = 2000;
+
+ @NonNull
+ private final AppCompatActivityRobot mActivityRobot;
+ @NonNull
+ private final AppCompatLetterboxConfigurationRobot mConfigurationRobot;
+ @NonNull
+ private final AppCompatComponentPropRobot mOptPropRobot;
+
+ AppCompatRobotBase(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor,
+ int displayWidth, int displayHeight) {
+ mActivityRobot = new AppCompatActivityRobot(wm, atm, supervisor,
+ displayWidth, displayHeight);
+ mConfigurationRobot =
+ new AppCompatLetterboxConfigurationRobot(wm.mLetterboxConfiguration);
+ mOptPropRobot = new AppCompatComponentPropRobot(wm);
+ }
+
+ AppCompatRobotBase(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor) {
+ this(wm, atm, supervisor, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
+ }
+
+ @NonNull
+ AppCompatLetterboxConfigurationRobot conf() {
+ return mConfigurationRobot;
+ }
+
+ @NonNull
+ void applyOnConf(@NonNull Consumer<AppCompatLetterboxConfigurationRobot> consumer) {
+ consumer.accept(mConfigurationRobot);
+ }
+
+ @NonNull
+ AppCompatActivityRobot activity() {
+ return mActivityRobot;
+ }
+
+ @NonNull
+ void applyOnActivity(@NonNull Consumer<AppCompatActivityRobot> consumer) {
+ consumer.accept(mActivityRobot);
+ }
+
+ @NonNull
+ AppCompatComponentPropRobot prop() {
+ return mOptPropRobot;
+ }
+
+ @NonNull
+ void applyOnProp(@NonNull Consumer<AppCompatComponentPropRobot> consumer) {
+ consumer.accept(mOptPropRobot);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatTransparentActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatTransparentActivityRobot.java
new file mode 100644
index 000000000000..3cfbb9e708f9
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatTransparentActivityRobot.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.WindowConfiguration;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.junit.Assert;
+
+import java.util.function.Consumer;
+
+/**
+ * Robot implementation for {@link ActivityRecord} when dealing with transparent activities.
+ */
+class AppCompatTransparentActivityRobot {
+
+ @Nullable
+ private WindowConfiguration mTopActivityWindowConfiguration;
+
+ @NonNull
+ private final AppCompatActivityRobot mActivityRobot;
+
+ AppCompatTransparentActivityRobot(@NonNull AppCompatActivityRobot activityRobot) {
+ mActivityRobot = activityRobot;
+ }
+
+ @NonNull
+ AppCompatActivityRobot activity() {
+ return mActivityRobot;
+ }
+
+ @NonNull
+ void applyOnActivity(@NonNull Consumer<AppCompatActivityRobot> consumer) {
+ consumer.accept(mActivityRobot);
+ }
+
+ void launchTransparentActivity() {
+ mActivityRobot.launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
+ SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true,
+ /* withComponent */ false, /* addToTask */ false);
+ }
+
+ void launchTransparentActivityInTask() {
+ mActivityRobot.launchActivity(/*minAspectRatio */ -1,
+ /* maxAspectRatio */ -1, SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true,
+ /* withComponent */ false, /* addToTask */true);
+ }
+
+ void launchOpaqueActivityInTask() {
+ mActivityRobot.launchActivity(/*minAspectRatio */ -1,
+ /* maxAspectRatio */ -1, SCREEN_ORIENTATION_PORTRAIT, /* transparent */ false,
+ /* withComponent */ false, /* addToTask */true);
+ }
+
+ void forceChangeInTopActivityConfiguration() {
+ activity().applyToTopActivity((topActivity) -> {
+ final Configuration requestedConfig =
+ topActivity.getRequestedOverrideConfiguration();
+ mTopActivityWindowConfiguration = requestedConfig.windowConfiguration;
+ mTopActivityWindowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+ mTopActivityWindowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ mTopActivityWindowConfiguration.setAlwaysOnTop(true);
+ topActivity.onRequestedOverrideConfigurationChanged(requestedConfig);
+ });
+ }
+
+ void checkTopActivityConfigurationConfiguration() {
+ activity().applyToTopActivity((topActivity) -> {
+ // The original override of WindowConfiguration should keep.
+ assertEquals(ACTIVITY_TYPE_STANDARD, topActivity.getActivityType());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW,
+ mTopActivityWindowConfiguration.getWindowingMode());
+ assertTrue(mTopActivityWindowConfiguration.isAlwaysOnTop());
+ // Unless display is going to be rotated, it should always inherit from parent.
+ assertEquals(ROTATION_UNDEFINED,
+ mTopActivityWindowConfiguration.getDisplayRotation());
+ });
+ }
+
+ void checkTopActivityTransparentPolicyStateIsRunning(boolean running) {
+ assertEquals(running,
+ activity().top().mAppCompatController.getTransparentPolicy().isRunning());
+ }
+
+ void checkTopActivityTransparentPolicyStartInvoked() {
+ activity().applyToTopActivity((topActivity) -> {
+ verify(topActivity.mAppCompatController.getTransparentPolicy()).start();
+ });
+ }
+
+ void checkTopActivityTransparentPolicyStartNotInvoked() {
+ verify(activity().top().mAppCompatController.getTransparentPolicy(), never()).start();
+ }
+
+ void checkTopActivityTransparentPolicyStopInvoked() {
+ verify(activity().top().mAppCompatController.getTransparentPolicy()).stop();
+ }
+
+ void checkTopActivityTransparentPolicyStopNotInvoked() {
+ verify(activity().top().mAppCompatController.getTransparentPolicy(), never()).stop();
+ }
+
+ void checkTopActivityHasInheritedBoundsFrom(int fromTop) {
+ final ActivityRecord topActivity = activity().top();
+ final ActivityRecord otherActivity = activity().getFromTop(/* fromTop */ fromTop);
+ final Rect opaqueBounds = otherActivity.getConfiguration().windowConfiguration
+ .getBounds();
+ final Rect translucentRequestedBounds = topActivity.getRequestedOverrideBounds();
+ Assert.assertEquals(opaqueBounds, translucentRequestedBounds);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 8b4d7796290c..63c14b90958f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -159,7 +159,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status to test translucent activity
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
final ActivityRecord topActivity = topTask.getTopMostActivity();
makeWindowVisibleAndDrawn(topActivity.findMainWindow());
// simulate translucent
@@ -170,7 +170,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status to test if previous task is translucent activity
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
+ makeWindowVisibleAndDrawn(topActivity.findMainWindow());
// simulate translucent
recordA.setOccludesParent(false);
backNavigationInfo = startBackNavigation();
@@ -181,7 +182,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
topActivity.setOccludesParent(true);
recordA.setOccludesParent(true);
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(topActivity.findMainWindow());
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
@@ -189,7 +190,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
doReturn(true).when(recordA).canShowWhenLocked();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -248,7 +249,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status
testCase.recordBack.setState(STOPPED, "stopped");
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(testCase.recordFront.findMainWindow());
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
@@ -257,7 +258,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status, test if top activity is translucent
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(testCase.recordFront.findMainWindow());
// simulate translucent
testCase.recordFront.setOccludesParent(false);
@@ -268,7 +269,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status, test if bottom activity is translucent
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(testCase.recordBack.findMainWindow());
// simulate translucent
testCase.recordBack.setOccludesParent(false);
@@ -279,7 +280,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status, test canShowWhenLocked
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
doReturn(true).when(testCase.recordBack).canShowWhenLocked();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -482,7 +483,10 @@ public class BackNavigationControllerTests extends WindowTestsBase {
.isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
+
+ final WindowState window = topTask.getTopVisibleAppMainWindow();
+ makeWindowVisibleAndDrawn(window);
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -842,7 +846,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
toHomeBuilder.build();
verify(mAtm.mTaskOrganizerController, never()).addWindowlessStartingSurface(
any(), any(), any(), any(), any(), any());
- animationHandler.clearBackAnimateTarget();
+ animationHandler.clearBackAnimateTarget(true);
openActivities.clear();
// Back to ACTIVITY and TASK have the same logic, just with different target.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index b21eca70fdf9..2bda9500905e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -19,7 +19,6 @@ package com.android.server.wm;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -106,31 +105,7 @@ public class ClientLifecycleManagerTests extends SystemServiceTestsBase {
}
@Test
- public void testScheduleTransactionItem_notBundle() throws RemoteException {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mTransactionItem);
-
- verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture());
- ClientTransaction transaction = mTransactionCaptor.getValue();
- assertEquals(1, transaction.getCallbacks().size());
- assertEquals(mTransactionItem, transaction.getCallbacks().get(0));
- assertNull(transaction.getLifecycleStateRequest());
- assertNull(transaction.getTransactionItems());
-
- clearInvocations(mLifecycleManager);
- mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem);
-
- verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture());
- transaction = mTransactionCaptor.getValue();
- assertNull(transaction.getCallbacks());
- assertEquals(mLifecycleItem, transaction.getLifecycleStateRequest());
- }
-
- @Test
public void testScheduleTransactionItem() throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
@@ -176,23 +151,7 @@ public class ClientLifecycleManagerTests extends SystemServiceTestsBase {
}
@Test
- public void testScheduleTransactionAndLifecycleItems_notBundle() throws RemoteException {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionAndLifecycleItems(mNonBinderClient, mTransactionItem,
- mLifecycleItem);
-
- verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture());
- final ClientTransaction transaction = mTransactionCaptor.getValue();
- assertEquals(1, transaction.getCallbacks().size());
- assertEquals(mTransactionItem, transaction.getCallbacks().get(0));
- assertEquals(mLifecycleItem, transaction.getLifecycleStateRequest());
- }
-
- @Test
public void testScheduleTransactionAndLifecycleItems() throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
@@ -216,7 +175,6 @@ public class ClientLifecycleManagerTests extends SystemServiceTestsBase {
@Test
public void testScheduleTransactionAndLifecycleItems_shouldDispatchImmediately()
throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
@@ -230,8 +188,6 @@ public class ClientLifecycleManagerTests extends SystemServiceTestsBase {
@Test
public void testDispatchPendingTransactions() throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
mLifecycleManager.mPendingTransactions.put(mClientBinder, mTransaction);
mLifecycleManager.dispatchPendingTransactions();
@@ -243,7 +199,6 @@ public class ClientLifecycleManagerTests extends SystemServiceTestsBase {
@Test
public void testLayoutDeferred() throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
spyOn(mWms.mWindowPlacerLocked);
doReturn(false).when(mWms.mWindowPlacerLocked).isInLayout();
doReturn(false).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 3a854511e3de..64527cb63e45 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1315,6 +1315,47 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+ public void testOverrideMinAspectRatioSmall_overridden() {
+ final int dh = 1200;
+ final int dw = 1000;
+ setUpDisplaySizeWithApp(dw, dh);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .build();
+
+ final Rect bounds = activity.getBounds();
+ assertEquals(dh, bounds.height());
+ assertEquals(dh / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE,
+ bounds.width(), 0.5f);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+ public void testOverrideMinAspectRatioSmall_notOverridden() {
+ final int dh = 1200;
+ final int dw = 1000;
+ setUpDisplaySizeWithApp(dw, dh);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
+ .build();
+
+ // Activity's requested aspect ratio is larger than OVERRIDE_MIN_ASPECT_RATIO_SMALL,
+ // so OVERRIDE_MIN_ASPECT_RATIO_SMALL is ignored.
+ final Rect bounds = activity.getBounds();
+ assertEquals(dh, bounds.height());
+ assertEquals(dh / OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ bounds.width(), 0.5f);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioMedium() {
setUpDisplaySizeWithApp(1000, 1200);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index 6c5f9752b6fc..1c32980aac91 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -33,6 +33,7 @@ import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -417,6 +418,22 @@ public class SyncEngineTests extends WindowTestsBase {
}
@Test
+ public void testSkipPrepareSync() {
+ final TestWindowContainer wc = new TestWindowContainer(mWm, true /* waiter */);
+ wc.mSkipPrepareSync = true;
+ final BLASTSyncEngine bse = createTestBLASTSyncEngine();
+ final BLASTSyncEngine.SyncGroup syncGroup = bse.prepareSyncSet(
+ mock(BLASTSyncEngine.TransactionReadyListener.class), "test");
+ bse.startSyncSet(syncGroup);
+ bse.addToSyncSet(syncGroup.mSyncId, wc);
+ assertEquals(SYNC_STATE_NONE, wc.mSyncState);
+ // If the implementation of prepareSync doesn't set sync state, the sync group should also
+ // be empty.
+ assertNull(wc.mSyncGroup);
+ assertTrue(wc.isSyncFinished(syncGroup));
+ }
+
+ @Test
public void testNonBlastMethod() {
mAppWindow = createWindow(null, TYPE_BASE_APPLICATION, "mAppWindow");
@@ -694,6 +711,7 @@ public class SyncEngineTests extends WindowTestsBase {
final boolean mWaiter;
boolean mVisibleRequested = true;
boolean mFillsParent = false;
+ boolean mSkipPrepareSync = false;
TestWindowContainer(WindowManagerService wms, boolean waiter) {
super(wms);
@@ -703,6 +721,9 @@ public class SyncEngineTests extends WindowTestsBase {
@Override
boolean prepareSync() {
+ if (mSkipPrepareSync) {
+ return false;
+ }
if (!super.prepareSync()) {
return false;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 9670a9a44eb7..a71b81e025d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -26,8 +26,8 @@ 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.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE;
+import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
@@ -220,30 +220,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
}
@Test
- public void testOnTaskFragmentAppeared_throughTaskFragmentOrganizer() throws RemoteException {
- mSetFlagsRule.disableFlags(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- // No-op when the TaskFragment is not attached.
- mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
- mController.dispatchPendingEvents();
-
- verify(mOrganizer, never()).onTransactionReady(any());
- verify(mAppThread, never()).scheduleTaskFragmentTransaction(any(), any());
-
- // Send callback when the TaskFragment is attached.
- setupMockParent(mTaskFragment, mTask);
-
- mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
- mController.dispatchPendingEvents();
-
- assertTaskFragmentParentInfoChangedTransaction(mTask);
- assertTaskFragmentAppearedTransaction(false /* hasSurfaceControl */);
- verify(mAppThread, never()).scheduleTaskFragmentTransaction(any(), any());
- }
-
- @Test
public void testOnTaskFragmentAppeared_throughApplicationThread() throws RemoteException {
- mSetFlagsRule.enableFlags(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
// Re-register the organizer in case the flag was disabled during setup.
mController.unregisterOrganizer(mIOrganizer);
registerTaskFragmentOrganizer(mIOrganizer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
index 628c65e992fd..f07b402c31b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
@@ -16,47 +16,22 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_90;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import android.app.WindowConfiguration;
-import android.content.res.Configuration;
-import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
-import com.android.server.wm.utils.TestComponentStack;
-
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
/**
* Test class for {@link TransparentPolicy}.
@@ -71,199 +46,238 @@ public class TransparentPolicyTest extends WindowTestsBase {
@Test
public void testNotStartingWhenDisabled() {
runTestScenario((robot) -> {
- robot.launchTransparentActivityInTask();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
}, /* policyEnabled */ false);
}
@Test
public void testNotStartingWithoutTask() {
runTestScenario((robot) -> {
- robot.launchTransparentActivity();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivity();
- robot.checkTopActivityPolicyStartNotInvoked();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ ta.checkTopActivityTransparentPolicyStartNotInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
});
}
@Test
public void testPolicyRunningWhenTransparentIsUsed() {
runTestScenario((robot) -> {
- robot.launchTransparentActivityInTask();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStartNotInvoked();
- robot.checkTopActivityPolicyStateIsRunning();
+ ta.checkTopActivityTransparentPolicyStartNotInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
+ });
});
}
@Test
public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() {
runTestScenario((robot) -> {
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStartNotInvoked();
- robot.checkTopActivityPolicyStateIsRunning();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityTransparentPolicyStartNotInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
- robot.clearInteractions();
- robot.destroyTopActivity();
+ robot.clearInteractions();
+ ta.activity().destroyTopActivity();
- robot.checkTopActivityPolicyStopInvoked();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ ta.checkTopActivityTransparentPolicyStopInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
});
}
@Test
public void testApplyStrategyAgainWhenOpaqueIsDestroyed() {
runTestScenario((robot) -> {
- robot.launchOpaqueActivityInTask();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ robot.transparentActivity((ta) -> {
+ ta.launchOpaqueActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStateIsRunning();
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
- robot.destroyActivity(/* fromTop */ 1);
- robot.checkTopActivityPolicyStartInvoked();
+ ta.activity().destroyActivity(/* fromTop */ 1);
+ ta.checkTopActivityTransparentPolicyStartInvoked();
+ });
});
}
@Test
public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() {
runTestScenario((robot) -> {
- robot.launchTransparentActivityInTask();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivityInTask();
- robot.clearInteractions();
- robot.destroyActivity(/* fromTop */ 1);
+ robot.clearInteractions();
+ ta.activity().destroyActivity(/* fromTop */ 1);
- robot.checkTopActivityPolicyStartInvoked();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ ta.checkTopActivityTransparentPolicyStartInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
});
}
@Test
public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() {
runTestScenario((robot) -> {
- robot.launchOpaqueActivityInTask();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ robot.transparentActivity((ta) -> {
+ ta.launchOpaqueActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStateIsRunning();
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
- robot.clearInteractions();
- robot.checkTopActivityPolicyStopNotInvoked();
+ robot.clearInteractions();
+ ta.checkTopActivityTransparentPolicyStopNotInvoked();
+ });
});
}
@Test
public void testApplyStrategyToTranslucentActivities() {
runTestScenario((robot) -> {
- robot.configureTopActivity(/* minAspect */ 1.2f, /* maxAspect */ 1.5f,
- SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ true);
- robot.configureTopActivityIgnoreOrientationRequest(true);
- robot.launchActivity(/* minAspect */ 1.1f, /* maxAspect */ 3f,
- SCREEN_ORIENTATION_LANDSCAPE, /* transparent */true, /* addToTask */true);
- robot.checkTopActivityPolicyStateIsRunning();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
- robot.checkTopOrientation(SCREEN_ORIENTATION_PORTRAIT);
- robot.checkTopAspectRatios(/* minAspectRatio */ 1.2f, /* maxAspectRatio */ 1.5f);
+ robot.transparentActivity((ta) -> {
+ ta.applyOnActivity((a) -> {
+ a.configureTopActivity(/* minAspect */ 1.2f, /* maxAspect */ 1.5f,
+ SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ true);
+ a.configureTopActivityIgnoreOrientationRequest(true);
+ a.launchActivity(/* minAspect */ 1.1f, /* maxAspect */ 3f,
+ SCREEN_ORIENTATION_LANDSCAPE, /* transparent */true,
+ /* withComponent */ false, /* addToTask */true);
+ });
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+ ta.applyOnActivity((a) -> {
+ a.checkTopActivityConfigOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ a.checkTopActivityAspectRatios(/* minAspectRatio */ 1.2f,
+ /* maxAspectRatio */ 1.5f);
+ });
+ });
});
}
@Test
public void testApplyStrategyToTransparentActivitiesRetainsWindowConfigurationProperties() {
runTestScenario((robot) -> {
- robot.launchTransparentActivity();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivity();
- robot.forceChangeInTopActivityConfiguration();
- robot.attachTopActivityToTask();
+ ta.forceChangeInTopActivityConfiguration();
+ ta.activity().attachTopActivityToTask();
- robot.checkTopActivityConfigurationConfiguration();
+ ta.checkTopActivityConfigurationConfiguration();
+ });
});
}
@Test
public void testApplyStrategyToMultipleTranslucentActivities() {
runTestScenario((robot) -> {
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStateIsRunning();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
-
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStateIsRunning();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 2);
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 2);
+ });
});
}
@Test
public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() {
runTestScenario((robot) -> {
- robot.configureTopActivityAsEmbedded();
- robot.launchTransparentActivityInTask();
+ robot.transparentActivity((ta) -> {
+ ta.activity().setTopActivityAsEmbedded(true);
+ ta.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStartNotInvoked();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ ta.checkTopActivityTransparentPolicyStartNotInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
});
}
@Test
public void testTranslucentActivitiesDontGoInSizeCompatMode() {
runTestScenario((robot) -> {
- robot.configureTopActivityIgnoreOrientationRequest(true);
- robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
- robot.rotateDisplayForTopActivity(ROTATION_90);
- robot.checkTopActivitySizeCompatMode(/* inScm */ true);
- robot.rotateDisplayForTopActivity(ROTATION_0);
- robot.checkTopActivitySizeCompatMode(/* inScm */ false);
-
- robot.launchTransparentActivityInTask();
- robot.checkTopActivitySizeCompatMode(/* inScm */ false);
- robot.rotateDisplayForTopActivity(ROTATION_90);
- robot.checkTopActivitySizeCompatMode(/* inScm */ false);
+ robot.transparentActivity((ta) -> {
+ ta.applyOnActivity((a) -> {
+ a.configureTopActivityIgnoreOrientationRequest(true);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ a.rotateDisplayForTopActivity(ROTATION_90);
+ a.checkTopActivityInSizeCompatMode(/* inScm */ true);
+ a.rotateDisplayForTopActivity(ROTATION_0);
+ a.checkTopActivityInSizeCompatMode(/* inScm */ false);
+
+ ta.launchTransparentActivityInTask();
+
+ a.checkTopActivityInSizeCompatMode(/* inScm */ false);
+ a.rotateDisplayForTopActivity(ROTATION_90);
+ a.checkTopActivityInSizeCompatMode(/* inScm */ false);
+ });
+ });
}, /* displayWidth */ 2800, /* displayHeight */ 1400);
}
@Test
public void testCheckOpaqueIsLetterboxedWhenStrategyIsApplied() {
runTestScenario((robot) -> {
- robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
- robot.configureTopActivityIgnoreOrientationRequest(true);
- robot.launchTransparentActivity();
-
- robot.assertFalseOnTopActivity(ActivityRecord::fillsParent);
- robot.assertTrueOnActivity(/* fromTop */ 1, ActivityRecord::fillsParent);
- robot.applyTo(/* fromTop */ 1, (activity) -> {
- activity.finishing = true;
+ robot.transparentActivity((ta) -> {
+ ta.applyOnActivity((a) -> {
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ a.configureTopActivityIgnoreOrientationRequest(true);
+ ta.launchTransparentActivity();
+
+ a.assertFalseOnTopActivity(ActivityRecord::fillsParent);
+ a.assertTrueOnActivity(/* fromTop */ 1, ActivityRecord::fillsParent);
+ a.applyToActivity(/* fromTop */ 1, (activity) -> {
+ activity.finishing = true;
+ });
+ a.assertFalseOnActivity(/* fromTop */ 1, ActivityRecord::occludesParent);
+ a.attachTopActivityToTask();
+
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
});
- robot.assertFalseOnActivity(/* fromTop */ 1, ActivityRecord::occludesParent);
- robot.attachTopActivityToTask();
-
- robot.checkTopActivityPolicyStateIsNotRunning();
});
}
@Test
public void testTranslucentActivitiesWhenUnfolding() {
runTestScenario((robot) -> {
- robot.applyToTop((activity) -> {
- activity.mWmService.mLetterboxConfiguration
- .setLetterboxHorizontalPositionMultiplier(1.0f);
+ robot.transparentActivity((ta) -> {
+ ta.applyOnActivity((a) -> {
+ a.applyToTopActivity((topActivity) -> {
+ topActivity.mWmService.mLetterboxConfiguration
+ .setLetterboxHorizontalPositionMultiplier(1.0f);
+ });
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ a.configureTopActivityIgnoreOrientationRequest(true);
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+
+ a.setTaskWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ a.configureTopActivityFoldablePosture(/* isHalfFolded */ true,
+ /* isTabletop */ false);
+ a.checkTopActivityRecomputedConfiguration();
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+ robot.clearInteractions();
+ a.configureTopActivityFoldablePosture(/* isHalfFolded */ false,
+ /* isTabletop */ false);
+ a.checkTopActivityRecomputedConfiguration();
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+ });
});
- robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
- robot.configureTopActivityIgnoreOrientationRequest(true);
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
-
- robot.configureTaskWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- robot.configureTopActivityFoldablePosture(/* isHalfFolded */ true,
- /* isTabletop */ false);
- robot.checkTopActivityRecomputedConfiguration();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
- robot.clearInteractions();
-
- robot.configureTopActivityFoldablePosture(/* isHalfFolded */ false,
- /* isTabletop */ false);
- robot.checkTopActivityRecomputedConfiguration();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
}, /* displayWidth */ 2800, /* displayHeight */ 1400);
}
@@ -271,31 +285,28 @@ public class TransparentPolicyTest extends WindowTestsBase {
@Test
public void testTranslucentActivity_clearSizeCompatMode_inheritedCompatDisplayInsetsCleared() {
runTestScenario((robot) -> {
- robot.configureTopActivityIgnoreOrientationRequest(true);
- robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
- // Rotate to put activity in size compat mode.
- robot.rotateDisplayForTopActivity(ROTATION_90);
- robot.checkTopActivitySizeCompatMode(/* inScm */ true);
-
- robot.launchTransparentActivityInTask();
- robot.assertNotNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
- robot.applyToTop(ActivityRecord::clearSizeCompatMode);
- robot.assertNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
+ robot.transparentActivity((ta) -> {
+ ta.applyOnActivity((a) -> {
+ a.configureTopActivityIgnoreOrientationRequest(true);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ // Rotate to put activity in size compat mode.
+ a.rotateDisplayForTopActivity(ROTATION_90);
+ a.checkTopActivityInSizeCompatMode(/* inScm */ true);
+
+ ta.launchTransparentActivityInTask();
+ a.assertNotNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
+ a.applyToTopActivity(ActivityRecord::clearSizeCompatMode);
+ a.assertNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
+ });
+ });
});
}
private void runTestScenario(Consumer<TransparentPolicyRobotTest> consumer,
boolean policyEnabled, int displayWidth, int displayHeight) {
- spyOn(mWm.mLetterboxConfiguration);
- when(mWm.mLetterboxConfiguration.isTranslucentLetterboxingEnabled())
- .thenReturn(policyEnabled);
- final TestDisplayContent.Builder builder = new TestDisplayContent.Builder(mAtm,
- displayWidth, displayHeight);
- final Task task = new TaskBuilder(mSupervisor).setDisplay(builder.build())
- .setCreateActivity(true).build();
- final ActivityRecord opaqueActivity = task.getTopNonFinishingActivity();
- final TransparentPolicyRobotTest robot = new TransparentPolicyRobotTest(mAtm, task,
- opaqueActivity);
+ final TransparentPolicyRobotTest robot =
+ new TransparentPolicyRobotTest(mWm, mAtm, mSupervisor, displayWidth, displayHeight);
+ robot.conf().enableTranslucentPolicy(policyEnabled);
consumer.accept(robot);
}
@@ -316,289 +327,31 @@ public class TransparentPolicyTest extends WindowTestsBase {
/**
* Robot pattern implementation for TransparentPolicy
- * TODO(b/344587983): Extract Robot to be reused in different test classes.
*/
- private static class TransparentPolicyRobotTest {
+ private static class TransparentPolicyRobotTest extends AppCompatRobotBase {
@NonNull
- private final ActivityTaskManagerService mAtm;
- @NonNull
- private final Task mTask;
- @NonNull
- private final TestComponentStack<ActivityRecord> mActivityStack;
- @NonNull
- private WindowConfiguration mTopActivityWindowConfiguration;
-
- private TransparentPolicyRobotTest(@NonNull ActivityTaskManagerService atm,
- @NonNull Task task,
- @NonNull ActivityRecord opaqueActivity) {
- mAtm = atm;
- mTask = task;
- mActivityStack = new TestComponentStack<>();
- mActivityStack.push(opaqueActivity);
- spyOn(opaqueActivity.mAppCompatController.getTransparentPolicy());
- }
-
- void configureTopActivityAsEmbedded() {
- final ActivityRecord topActivity = mActivityStack.top();
- spyOn(topActivity);
- doReturn(true).when(topActivity).isEmbedded();
- }
-
- private void launchActivity(float minAspectRatio, float maxAspectRatio,
- @Configuration.Orientation int orientation, boolean transparent,
- boolean addToTask) {
- final ActivityBuilder activityBuilder = new ActivityBuilder(mAtm)
- .setScreenOrientation(orientation)
- .setLaunchedFromUid(mActivityStack.base().getUid());
- if (transparent) {
- activityBuilder.setActivityTheme(android.R.style.Theme_Translucent);
- }
- if (minAspectRatio >= 0) {
- activityBuilder.setMinAspectRatio(minAspectRatio);
- }
- if (maxAspectRatio >= 0) {
- activityBuilder.setMaxAspectRatio(maxAspectRatio);
- }
- final ActivityRecord newActivity = activityBuilder.build();
- if (addToTask) {
- mTask.addChild(newActivity);
- }
- spyOn(newActivity.mAppCompatController.getTransparentPolicy());
- mActivityStack.push(newActivity);
- }
-
- void attachTopActivityToTask() {
- mTask.addChild(mActivityStack.top());
- }
-
- void launchTransparentActivity() {
- launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
- SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true,
- /* addToTask */ false);
- }
-
- void launchTransparentActivityInTask() {
- launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
- SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true,
- /* addToTask */true);
- }
-
- void launchOpaqueActivityInTask() {
- launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
- SCREEN_ORIENTATION_PORTRAIT, /* transparent */ false,
- /* addToTask */true);
- }
-
- void destroyTopActivity() {
- mActivityStack.top().removeImmediately();
- }
-
- void destroyActivity(int fromTop) {
- mActivityStack.applyTo(/* fromTop */ fromTop, ActivityRecord::removeImmediately);
- }
-
- void forceChangeInTopActivityConfiguration() {
- mActivityStack.applyToTop((activity) -> {
- final Configuration requestedConfig = activity.getRequestedOverrideConfiguration();
- mTopActivityWindowConfiguration = requestedConfig.windowConfiguration;
- mTopActivityWindowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
- mTopActivityWindowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- mTopActivityWindowConfiguration.setAlwaysOnTop(true);
- activity.onRequestedOverrideConfigurationChanged(requestedConfig);
- });
- }
-
- void checkTopActivityPolicyStateIsRunning() {
- assertTrue(mActivityStack.top().mAppCompatController
- .getTransparentPolicy().isRunning());
- }
-
- void checkTopActivityPolicyStateIsNotRunning() {
- assertFalse(mActivityStack.top().mAppCompatController
- .getTransparentPolicy().isRunning());
- }
-
- void checkTopActivityPolicyStopInvoked() {
- verify(mActivityStack.top().mAppCompatController.getTransparentPolicy()).stop();
- }
-
- void checkTopActivityPolicyStopNotInvoked() {
- mActivityStack.applyToTop((activity) -> {
- verify(activity.mAppCompatController.getTransparentPolicy(), never()).stop();
- });
- }
-
- void checkTopActivityPolicyStartInvoked() {
- mActivityStack.applyToTop((activity) -> {
- verify(activity.mAppCompatController.getTransparentPolicy()).start();
- });
- }
-
- void checkTopActivityPolicyStartNotInvoked() {
- verify(mActivityStack.top().mAppCompatController.getTransparentPolicy(),
- never()).start();
- }
-
- void assertTrueOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
- mActivityStack.applyTo(fromTop, (activity) -> {
- Assert.assertTrue(predicate.test(activity));
- });
- }
-
- void assertFalseOnTopActivity(Predicate<ActivityRecord> predicate) {
- Assert.assertFalse(predicate.test(mActivityStack.top()));
- }
-
- void assertFalseOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
- mActivityStack.applyTo(fromTop, (activity) -> {
- Assert.assertFalse(predicate.test(activity));
- });
- }
-
- void assertNotNullOnTopActivity(Function<ActivityRecord, Object> getter) {
- Assert.assertNotNull(getter.apply(mActivityStack.top()));
- }
-
- void assertNullOnTopActivity(Function<ActivityRecord, Object> getter) {
- Assert.assertNull(getter.apply(mActivityStack.top()));
- }
-
- void checkTopActivityConfigurationConfiguration() {
- mActivityStack.applyToTop((activity) -> {
- // The original override of WindowConfiguration should keep.
- assertEquals(ACTIVITY_TYPE_STANDARD, activity.getActivityType());
- assertEquals(WINDOWING_MODE_MULTI_WINDOW,
- mTopActivityWindowConfiguration.getWindowingMode());
- assertTrue(mTopActivityWindowConfiguration.isAlwaysOnTop());
- // Unless display is going to be rotated, it should always inherit from parent.
- assertEquals(ROTATION_UNDEFINED,
- mTopActivityWindowConfiguration.getDisplayRotation());
- });
- }
-
- void checkTopActivityHasInheritedBoundsFrom(int fromTop) {
- final ActivityRecord topActivity = mActivityStack.top();
- final ActivityRecord otherActivity = mActivityStack.getFromTop(/* fromTop */ fromTop);
- final Rect opaqueBounds = otherActivity.getConfiguration().windowConfiguration
- .getBounds();
- final Rect translucentRequestedBounds = topActivity.getRequestedOverrideBounds();
- Assert.assertEquals(opaqueBounds, translucentRequestedBounds);
- }
-
- void checkTopActivityRecomputedConfiguration() {
- verify(mActivityStack.top()).recomputeConfiguration();
- }
-
- void checkTopOrientation(int orientation) {
- Assert.assertEquals(orientation, mActivityStack.top()
- .getRequestedConfigurationOrientation());
- }
+ private final AppCompatTransparentActivityRobot mTransparentActivityRobot;
- void configureTaskWindowingMode(int windowingMode) {
- mTask.setWindowingMode(windowingMode);
+ private TransparentPolicyRobotTest(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor,
+ int displayWidth, int displayHeight) {
+ super(wm, atm, supervisor, displayWidth, displayHeight);
+ mTransparentActivityRobot = new AppCompatTransparentActivityRobot(activity());
+ // We always create at least an opaque activity in a Task
+ activity().createNewTaskWithBaseActivity();
}
- void checkTopAspectRatios(float minAspectRatio, float maxAspectRatio) {
- final ActivityRecord topActivity = mActivityStack.top();
- Assert.assertEquals(minAspectRatio, topActivity.getMinAspectRatio(), 0.0001);
- Assert.assertEquals(maxAspectRatio, topActivity.getMaxAspectRatio(), 0.0001);
- }
-
- void checkTopActivitySizeCompatMode(boolean inScm) {
- Assert.assertEquals(inScm, mActivityStack.top().inSizeCompatMode());
+ void transparentActivity(@NonNull Consumer<AppCompatTransparentActivityRobot> consumer) {
+ consumer.accept(mTransparentActivityRobot);
}
void clearInteractions() {
- mActivityStack.applyToAll((activity) -> {
+ activity().applyToAllActivities((activity) -> {
clearInvocations(activity);
clearInvocations(activity.mAppCompatController.getTransparentPolicy());
});
}
-
- void configureTopActivity(float minAspect, float maxAspect, int screenOrientation,
- boolean isUnresizable) {
- prepareLimitedBounds(mActivityStack.top(), minAspect, maxAspect, screenOrientation,
- isUnresizable);
- }
-
- void configureTopActivityIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
- mActivityStack.top().mDisplayContent
- .setIgnoreOrientationRequest(ignoreOrientationRequest);
- }
-
- void configureUnresizableTopActivity(int screenOrientation) {
- configureTopActivity(-1, -1, screenOrientation, true);
- }
-
- void applyToTop(Consumer<ActivityRecord> consumer) {
- consumer.accept(mActivityStack.top());
- }
-
- void applyTo(int fromTop, Consumer<ActivityRecord> consumer) {
- mActivityStack.applyTo(fromTop, consumer);
- }
-
- void rotateDisplayForTopActivity(int rotation) {
- rotateDisplay(mActivityStack.top().mDisplayContent, rotation);
- }
-
- /**
- * Setups activity with restriction on its bounds, such as maxAspect, minAspect,
- * fixed orientation, and/or whether it is resizable.
- */
- void prepareLimitedBounds(ActivityRecord activity, float minAspect, float maxAspect,
- int screenOrientation, boolean isUnresizable) {
- activity.info.resizeMode = isUnresizable
- ? RESIZE_MODE_UNRESIZEABLE
- : RESIZE_MODE_RESIZEABLE;
- final Task task = activity.getTask();
- if (task != null) {
- // Update the Task resize value as activity will follow the task.
- task.mResizeMode = activity.info.resizeMode;
- task.getRootActivity().info.resizeMode = activity.info.resizeMode;
- }
- activity.setVisibleRequested(true);
- if (maxAspect >= 0) {
- activity.info.setMaxAspectRatio(maxAspect);
- }
- if (minAspect >= 0) {
- activity.info.setMinAspectRatio(minAspect);
- }
- if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
- activity.info.screenOrientation = screenOrientation;
- activity.setRequestedOrientation(screenOrientation);
- }
- // Make sure to use the provided configuration to construct the size compat fields.
- activity.clearSizeCompatMode();
- activity.ensureActivityConfiguration();
- // Make sure the display configuration reflects the change of activity.
- if (activity.mDisplayContent.updateOrientation()) {
- activity.mDisplayContent.sendNewConfiguration();
- }
- }
-
- void configureTopActivityFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
- mActivityStack.applyToTop((activity) -> {
- final DisplayRotation r = activity.mDisplayContent.getDisplayRotation();
- doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
- doReturn(false).when(r)
- .isDeviceInPosture(any(DeviceStateController.DeviceState.class),
- anyBoolean());
- if (isHalfFolded) {
- doReturn(true).when(r)
- .isDeviceInPosture(DeviceStateController.DeviceState.HALF_FOLDED,
- isTabletop);
- }
- activity.recomputeConfiguration();
- });
- }
-
- private static void rotateDisplay(DisplayContent display, int rotation) {
- final Configuration c = new Configuration();
- display.getDisplayRotation().setRotation(rotation);
- display.computeScreenConfiguration(c);
- display.onRequestedOverrideConfigurationChanged(c);
- }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 059fed20c38d..e6648dad4bbe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -340,54 +340,33 @@ public class WindowProcessControllerTests extends WindowTestsBase {
public void testComputeOomAdjFromActivities() {
final ActivityRecord activity = createActivityRecord(mWpc);
activity.setVisibleRequested(true);
- final int[] callbackResult = { 0 };
- final int visible = 1;
- final int paused = 2;
- final int stopping = 4;
- final int other = 8;
- final WindowProcessController.ComputeOomAdjCallback callback =
- new WindowProcessController.ComputeOomAdjCallback() {
- @Override
- public void onVisibleActivity() {
- callbackResult[0] |= visible;
- }
-
- @Override
- public void onPausedActivity() {
- callbackResult[0] |= paused;
- }
-
- @Override
- public void onStoppingActivity(boolean finishing) {
- callbackResult[0] |= stopping;
- }
-
- @Override
- public void onOtherActivity() {
- callbackResult[0] |= other;
- }
- };
// onStartActivity should refresh the state immediately.
mWpc.onStartActivity(0 /* topProcessState */, activity.info);
- assertEquals(1 /* minTaskLayer */, mWpc.computeOomAdjFromActivities(callback));
- assertEquals(visible, callbackResult[0]);
+ int flags = mWpc.getActivityStateFlags();
+ assertEquals(1 /* minTaskLayer */,
+ flags & WindowProcessController.ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER);
+ final int visibleFlags = WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+ | WindowProcessController.ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE
+ | WindowProcessController.ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK;
+ assertEquals(visibleFlags,
+ flags & ~WindowProcessController.ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER);
- callbackResult[0] = 0;
activity.setVisibleRequested(false);
activity.setState(PAUSED, "test");
- mWpc.computeOomAdjFromActivities(callback);
- assertEquals(paused, callbackResult[0]);
+ final int exclusiveFlags = WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+ | WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED
+ | WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING;
+ flags = mWpc.getActivityStateFlags() & exclusiveFlags;
+ assertEquals(WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED, flags);
- callbackResult[0] = 0;
activity.setState(STOPPING, "test");
- mWpc.computeOomAdjFromActivities(callback);
- assertEquals(stopping, callbackResult[0]);
+ flags = mWpc.getActivityStateFlags() & exclusiveFlags;
+ assertEquals(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING, flags);
- callbackResult[0] = 0;
activity.setState(STOPPED, "test");
- mWpc.computeOomAdjFromActivities(callback);
- assertEquals(other, callbackResult[0]);
+ flags = mWpc.getActivityStateFlags() & exclusiveFlags;
+ assertEquals(0, flags);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CurrentTimeMillisSupplierFake.java b/services/tests/wmtests/src/com/android/server/wm/utils/CurrentTimeMillisSupplierFake.java
new file mode 100644
index 000000000000..f4b2adb23e15
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/CurrentTimeMillisSupplierFake.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import java.util.function.LongSupplier;
+
+/**
+ * Fake {@link LongSupplier} implementation used in tests.
+ */
+public class CurrentTimeMillisSupplierFake implements LongSupplier {
+
+ private long mCurrenTimeMillis = System.currentTimeMillis();
+
+ /**
+ * @return The currentTimeMilles used in test
+ */
+ @Override
+ public long getAsLong() {
+ return mCurrenTimeMillis;
+ }
+
+ /**
+ * Simulate some time passed.
+ * @param delayInMillis The delay in milliseconds,
+ */
+ public void delay(long delayInMillis) {
+ mCurrenTimeMillis += delayInMillis;
+ }
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 1d014201cf46..7e7a53148603 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1055,15 +1055,22 @@ public class VoiceInteractionManagerService extends SystemService {
if (sessionArgs != null && sessionArgs.containsKey(csKey)) {
if (sessionArgs.getBoolean(csEnabledKey, true)) {
// If Contextual Search is enabled, try to follow that path.
- Intent launchIntent = getContextualSearchIntent(sessionArgs);
+ Intent launchIntent;
+ final long getSearchIntentCaller = Binder.clearCallingIdentity();
+ try {
+ launchIntent = getContextualSearchIntent(sessionArgs);
+ } finally {
+ Binder.restoreCallingIdentity(getSearchIntentCaller);
+ }
if (launchIntent != null) {
// Hand over to contextual search helper.
Slog.d(TAG, "Handed over to contextual search helper.");
- final long caller = Binder.clearCallingIdentity();
+ final int userId = Binder.getCallingUserHandle().getIdentifier();
+ final long startSearchCaller = Binder.clearCallingIdentity();
try {
- return startContextualSearch(launchIntent);
+ return startContextualSearch(launchIntent, userId);
} finally {
- Binder.restoreCallingIdentity(caller);
+ Binder.restoreCallingIdentity(startSearchCaller);
}
}
}
@@ -2761,7 +2768,7 @@ public class VoiceInteractionManagerService extends SystemService {
}
@RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS)
- private boolean startContextualSearch(Intent launchIntent) {
+ private boolean startContextualSearch(Intent launchIntent, final int userId) {
// Contextual search starts with a frozen screen - so we launch without
// any system animations or starting window.
final ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(mContext,
@@ -2769,7 +2776,7 @@ public class VoiceInteractionManagerService extends SystemService {
opts.setDisableStartingWindow(true);
int resultCode = mAtmInternal.startActivityWithScreenshot(launchIntent,
mContext.getPackageName(), Binder.getCallingUid(), Binder.getCallingPid(), null,
- opts.toBundle(), Binder.getCallingUserHandle().getIdentifier());
+ opts.toBundle(), userId);
return resultCode == ActivityManager.START_SUCCESS;
}
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index e29d321e5105..59766579eee2 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -51,10 +51,8 @@ java_sdk_library {
java_library {
name: "android.test.mock.ravenwood",
+ defaults: ["ravenwood-internal-only-visibility-java"],
srcs: [":android-test-mock-sources"],
- visibility: [
- "//frameworks/base",
- ],
}
android_ravenwood_test {
diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
index 63782f1bf955..1842f0a64a83 100644
--- a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
+++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+// InputMonitor is deprecated, but we still need to test it.
+@file:Suppress("DEPRECATION")
+
package com.android.test.input
import android.app.Activity
@@ -43,6 +46,7 @@ class UnresponsiveGestureMonitorActivity : Activity() {
}
private lateinit var mInputEventReceiver: InputEventReceiver
private lateinit var mInputMonitor: InputMonitor
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val inputManager = checkNotNull(getSystemService(InputManager::class.java))