summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp13
-rw-r--r--MEMORY_OWNERS1
-rw-r--r--core/java/android/app/ActivityManagerInternal.java6
-rw-r--r--core/java/android/app/AppCompatTaskInfo.java29
-rw-r--r--core/java/android/app/NotificationManager.java10
-rw-r--r--core/java/android/app/SystemServiceRegistry.java9
-rw-r--r--core/java/android/content/pm/flags.aconfig2
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java9
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java7
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java18
-rw-r--r--core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java24
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java140
-rw-r--r--core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java105
-rw-r--r--core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java13
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java13
-rw-r--r--core/java/android/hardware/camera2/params/SharedSessionConfiguration.java2
-rw-r--r--core/java/android/hardware/contexthub/HubEndpoint.java10
-rw-r--r--core/java/android/hardware/display/DisplayTopology.java33
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig7
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletService.java8
-rw-r--r--core/java/android/tracing/flags.aconfig8
-rw-r--r--core/java/android/view/KeyCharacterMap.java13
-rw-r--r--core/java/android/view/contentcapture/flags/content_capture_flags.aconfig2
-rw-r--r--core/java/android/window/WindowTokenClientController.java3
-rw-r--r--core/java/android/window/flags/large_screen_experiences_app_compat.aconfig17
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig7
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_base.xml40
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_media.xml7
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_messaging.xml6
-rw-r--r--core/res/res/layout/notification_2025_template_header.xml31
-rw-r--r--core/res/res/values/config.xml11
-rw-r--r--core/res/res/values/config_watch.xml5
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt86
-rw-r--r--libs/WindowManager/Shell/multivalentTests/Android.bp2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt72
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt33
-rw-r--r--libs/androidfw/Android.bp1
-rw-r--r--libs/androidfw/TypeWrappers.cpp59
-rw-r--r--libs/androidfw/include/androidfw/TypeWrappers.h30
-rw-r--r--libs/androidfw/tests/TypeWrappers_test.cpp127
-rw-r--r--libs/protoutil/Android.bp1
-rw-r--r--location/api/system-current.txt7
-rw-r--r--location/java/android/location/flags/location.aconfig2
-rw-r--r--location/java/android/location/provider/GnssAssistanceProviderBase.java152
-rw-r--r--location/java/android/location/provider/IGnssAssistanceCallback.aidl28
-rw-r--r--location/java/android/location/provider/IGnssAssistanceProvider.aidl28
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java66
-rw-r--r--packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java47
-rw-r--r--packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java47
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java23
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java27
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java22
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java37
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java61
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java28
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java41
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java4
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java58
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java175
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig14
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java57
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt17
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt63
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt116
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt122
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt34
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt86
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt56
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt31
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt46
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt46
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java104
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt146
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt17
-rw-r--r--packages/SystemUI/res/drawable/audio_bars_idle.xml56
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml2
-rw-r--r--packages/SystemUI/res/raw/audio_bars_in.json1
-rw-r--r--packages/SystemUI/res/raw/audio_bars_out.json1
-rw-r--r--packages/SystemUI/res/raw/audio_bars_playing.json1
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java67
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt225
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt96
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java141
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt127
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt197
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java16
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java36
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java16
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java13
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java6
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java2
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayTopologyCoordinator.java17
-rw-r--r--services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java97
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2Provider.java25
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java21
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java40
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java15
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java110
-rw-r--r--services/core/java/com/android/server/power/Notifier.java28
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java4
-rw-r--r--services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java16
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java2
-rw-r--r--services/java/com/android/server/SystemServer.java11
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt41
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java25
-rw-r--r--tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt41
-rw-r--r--tools/aapt2/Debug.cpp24
252 files changed, 4579 insertions, 1573 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 2f843f9d6164..8547ec164084 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -1809,12 +1809,25 @@ aconfig_declarations {
name: "aconfig_settingslib_flags",
package: "com.android.settingslib.flags",
container: "system",
+ exportable: true,
srcs: [
"packages/SettingsLib/aconfig/settingslib.aconfig",
],
}
java_aconfig_library {
+ name: "aconfig_settingslib_exported_flags_java_lib",
+ aconfig_declarations: "aconfig_settingslib_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ mode: "exported",
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
+}
+
+java_aconfig_library {
name: "aconfig_settingslib_flags_java_lib",
aconfig_declarations: "aconfig_settingslib_flags",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
diff --git a/MEMORY_OWNERS b/MEMORY_OWNERS
index 89ce5140d8ea..12aa2951bbc9 100644
--- a/MEMORY_OWNERS
+++ b/MEMORY_OWNERS
@@ -2,5 +2,4 @@ surenb@google.com
tjmercier@google.com
kaleshsingh@google.com
jyescas@google.com
-carlosgalo@google.com
jji@google.com
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index abdfb53537f8..999db18a1229 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -485,6 +485,11 @@ public abstract class ActivityManagerInternal {
*/
public static final int OOM_ADJ_REASON_FOLLOW_UP = 23;
+ /**
+ * Oom Adj Reason: Update after oom adjuster configuration has changed.
+ */
+ public static final int OOM_ADJ_REASON_RECONFIGURATION = 24;
+
@IntDef(prefix = {"OOM_ADJ_REASON_"}, value = {
OOM_ADJ_REASON_NONE,
OOM_ADJ_REASON_ACTIVITY,
@@ -510,6 +515,7 @@ public abstract class ActivityManagerInternal {
OOM_ADJ_REASON_RESTRICTION_CHANGE,
OOM_ADJ_REASON_COMPONENT_DISABLED,
OOM_ADJ_REASON_FOLLOW_UP,
+ OOM_ADJ_REASON_RECONFIGURATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OomAdjReason {}
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 61b56877589b..599f1a8be233 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -27,6 +27,7 @@ import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Stores App Compat information about a particular Task.
@@ -58,16 +59,11 @@ public class AppCompatTaskInfo implements Parcelable {
public int topActivityLetterboxHeight = PROPERTY_VALUE_UNSET;
/**
- * Contains the current app height of the letterboxed activity if available or
- * {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
+ * Contains the app bounds of the top activity or size compat mode
+ * bounds when in size compat mode. If null, contains bounds.
*/
- public int topActivityLetterboxAppHeight = PROPERTY_VALUE_UNSET;
-
- /**
- * Contains the current app width of the letterboxed activity if available or
- * {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
- */
- public int topActivityLetterboxAppWidth = PROPERTY_VALUE_UNSET;
+ @NonNull
+ public final Rect topActivityAppBounds = new Rect();
/**
* Contains the top activity bounds when the activity is letterboxed.
@@ -350,8 +346,7 @@ public class AppCompatTaskInfo implements Parcelable {
&& topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
- && topActivityLetterboxAppWidth == that.topActivityLetterboxAppWidth
- && topActivityLetterboxAppHeight == that.topActivityLetterboxAppHeight
+ && topActivityAppBounds.equals(that.topActivityAppBounds)
&& topActivityLetterboxHorizontalPosition
== that.topActivityLetterboxHorizontalPosition
&& cameraCompatTaskInfo.equalsForTaskOrganizer(that.cameraCompatTaskInfo);
@@ -371,8 +366,7 @@ public class AppCompatTaskInfo implements Parcelable {
== that.topActivityLetterboxHorizontalPosition
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
- && topActivityLetterboxAppWidth == that.topActivityLetterboxAppWidth
- && topActivityLetterboxAppHeight == that.topActivityLetterboxAppHeight
+ && topActivityAppBounds.equals(that.topActivityAppBounds)
&& cameraCompatTaskInfo.equalsForCompatUi(that.cameraCompatTaskInfo);
}
@@ -385,8 +379,7 @@ public class AppCompatTaskInfo implements Parcelable {
topActivityLetterboxHorizontalPosition = source.readInt();
topActivityLetterboxWidth = source.readInt();
topActivityLetterboxHeight = source.readInt();
- topActivityLetterboxAppWidth = source.readInt();
- topActivityLetterboxAppHeight = source.readInt();
+ topActivityAppBounds.set(Objects.requireNonNull(source.readTypedObject(Rect.CREATOR)));
topActivityLetterboxBounds = source.readTypedObject(Rect.CREATOR);
cameraCompatTaskInfo = source.readTypedObject(CameraCompatTaskInfo.CREATOR);
}
@@ -401,8 +394,7 @@ public class AppCompatTaskInfo implements Parcelable {
dest.writeInt(topActivityLetterboxHorizontalPosition);
dest.writeInt(topActivityLetterboxWidth);
dest.writeInt(topActivityLetterboxHeight);
- dest.writeInt(topActivityLetterboxAppWidth);
- dest.writeInt(topActivityLetterboxAppHeight);
+ dest.writeTypedObject(topActivityAppBounds, flags);
dest.writeTypedObject(topActivityLetterboxBounds, flags);
dest.writeTypedObject(cameraCompatTaskInfo, flags);
}
@@ -421,8 +413,7 @@ public class AppCompatTaskInfo implements Parcelable {
+ topActivityLetterboxHorizontalPosition
+ " topActivityLetterboxWidth=" + topActivityLetterboxWidth
+ " topActivityLetterboxHeight=" + topActivityLetterboxHeight
- + " topActivityLetterboxAppWidth=" + topActivityLetterboxAppWidth
- + " topActivityLetterboxAppHeight=" + topActivityLetterboxAppHeight
+ + " topActivityAppBounds=" + topActivityAppBounds
+ " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled()
+ " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled()
+ " hasMinAspectRatioOverride=" + hasMinAspectRatioOverride()
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 8ed66eb7e6c0..e9b889a2f1aa 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -55,6 +55,7 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -677,9 +678,14 @@ public class NotificationManager {
}
/** {@hide} */
- @UnsupportedAppUsage
- public NotificationManager(Context context, InstantSource clock)
+ public NotificationManager(Context context)
{
+ this(context, SystemClock.elapsedRealtimeClock());
+ }
+
+ /** {@hide} */
+ @UnsupportedAppUsage
+ public NotificationManager(Context context, InstantSource clock) {
mContext = context;
mClock = clock;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 920b19cd8f78..0bbe9434293a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -17,7 +17,7 @@
package android.app;
import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
-import static android.provider.flags.Flags.stageFlagsForBuild;
+import static android.provider.flags.Flags.newStoragePublicApi;
import static android.server.Flags.removeGameManagerServiceFromWear;
import android.accounts.AccountManager;
@@ -289,7 +289,6 @@ import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.policy.PhoneLayoutInflater;
import com.android.internal.util.Preconditions;
-import java.time.InstantSource;
import java.util.Map;
import java.util.Objects;
@@ -625,8 +624,8 @@ public final class SystemServiceRegistry {
com.android.internal.R.style.Theme_Dialog,
com.android.internal.R.style.Theme_Holo_Dialog,
com.android.internal.R.style.Theme_DeviceDefault_Dialog,
- com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)),
- InstantSource.system());
+ com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog))
+ );
}});
registerService(Context.PEOPLE_SERVICE, PeopleManager.class,
@@ -1841,7 +1840,7 @@ public final class SystemServiceRegistry {
VirtualizationFrameworkInitializer.registerServiceWrappers();
ConnectivityFrameworkInitializerBaklava.registerServiceWrappers();
- if (stageFlagsForBuild()) {
+ if (newStoragePublicApi()) {
ConfigInfrastructureFrameworkInitializer.registerServiceWrappers();
}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 0d219a901b9d..4c4753872c03 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -152,7 +152,7 @@ flag {
name: "cache_sdk_system_features"
namespace: "system_performance"
description: "Feature flag to enable optimized cache for SDK-defined system feature lookups."
- bug: "375000483"
+ bug: "326623529"
}
flag {
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 852f04793f15..9c6b71b72ec8 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -417,6 +417,7 @@ public abstract class CameraDevice implements AutoCloseable {
* or if any of the output configurations sets a stream use
* case different from {@link
* android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT}.
+ * @throws UnsupportedOperationException if the camera has been opened in shared mode
* @see CameraExtensionCharacteristics#getSupportedExtensions
* @see CameraExtensionCharacteristics#getExtensionSupportedSizes
*/
@@ -1258,7 +1259,8 @@ public abstract class CameraDevice implements AutoCloseable {
* configurations are empty; or the session configuration
* executor is invalid;
* or the output dynamic range combination is
- * invalid/unsupported.
+ * invalid/unsupported; or the session type is not shared when
+ * camera has been opened in shared mode.
* @throws CameraAccessException In case the camera device is no longer connected or has
* encountered a fatal error.
* @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
@@ -1292,6 +1294,8 @@ public abstract class CameraDevice implements AutoCloseable {
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
+ * @throws UnsupportedOperationException if this is not a primary client of a camera opened in
+ * shared mode
*/
@NonNull
public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType)
@@ -1328,6 +1332,8 @@ public abstract class CameraDevice implements AutoCloseable {
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
+ * @throws UnsupportedOperationException if this is not a primary client of a camera opened in
+ * shared mode
*
* @see #TEMPLATE_PREVIEW
* @see #TEMPLATE_RECORD
@@ -1369,6 +1375,7 @@ public abstract class CameraDevice implements AutoCloseable {
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
+ * @throws UnsupportedOperationException if the camera has been opened in shared mode
*
* @see CaptureRequest.Builder
* @see TotalCaptureResult
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index aba2345f28d8..bfaff941939c 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1375,6 +1375,9 @@ public final class CameraManager {
* @throws SecurityException if the application does not have permission to
* access the camera
*
+ * @throws UnsupportedOperationException if {@link #isCameraDeviceSharingSupported} returns
+ * false for the given {@code cameraId}.
+ *
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*
@@ -1393,6 +1396,10 @@ public final class CameraManager {
if (executor == null) {
throw new IllegalArgumentException("executor was null");
}
+ if (!isCameraDeviceSharingSupported(cameraId)) {
+ throw new UnsupportedOperationException(
+ "CameraDevice sharing is not supported for Camera ID: " + cameraId);
+ }
openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0,
getRotationOverride(mContext), /*sharedMode*/true);
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 496d316eb028..1c65b0882e0f 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -299,6 +299,24 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
return mRequestType;
}
+ /**
+ * Get the stream ids corresponding to the target surfaces.
+ *
+ * @hide
+ */
+ public int[] getStreamIds() {
+ return mStreamIdxArray;
+ };
+
+ /**
+ * Get the surface ids corresponding to the target surfaces.
+ *
+ * @hide
+ */
+ public int[] getSurfaceIds() {
+ return mSurfaceIdxArray;
+ };
+
// If this request is part of constrained high speed request list that was created by
// {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
private boolean mIsPartOfCHSRequestList = false;
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index ce8661e90978..7e0456b22be8 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -340,6 +340,30 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
}
}
+ /**
+ * Shared Camera capture session API which can be used by the clients
+ * to start streaming.
+ *
+ * @hide
+ */
+ public int startStreaming(List<Surface> surfaces, Executor executor,
+ CaptureCallback callback) throws CameraAccessException {
+
+ synchronized (mDeviceImpl.mInterfaceLock) {
+ checkNotClosed();
+
+ executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+ if (DEBUG) {
+ Log.v(TAG, mIdString + "startStreaming callback " + callback + " executor"
+ + " " + executor);
+ }
+
+ return addPendingSequence(mDeviceImpl.startStreaming(surfaces,
+ createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+ }
+ }
+
private void checkRepeatingRequest(CaptureRequest request) {
if (request == null) {
throw new IllegalArgumentException("request must not be null");
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 321f09bd4760..89a6b02b56c4 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -451,6 +451,16 @@ public class CameraDeviceImpl extends CameraDevice
}
}
+ /**
+ * When camera device is opened in shared mode, call to check if this is a primary client.
+ *
+ */
+ public boolean isPrimaryClient() {
+ synchronized (mInterfaceLock) {
+ return mIsPrimaryClient;
+ }
+ }
+
private Map<String, CameraCharacteristics> getPhysicalIdToChars() {
if (mPhysicalIdsToChars == null) {
try {
@@ -858,24 +868,19 @@ public class CameraDeviceImpl extends CameraDevice
List<SharedOutputConfiguration> sharedConfigs =
sharedSessionConfiguration.getOutputStreamsInformation();
for (SharedOutputConfiguration sharedConfig : sharedConfigs) {
- if (outConfig.getConfiguredSize().equals(sharedConfig.getSize())
- && (outConfig.getConfiguredFormat() == sharedConfig.getFormat())
- && (outConfig.getSurfaceGroupId() == OutputConfiguration.SURFACE_GROUP_ID_NONE)
- && (outConfig.getSurfaceType() == sharedConfig.getSurfaceType())
+ if ((outConfig.getSurfaceGroupId() == OutputConfiguration.SURFACE_GROUP_ID_NONE)
&& (outConfig.getMirrorMode() == sharedConfig.getMirrorMode())
- && (outConfig.getUsage() == sharedConfig.getUsage())
&& (outConfig.isReadoutTimestampEnabled()
== sharedConfig.isReadoutTimestampEnabled())
&& (outConfig.getTimestampBase() == sharedConfig.getTimestampBase())
&& (outConfig.getStreamUseCase() == sharedConfig.getStreamUseCase())
- && (outConfig.getColorSpace().equals(
- sharedSessionConfiguration.getColorSpace()))
&& (outConfig.getDynamicRangeProfile()
== DynamicRangeProfiles.STANDARD)
- && (outConfig.getConfiguredDataspace() == sharedConfig.getDataspace())
&& (Objects.equals(outConfig.getPhysicalCameraId(),
sharedConfig.getPhysicalCameraId()))
&& (outConfig.getSensorPixelModes().isEmpty())
+ && (!outConfig.isMultiResolution())
+ && (!outConfig.isDeferredConfiguration())
&& (!outConfig.isShared())) {
//Found valid config, return true
return true;
@@ -907,14 +912,6 @@ public class CameraDeviceImpl extends CameraDevice
if (config.getExecutor() == null) {
throw new IllegalArgumentException("Invalid executor");
}
- if (mSharedMode) {
- if (config.getSessionType() != SessionConfiguration.SESSION_SHARED) {
- throw new IllegalArgumentException("Invalid session type");
- }
- if (!checkSharedSessionConfiguration(outputConfigs)) {
- throw new IllegalArgumentException("Invalid output configurations");
- }
- }
createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
config.getStateCallback(), config.getExecutor(), config.getSessionType(),
config.getSessionParameters());
@@ -932,17 +929,26 @@ public class CameraDeviceImpl extends CameraDevice
checkIfCameraClosedOrInError();
+ boolean isSharedSession = (operatingMode == ICameraDeviceUser.SHARED_MODE);
+ if (Flags.cameraMultiClient() && mSharedMode) {
+ if (!isSharedSession) {
+ throw new IllegalArgumentException("Invalid session type");
+ }
+ if (!checkSharedSessionConfiguration(outputConfigurations)) {
+ throw new IllegalArgumentException("Invalid output configurations");
+ }
+ if (inputConfig != null) {
+ throw new IllegalArgumentException("Shared capture session doesn't support"
+ + " input configuration yet.");
+ }
+ }
+
boolean isConstrainedHighSpeed =
(operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
if (isConstrainedHighSpeed && inputConfig != null) {
throw new IllegalArgumentException("Constrained high speed session doesn't support"
+ " input configuration yet.");
}
- boolean isSharedSession = (operatingMode == ICameraDeviceUser.SHARED_MODE);
- if (isSharedSession && inputConfig != null) {
- throw new IllegalArgumentException("Shared capture session doesn't support"
- + " input configuration yet.");
- }
if (mCurrentExtensionSession != null) {
mCurrentExtensionSession.commitStats();
@@ -1004,8 +1010,7 @@ public class CameraDeviceImpl extends CameraDevice
mCharacteristics);
} else if (isSharedSession) {
newSession = new CameraSharedCaptureSessionImpl(mNextSessionId++,
- callback, executor, this, mDeviceExecutor, configureSuccess,
- mIsPrimaryClient);
+ callback, executor, this, mDeviceExecutor, configureSuccess);
} else {
newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
callback, executor, this, mDeviceExecutor, configureSuccess);
@@ -1074,6 +1079,11 @@ public class CameraDeviceImpl extends CameraDevice
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
+ if (Flags.cameraMultiClient() && mSharedMode && !mIsPrimaryClient) {
+ throw new UnsupportedOperationException("In shared session mode,"
+ + "only primary clients can create capture request.");
+ }
+
for (String physicalId : physicalCameraIdSet) {
if (Objects.equals(physicalId, getId())) {
throw new IllegalStateException("Physical id matches the logical id!");
@@ -1100,6 +1110,11 @@ public class CameraDeviceImpl extends CameraDevice
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
+ if (Flags.cameraMultiClient() && mSharedMode && !mIsPrimaryClient) {
+ throw new UnsupportedOperationException("In shared session mode,"
+ + "only primary clients can create capture request.");
+ }
+
CameraMetadataNative templatedRequest = null;
templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
@@ -1119,6 +1134,10 @@ public class CameraDeviceImpl extends CameraDevice
throws CameraAccessException {
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
+ if (Flags.cameraMultiClient() && mSharedMode) {
+ throw new UnsupportedOperationException("In shared session mode,"
+ + "reprocess capture requests are not supported.");
+ }
CameraMetadataNative resultMetadata = new
CameraMetadataNative(inputResult.getNativeCopy());
@@ -1572,6 +1591,74 @@ public class CameraDeviceImpl extends CameraDevice
}
}
+ public int startStreaming(List<Surface> surfaces, CaptureCallback callback,
+ Executor executor) throws CameraAccessException {
+ // Need a valid executor, or current thread needs to have a looper, if
+ // callback is valid
+ executor = checkExecutor(executor, callback);
+ synchronized (mInterfaceLock) {
+ checkIfCameraClosedOrInError();
+ for (Surface surface : surfaces) {
+ if (surface == null) {
+ throw new IllegalArgumentException("Null Surface targets are not allowed");
+ }
+ }
+ // In shared session mode, if there are other active clients streaming then
+ // stoprepeating does not actually send request to HAL to cancel the request.
+ // Cameraservice will use this call to remove this client surfaces provided in its
+ // previous streaming request. If this is the only client for the shared camera device
+ // then camerservice will ask HAL to cancel the previous repeating request
+ stopRepeating();
+
+ // StartStreaming API does not allow capture parameters to be provided through a capture
+ // request. If the primary client has an existing repeating request, the camera service
+ // will either attach the provided surfaces to that request or create a default capture
+ // request if no repeating request is active. A default capture request is created here
+ // for initial use. The capture callback will provide capture results that include the
+ // actual capture parameters used for the streaming.
+ CaptureRequest.Builder builder = createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ for (Surface surface : surfaces) {
+ builder.addTarget(surface);
+ }
+ CaptureRequest request = builder.build();
+ request.convertSurfaceToStreamId(mConfiguredOutputs);
+
+ SubmitInfo requestInfo;
+ requestInfo = mRemoteDevice.startStreaming(request.getStreamIds(),
+ request.getSurfaceIds());
+ request.recoverStreamIdToSurface();
+ List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
+ requestList.add(request);
+
+ if (callback != null) {
+ mCaptureCallbackMap.put(requestInfo.getRequestId(),
+ new CaptureCallbackHolder(
+ callback, requestList, executor, true, mNextSessionId - 1));
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
+ }
+ }
+
+ if (mRepeatingRequestId != REQUEST_ID_NONE) {
+ checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId,
+ requestInfo.getLastFrameNumber(), mRepeatingRequestTypes);
+ }
+
+ CaptureRequest[] requestArray = requestList.toArray(
+ new CaptureRequest[requestList.size()]);
+ mRepeatingRequestId = requestInfo.getRequestId();
+ mRepeatingRequestTypes = getRequestTypes(requestArray);
+
+ if (mIdle) {
+ mDeviceExecutor.execute(mCallOnActive);
+ }
+ mIdle = false;
+
+ return requestInfo.getRequestId();
+ }
+ }
+
public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
Executor executor) throws CameraAccessException {
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
@@ -2894,6 +2981,11 @@ public class CameraDeviceImpl extends CameraDevice
@Override
public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
throws CameraAccessException {
+ if (Flags.cameraMultiClient() && mSharedMode) {
+ throw new UnsupportedOperationException("In shared session mode,"
+ + "extension sessions are not supported.");
+ }
+
HashMap<String, CameraCharacteristics> characteristicsMap = new HashMap<>(
getPhysicalIdToChars());
characteristicsMap.put(mCameraId, mCharacteristics);
@@ -2929,4 +3021,4 @@ public class CameraDeviceImpl extends CameraDevice
}
}
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
index a1f31c0ced5e..8c0dcfb2a28c 100644
--- a/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java
@@ -19,6 +19,8 @@ import android.annotation.FlaggedApi;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraOfflineSession;
+import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
import android.hardware.camera2.CameraSharedCaptureSession;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.OutputConfiguration;
@@ -28,6 +30,7 @@ import android.view.Surface;
import com.android.internal.camera.flags.Flags;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
@@ -46,7 +49,8 @@ public class CameraSharedCaptureSessionImpl
private static final String TAG = "CameraSharedCaptureSessionImpl";
private final CameraCaptureSessionImpl mSessionImpl;
private final ConditionVariable mInitialized = new ConditionVariable();
- private boolean mIsPrimary;
+ private final android.hardware.camera2.impl.CameraDeviceImpl mCameraDevice;
+ private final Executor mDeviceExecutor;
/**
* Create a new CameraCaptureSession.
@@ -54,24 +58,32 @@ public class CameraSharedCaptureSessionImpl
CameraSharedCaptureSessionImpl(int id,
CameraCaptureSession.StateCallback callback, Executor stateExecutor,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
- Executor deviceStateExecutor, boolean configureSuccess, boolean isPrimary) {
+ Executor deviceStateExecutor, boolean configureSuccess) {
CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess);
- mIsPrimary = isPrimary;
+ mCameraDevice = deviceImpl;
+ mDeviceExecutor = deviceStateExecutor;
mInitialized.open();
}
@Override
- public int startStreaming(List<Surface> surfaces, Executor executor, CaptureCallback listener)
+ public int startStreaming(List<Surface> surfaces, Executor executor, CaptureCallback callback)
throws CameraAccessException {
- // Todo: Need to add implementation.
- return 0;
+ if (surfaces.isEmpty()) {
+ throw new IllegalArgumentException("No surfaces provided for streaming");
+ } else if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ } else if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null");
+ }
+
+ return mSessionImpl.startStreaming(surfaces, executor, callback);
}
@Override
public void stopStreaming() throws CameraAccessException {
- // Todo: Need to add implementation.
+ mSessionImpl.stopRepeating();
}
@Override
@@ -90,16 +102,24 @@ public class CameraSharedCaptureSessionImpl
}
@Override
+ public boolean supportsOfflineProcessing(Surface surface) {
+ return false;
+ }
+
+ @Override
public void abortCaptures() throws CameraAccessException {
- if (mIsPrimary) {
+ if (mCameraDevice.isPrimaryClient()) {
mSessionImpl.abortCaptures();
+ return;
}
+ throw new UnsupportedOperationException("Shared capture session only supports this method"
+ + " for primary clients");
}
@Override
public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
Handler handler) throws CameraAccessException {
- if (mIsPrimary) {
+ if (mCameraDevice.isPrimaryClient()) {
return mSessionImpl.setRepeatingRequest(request, listener, handler);
}
throw new UnsupportedOperationException("Shared capture session only supports this method"
@@ -107,16 +127,30 @@ public class CameraSharedCaptureSessionImpl
}
@Override
+ public int setSingleRepeatingRequest(CaptureRequest request, Executor executor,
+ CaptureCallback listener)
+ throws CameraAccessException {
+ if (mCameraDevice.isPrimaryClient()) {
+ return mSessionImpl.setSingleRepeatingRequest(request, executor, listener);
+ }
+ throw new UnsupportedOperationException("Shared capture session only supports this method"
+ + " for primary clients");
+ }
+
+ @Override
public void stopRepeating() throws CameraAccessException {
- if (mIsPrimary) {
+ if (mCameraDevice.isPrimaryClient()) {
mSessionImpl.stopRepeating();
+ return;
}
+ throw new UnsupportedOperationException("Shared capture session only supports this method"
+ + " for primary clients");
}
@Override
public int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
throws CameraAccessException {
- if (mIsPrimary) {
+ if (mCameraDevice.isPrimaryClient()) {
return mSessionImpl.capture(request, listener, handler);
}
throw new UnsupportedOperationException("Shared capture session only supports this method"
@@ -124,6 +158,17 @@ public class CameraSharedCaptureSessionImpl
}
@Override
+ public int captureSingleRequest(CaptureRequest request, Executor executor,
+ CaptureCallback listener)
+ throws CameraAccessException {
+ if (mCameraDevice.isPrimaryClient()) {
+ return mSessionImpl.captureSingleRequest(request, executor, listener);
+ }
+ throw new UnsupportedOperationException("Shared capture session only supports this method"
+ + " for primary clients");
+ }
+
+ @Override
public void tearDown(Surface surface) throws CameraAccessException {
mSessionImpl.tearDown(surface);
}
@@ -149,48 +194,72 @@ public class CameraSharedCaptureSessionImpl
}
@Override
+ public CameraOfflineSession switchToOffline(Collection<Surface> offlineSurfaces,
+ Executor executor, CameraOfflineSessionCallback listener)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared capture session do not support this method"
+ );
+ }
+
+ @Override
public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
Handler handler) throws CameraAccessException {
- throw new UnsupportedOperationException("Shared Capture session doesn't support"
+ throw new UnsupportedOperationException("Shared Capture session do not support"
+ + " this method");
+ }
+
+ @Override
+ public int setRepeatingBurstRequests(List<CaptureRequest> requests,
+ Executor executor, CaptureCallback listener)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared Capture session do not support"
+ " this method");
}
@Override
public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
Handler handler) throws CameraAccessException {
- throw new UnsupportedOperationException("Shared Capture session doesn't support"
+ throw new UnsupportedOperationException("Shared Capture session do not support"
+ + " this method");
+ }
+
+ @Override
+ public int captureBurstRequests(List<CaptureRequest> requests,
+ Executor executor, CaptureCallback listener)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Shared Capture session do not support"
+ " this method");
}
@Override
public void updateOutputConfiguration(OutputConfiguration config)
throws CameraAccessException {
- throw new UnsupportedOperationException("Shared capture session doesn't support"
+ throw new UnsupportedOperationException("Shared capture session do not support"
+ " this method");
}
@Override
public void finalizeOutputConfigurations(List<OutputConfiguration> deferredOutputConfigs)
throws CameraAccessException {
- throw new UnsupportedOperationException("Shared capture session doesn't support"
+ throw new UnsupportedOperationException("Shared capture session do not support"
+ " this method");
}
@Override
public void prepare(Surface surface) throws CameraAccessException {
- throw new UnsupportedOperationException("Shared capture session doesn't support"
+ throw new UnsupportedOperationException("Shared capture session do not support"
+ " this method");
}
@Override
public void prepare(int maxCount, Surface surface) throws CameraAccessException {
- throw new UnsupportedOperationException("Shared capture session doesn't support"
+ throw new UnsupportedOperationException("Shared capture session do not support"
+ " this method");
}
@Override
public void closeWithoutDraining() {
- throw new UnsupportedOperationException("Shared capture session doesn't support"
+ throw new UnsupportedOperationException("Shared capture session do not support"
+ " this method");
}
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index a79e084b7f41..0b8e9c2687c3 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -65,6 +65,17 @@ public class ICameraDeviceUserWrapper {
}
}
+ public SubmitInfo startStreaming(int[] streamIdxArray, int[] surfaceIdxArray)
+ throws CameraAccessException {
+ try {
+ return mRemoteDevice.startStreaming(streamIdxArray, surfaceIdxArray);
+ } catch (ServiceSpecificException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ } catch (RemoteException e) {
+ throw ExceptionUtils.throwAsPublicException(e);
+ }
+ }
+
public SubmitInfo submitRequest(CaptureRequest request, boolean streaming)
throws CameraAccessException {
try {
@@ -325,4 +336,4 @@ public class ICameraDeviceUserWrapper {
}
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index e12c46322d8c..d394154a2c0e 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1803,6 +1803,19 @@ public final class OutputConfiguration implements Parcelable {
}
/**
+ * Get the flag indicating if this {@link OutputConfiguration} is for a multi-resolution output
+ * with a MultiResolutionImageReader.
+ *
+ * @return true if this {@link OutputConfiguration} is for a multi-resolution output with a
+ * MultiResolutionImageReader.
+ *
+ * @hide
+ */
+ public boolean isMultiResolution() {
+ return mIsMultiResolution;
+ }
+
+ /**
* Get the physical camera ID associated with this {@link OutputConfiguration}.
*
* <p>If this OutputConfiguration isn't targeting a physical camera of a logical
diff --git a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
index cdcc92ce4404..365f870ba22d 100644
--- a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
@@ -212,7 +212,7 @@ public final class SharedSessionConfiguration {
}
public @Nullable String getPhysicalCameraId() {
- return mPhysicalCameraId;
+ return mPhysicalCameraId.isEmpty() ? null : mPhysicalCameraId;
}
}
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 1e5bed519e1a..de88895ba55c 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -539,7 +539,10 @@ public class HubEndpoint {
return this;
}
- /** Attach a callback interface for lifecycle events for this Endpoint */
+ /**
+ * Attach a callback interface for lifecycle events for this Endpoint. Callback will be
+ * posted to the main thread.
+ */
@NonNull
public Builder setLifecycleCallback(
@NonNull HubEndpointLifecycleCallback lifecycleCallback) {
@@ -560,7 +563,10 @@ public class HubEndpoint {
return this;
}
- /** Attach a callback interface for message events for this Endpoint */
+ /**
+ * Attach a callback interface for message events for this Endpoint. Callback will be posted
+ * to the main thread.
+ */
@NonNull
public Builder setMessageCallback(@NonNull HubEndpointMessageCallback messageCallback) {
mMessageCallback = messageCallback;
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index 211aefffa34c..ba5dfc094afb 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -129,14 +129,38 @@ public final class DisplayTopology implements Parcelable {
}
/**
+ * Update the size of a display and normalize the topology.
+ * @param displayId The logical display ID
+ * @param width The new width
+ * @param height The new height
+ * @return True if the topology has changed.
+ */
+ public boolean updateDisplay(int displayId, float width, float height) {
+ TreeNode display = findDisplay(displayId, mRoot);
+ if (display == null) {
+ return false;
+ }
+ if (floatEquals(display.mWidth, width) && floatEquals(display.mHeight, height)) {
+ return false;
+ }
+ display.mWidth = width;
+ display.mHeight = height;
+ normalize();
+ Slog.i(TAG, "Display with ID " + displayId + " updated, new width: " + width
+ + ", new height: " + height);
+ return true;
+ }
+
+ /**
* Remove a display from the topology.
* The default topology is created from the remaining displays, as if they were reconnected
* one by one.
* @param displayId The logical display ID
+ * @return True if the display was present in the topology and removed.
*/
- public void removeDisplay(int displayId) {
+ public boolean removeDisplay(int displayId) {
if (findDisplay(displayId, mRoot) == null) {
- return;
+ return false;
}
Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(mRoot);
@@ -159,6 +183,7 @@ public final class DisplayTopology implements Parcelable {
} else {
Slog.i(TAG, "Display with ID " + displayId + " removed");
}
+ return true;
}
/**
@@ -685,12 +710,12 @@ public final class DisplayTopology implements Parcelable {
/**
* The width of the display in density-independent pixels (dp).
*/
- private final float mWidth;
+ private float mWidth;
/**
* The height of the display in density-independent pixels (dp).
*/
- private final float mHeight;
+ private float mHeight;
/**
* The position of this display relative to its parent.
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 313bad50e88e..c4d11cd8aff7 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -204,3 +204,10 @@ flag {
description: "Allows the user to disable input scrolling acceleration for mouse."
bug: "383555305"
}
+
+flag {
+ name: "remove_fallback_modifiers"
+ namespace: "input"
+ description: "Removes modifiers from the original key event that activated the fallback, ensuring that only the intended fallback event is sent."
+ bug: "382545048"
+}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
index 90136ae00f6a..ffe8086ca4a1 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
@@ -93,6 +93,10 @@ import android.util.Log;
* must do its own state management (keeping in mind that the service's process might be killed
* by the Android System when unbound; for example, if the device is running low in memory).
*
+ * <p> The service also provides pending intents to override the system's Quick Access activities
+ * via the {@link #getTargetActivityPendingIntent} and the
+ * {@link #getGestureTargetActivityPendingIntent} method.
+ *
* <p>
* <a name="ErrorHandling"></a>
* <h3>Error handling</h3>
@@ -384,6 +388,10 @@ public abstract class QuickAccessWalletService extends Service {
*
* <p>The pending intent will be sent when the user performs a gesture to open Wallet.
* The pending intent should launch an activity.
+ *
+ * <p> If the gesture is performed and this method returns null, the system will launch the
+ * activity specified by the {@link #getTargetActivityPendingIntent} method. If that method
+ * also returns null, the system will launch the system-provided card switcher activity.
*/
@Nullable
@FlaggedApi(Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index fb1bd1703ce6..6116d599baa0 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -70,3 +70,11 @@ flag {
is_fixed_read_only: true
bug: "352538294"
}
+
+flag {
+ name: "system_server_large_perfetto_shmem_buffer"
+ namespace: "windowing_tools"
+ description: "Large perfetto shmem buffer"
+ is_fixed_read_only: true
+ bug: "382369925"
+}
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index a8d4e2d2c70a..48dfdd4a95f4 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -16,6 +16,9 @@
package android.view;
+
+import static com.android.hardware.input.Flags.removeFallbackModifiers;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -458,7 +461,15 @@ public class KeyCharacterMap implements Parcelable {
FallbackAction action = FallbackAction.obtain();
metaState = KeyEvent.normalizeMetaState(metaState);
if (nativeGetFallbackAction(mPtr, keyCode, metaState, action)) {
- action.metaState = KeyEvent.normalizeMetaState(action.metaState);
+ if (removeFallbackModifiers()) {
+ // Strip all modifiers. This is safe to do since only exact keyCode + metaState
+ // modifiers will trigger a fallback.
+ // E.g. Ctrl + Space -> language_switch (fallback generated)
+ // Ctrl + Alt + Space -> Ctrl + Alt + Space (no fallback generated)
+ action.metaState = 0;
+ } else {
+ action.metaState = KeyEvent.normalizeMetaState(action.metaState);
+ }
return action;
}
action.recycle();
diff --git a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
index 20d193ea2351..f709ed7f57cd 100644
--- a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
+++ b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
@@ -12,5 +12,5 @@ flag {
name: "ccapi_baklava_enabled"
namespace: "machine_learning"
description: "Feature flag for baklava content capture API"
- bug: "309411951"
+ bug: "380381249"
}
diff --git a/core/java/android/window/WindowTokenClientController.java b/core/java/android/window/WindowTokenClientController.java
index 11019324acd8..fcd7dfbb1769 100644
--- a/core/java/android/window/WindowTokenClientController.java
+++ b/core/java/android/window/WindowTokenClientController.java
@@ -148,6 +148,9 @@ public class WindowTokenClientController {
info = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed attachToDisplayContent", e);
+ return false;
}
if (info == null) {
return false;
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 801698caff0e..a9674716eb40 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -2,16 +2,6 @@ package: "com.android.window.flags"
container: "system"
flag {
- name: "disable_thin_letterboxing_policy"
- namespace: "large_screen_experiences_app_compat"
- description: "Whether reachability is disabled in case of thin letterboxing"
- bug: "341027847"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "allows_screen_size_decoupled_from_status_bar_and_cutout"
namespace: "large_screen_experiences_app_compat"
description: "When necessary, configuration decoupled from status bar and display cutout"
@@ -58,13 +48,6 @@ flag {
}
flag {
- name: "user_min_aspect_ratio_app_default"
- namespace: "large_screen_experiences_app_compat"
- description: "Whether the API PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT is available"
- bug: "310816437"
-}
-
-flag {
name: "allow_hide_scm_button"
namespace: "large_screen_experiences_app_compat"
description: "Whether we should allow hiding the size compat restart button"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index c97d3ec787b3..abd93cfaf179 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -105,6 +105,13 @@ flag {
flag {
namespace: "windowing_sdk"
+ name: "activity_embedding_support_for_connected_displays"
+ description: "Enables activity embedding support for connected displays, including enabling AE optimization for Settings."
+ bug: "369438353"
+}
+
+flag {
+ namespace: "windowing_sdk"
name: "wlinfo_oncreate"
description: "Makes WindowLayoutInfo accessible without racing in the Activity#onCreate()"
bug: "337820752"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index 76c810bdb2c1..e91e1115ac1c 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -157,39 +157,27 @@
android:maxDrawableHeight="@dimen/notification_right_icon_size"
/>
- <LinearLayout
- android:id="@+id/notification_buttons_column"
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
- android:orientation="vertical"
+ android:minWidth="@dimen/notification_content_margin_end"
>
- <include layout="@layout/notification_close_button"
- android:layout_width="@dimen/notification_close_button_size"
- android:layout_height="@dimen/notification_close_button_size"
- android:layout_gravity="end"
- android:layout_marginEnd="20dp"
- />
-
- <FrameLayout
- android:id="@+id/expand_button_touch_container"
+ <include layout="@layout/notification_expand_button"
android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:minWidth="@dimen/notification_content_margin_end"
- >
-
- <include layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
- />
-
- </FrameLayout>
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
- </LinearLayout>
+ </FrameLayout>
</LinearLayout>
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="top|end" />
+
</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 2e0a7afc3cd1..2d367337bb6f 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -194,4 +194,11 @@
</FrameLayout>
</LinearLayout>
+
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="top|end" />
+
</com.android.internal.widget.MediaNotificationView>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index f644adefda9d..fbecb8c30b9c 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -199,6 +199,12 @@
</LinearLayout>
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="top|end" />
+
</com.android.internal.widget.NotificationMaxHeightFrameLayout>
<LinearLayout
diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml
index fc727e1c72f5..2d30d8a8bbb6 100644
--- a/core/res/res/layout/notification_2025_template_header.xml
+++ b/core/res/res/layout/notification_2025_template_header.xml
@@ -60,7 +60,7 @@
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
- android:layout_toStartOf="@id/notification_buttons_column"
+ android:layout_toStartOf="@id/expand_button"
android:layout_alignWithParentIfMissing="true"
android:clipChildren="false"
android:gravity="center_vertical"
@@ -81,28 +81,17 @@
android:focusable="false"
/>
- <LinearLayout
- android:id="@+id/notification_buttons_column"
+ <include layout="@layout/notification_expand_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:orientation="vertical"
- >
-
- <include layout="@layout/notification_close_button"
- android:layout_width="@dimen/notification_close_button_size"
- android:layout_height="@dimen/notification_close_button_size"
- android:layout_gravity="end"
- android:layout_marginEnd="20dp"
- />
-
- <include layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- />
+ android:layout_centerVertical="true"
+ android:layout_alignParentEnd="true" />
- </LinearLayout>
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentEnd="true" />
</NotificationHeaderView>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 565e28e0cc87..45a5d85a097d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2165,6 +2165,17 @@
config_enableGeofenceOverlay is false. -->
<string name="config_geofenceProviderPackageName" translatable="false">@null</string>
+ <!-- Whether to enable GNSS assistance overlay which allows GnssAssistanceProvider to be
+ replaced by an app at run-time. When disabled, only the
+ config_gnssAssistanceProviderPackageName package will be searched for
+ GnssAssistanceProvider, otherwise any system package is eligible. Anyone who wants to
+ disable the overlay mechanism can set it to false.
+ -->
+ <bool name="config_enableGnssAssistanceOverlay" translatable="false">true</bool>
+ <!-- Package name providing GNSS assistance API support. Used only when
+ config_enableGnssAssistanceOverlay is false. -->
+ <string name="config_gnssAssistanceProviderPackageName" translatable="false">@null</string>
+
<!-- Whether to enable Hardware Activity-Recognition overlay which allows Hardware
Activity-Recognition to be replaced by an app at run-time. When disabled, only the
config_activityRecognitionHardwarePackageName package will be searched for
diff --git a/core/res/res/values/config_watch.xml b/core/res/res/values/config_watch.xml
index 629a343f1280..bcb1e0941b5a 100644
--- a/core/res/res/values/config_watch.xml
+++ b/core/res/res/values/config_watch.xml
@@ -15,8 +15,7 @@
-->
<resources>
- <!-- TODO(b/382103556): use predefined Material3 token -->
<!-- For Wear Material3 -->
- <dimen name="config_wearMaterial3_buttonCornerRadius">26dp</dimen>
- <dimen name="config_wearMaterial3_bottomDialogCornerRadius">18dp</dimen>
+ <dimen name="config_wearMaterial3_buttonCornerRadius">@dimen/config_shapeCornerRadiusLarge</dimen>
+ <dimen name="config_wearMaterial3_bottomDialogCornerRadius">@dimen/config_shapeCornerRadiusMedium</dimen>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 73e06f6a2520..8195d38993c8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2040,6 +2040,7 @@
<java-symbol type="bool" name="config_useGnssHardwareProvider" />
<java-symbol type="bool" name="config_enableGeocoderOverlay" />
<java-symbol type="bool" name="config_enableGeofenceOverlay" />
+ <java-symbol type="bool" name="config_enableGnssAssistanceOverlay" />
<java-symbol type="bool" name="config_enableNetworkLocationOverlay" />
<java-symbol type="bool" name="config_sf_limitedAlpha" />
<java-symbol type="bool" name="config_unplugTurnsOnScreen" />
@@ -2223,6 +2224,7 @@
<java-symbol type="string" name="config_gnssLocationProviderPackageName" />
<java-symbol type="string" name="config_geocoderProviderPackageName" />
<java-symbol type="string" name="config_geofenceProviderPackageName" />
+ <java-symbol type="string" name="config_gnssAssistanceProviderPackageName" />
<java-symbol type="string" name="config_networkLocationProviderPackageName" />
<java-symbol type="string" name="config_wimaxManagerClassname" />
<java-symbol type="string" name="config_wimaxNativeLibLocation" />
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
index 4a227d8ff1ef..255d09b854bd 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
+++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
@@ -86,7 +86,7 @@ class DisplayTopologyTest {
verifyDisplay(display1, displayId1, width1, height1, noOfChildren = 1)
val display2 = display1.children[0]
- verifyDisplay(display1.children[0], displayId2, width2, height2, POSITION_TOP,
+ verifyDisplay(display2, displayId2, width2, height2, POSITION_TOP,
offset = width1 / 2 - width2 / 2, noOfChildren = 1)
var display = display2
@@ -99,6 +99,76 @@ class DisplayTopologyTest {
}
@Test
+ fun updateDisplay() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+
+ val newWidth = 1000f
+ val newHeight = 500f
+ topology.addDisplay(displayId, width, height)
+ assertThat(topology.updateDisplay(displayId, newWidth, newHeight)).isTrue()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId)
+ verifyDisplay(topology.root!!, displayId, newWidth, newHeight, noOfChildren = 0)
+ }
+
+ @Test
+ fun updateDisplay_notUpdated() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+ topology.addDisplay(displayId, width, height)
+
+ // Same size
+ assertThat(topology.updateDisplay(displayId, width, height)).isFalse()
+
+ // Display doesn't exist
+ assertThat(topology.updateDisplay(/* displayId= */ 100, width, height)).isFalse()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId)
+ verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0)
+ }
+
+ @Test
+ fun updateDisplayDoesNotAffectDefaultTopology() {
+ val width1 = 700f
+ val height = 600f
+ topology.addDisplay(/* displayId= */ 1, width1, height)
+
+ val width2 = 800f
+ val noOfDisplays = 30
+ for (i in 2..noOfDisplays) {
+ topology.addDisplay(/* displayId= */ i, width2, height)
+ }
+
+ val displaysToUpdate = arrayOf(3, 7, 18)
+ val newWidth = 1000f
+ val newHeight = 1500f
+ for (i in displaysToUpdate) {
+ assertThat(topology.updateDisplay(/* displayId= */ i, newWidth, newHeight)).isTrue()
+ }
+
+ assertThat(topology.primaryDisplayId).isEqualTo(1)
+
+ val display1 = topology.root!!
+ verifyDisplay(display1, id = 1, width1, height, noOfChildren = 1)
+
+ val display2 = display1.children[0]
+ verifyDisplay(display2, id = 2, width2, height, POSITION_TOP,
+ offset = width1 / 2 - width2 / 2, noOfChildren = 1)
+
+ var display = display2
+ for (i in 3..noOfDisplays) {
+ display = display.children[0]
+ // The last display should have no children
+ verifyDisplay(display, id = i, if (i in displaysToUpdate) newWidth else width2,
+ if (i in displaysToUpdate) newHeight else height, POSITION_RIGHT, offset = 0f,
+ noOfChildren = if (i < noOfDisplays) 1 else 0)
+ }
+ }
+
+ @Test
fun removeDisplays() {
val displayId1 = 1
val width1 = 800f
@@ -117,7 +187,7 @@ class DisplayTopologyTest {
}
var removedDisplays = arrayOf(20)
- topology.removeDisplay(20)
+ assertThat(topology.removeDisplay(20)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
@@ -139,11 +209,11 @@ class DisplayTopologyTest {
noOfChildren = if (i < noOfDisplays) 1 else 0)
}
- topology.removeDisplay(22)
+ assertThat(topology.removeDisplay(22)).isTrue()
removedDisplays += 22
- topology.removeDisplay(23)
+ assertThat(topology.removeDisplay(23)).isTrue()
removedDisplays += 23
- topology.removeDisplay(25)
+ assertThat(topology.removeDisplay(25)).isTrue()
removedDisplays += 25
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
@@ -174,7 +244,7 @@ class DisplayTopologyTest {
val height = 600f
topology.addDisplay(displayId, width, height)
- topology.removeDisplay(displayId)
+ assertThat(topology.removeDisplay(displayId)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(Display.INVALID_DISPLAY)
assertThat(topology.root).isNull()
@@ -187,7 +257,7 @@ class DisplayTopologyTest {
val height = 600f
topology.addDisplay(displayId, width, height)
- topology.removeDisplay(3)
+ assertThat(topology.removeDisplay(3)).isFalse()
assertThat(topology.primaryDisplayId).isEqualTo(displayId)
verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0)
@@ -203,7 +273,7 @@ class DisplayTopologyTest {
topology = DisplayTopology(/* root= */ null, displayId2)
topology.addDisplay(displayId1, width, height)
topology.addDisplay(displayId2, width, height)
- topology.removeDisplay(displayId2)
+ assertThat(topology.removeDisplay(displayId2)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
verifyDisplay(topology.root!!, displayId1, width, height, noOfChildren = 0)
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
index 41d1b5c15369..eecf199a3ec2 100644
--- a/libs/WindowManager/Shell/multivalentTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -55,6 +55,7 @@ android_robolectric_test {
"truth",
"flag-junit-base",
"flag-junit",
+ "testables",
],
auto_gen_config: true,
}
@@ -77,6 +78,7 @@ android_test {
"truth",
"platform-test-annotations",
"platform-test-rules",
+ "testables",
],
libs: [
"android.test.base.stubs.system",
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
index 0d8f80935f5a..3e01256fd67c 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
@@ -16,6 +16,8 @@
package com.android.wm.shell.bubbles.bar
+import android.animation.AnimatorTestRule
+import android.app.ActivityManager
import android.content.Context
import android.graphics.Insets
import android.graphics.Rect
@@ -23,7 +25,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
-import androidx.core.animation.AnimatorTestRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -36,27 +37,34 @@ import com.android.wm.shell.bubbles.BubbleExpandedViewManager
import com.android.wm.shell.bubbles.BubbleLogger
import com.android.wm.shell.bubbles.BubbleOverflow
import com.android.wm.shell.bubbles.BubblePositioner
+import com.android.wm.shell.bubbles.BubbleTaskView
import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
import com.android.wm.shell.bubbles.FakeBubbleFactory
-import com.android.wm.shell.bubbles.FakeBubbleTaskViewFactory
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import org.junit.After
import org.junit.Before
-import org.junit.ClassRule
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
/** Tests for [BubbleBarAnimationHelper] */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleBarAnimationHelperTest {
- companion object {
- @JvmField @ClassRule val animatorTestRule: AnimatorTestRule = AnimatorTestRule()
+ @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)
+ companion object {
const val SCREEN_WIDTH = 2000
const val SCREEN_HEIGHT = 1000
}
@@ -148,6 +156,26 @@ class BubbleBarAnimationHelperTest {
}
@Test
+ fun animateSwitch_bubbleToBubble_updateTaskBounds() {
+ val fromBubble = createBubble("from").initialize(container)
+ val toBubbleTaskController = mock<TaskViewTaskController>()
+ val toBubble = createBubble("to", toBubbleTaskController).initialize(container)
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateSwitch(fromBubble, toBubble) {}
+ // Start the animation, but don't finish
+ animatorTestRule.advanceTimeBy(100)
+ }
+ getInstrumentation().waitForIdleSync()
+ // Clear invocations to ensure that bounds update happens after animation ends
+ clearInvocations(toBubbleTaskController)
+ getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(900) }
+ getInstrumentation().waitForIdleSync()
+
+ verify(toBubbleTaskController).setWindowBounds(any())
+ }
+
+ @Test
fun animateSwitch_bubbleToOverflow_oldHiddenNewShown() {
val fromBubble = createBubble(key = "from").initialize(container)
val overflow = createOverflow().initialize(container)
@@ -193,13 +221,43 @@ class BubbleBarAnimationHelperTest {
assertThat(toBubble.bubbleBarExpandedView?.isSurfaceZOrderedOnTop).isFalse()
}
- private fun createBubble(key: String): Bubble {
+ @Test
+ fun animateToRestPosition_updateTaskBounds() {
+ val taskController = mock<TaskViewTaskController>()
+ val bubble = createBubble("key", taskController).initialize(container)
+
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateExpansion(bubble) {}
+ animatorTestRule.advanceTimeBy(1000)
+ }
+ getInstrumentation().waitForIdleSync()
+ getInstrumentation().runOnMainSync {
+ animationHelper.animateToRestPosition()
+ animatorTestRule.advanceTimeBy(100)
+ }
+ // Clear invocations to ensure that bounds update happens after animation ends
+ clearInvocations(taskController)
+ getInstrumentation().runOnMainSync { animatorTestRule.advanceTimeBy(900) }
+ getInstrumentation().waitForIdleSync()
+
+ verify(taskController).setWindowBounds(any())
+ }
+
+ private fun createBubble(
+ key: String,
+ taskViewTaskController: TaskViewTaskController = mock<TaskViewTaskController>(),
+ ): Bubble {
+ val taskView = TaskView(context, taskViewTaskController)
+ val taskInfo = mock<ActivityManager.RunningTaskInfo>()
+ whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
+ val bubbleTaskView = BubbleTaskView(taskView, mainExecutor)
+
val bubbleBarExpandedView =
FakeBubbleFactory.createExpandedView(
context,
bubblePositioner,
expandedViewManager,
- FakeBubbleTaskViewFactory(context, mainExecutor).create(),
+ bubbleTaskView,
mainExecutor,
bgExecutor,
bubbleLogger,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 3e8a9b64dac6..3188e5b9c6d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -463,6 +463,7 @@ public class BubbleBarAnimationHelper {
super.onAnimationEnd(animation);
bbev.resetPivot();
bbev.setDragging(false);
+ updateExpandedView(bbev);
}
});
startNewAnimator(animatorSet);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index c74bf53268f9..9ebb7f5aa270 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -643,7 +643,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
t.setPosition(animatingLeash, x, endY);
t.setAlpha(animatingLeash, 1.f);
}
- dispatchEndPositioning(mDisplayId, mCancelled, t);
+ if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+ dispatchEndPositioning(mDisplayId, mCancelled, t);
+ }
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_ANIMATION_RUNNING);
@@ -659,6 +661,14 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
ImeTracker.forLogging().onCancelled(mStatsToken,
ImeTracker.PHASE_WM_ANIMATION_RUNNING);
}
+ if (android.view.inputmethod.Flags.refactorInsetsController()) {
+ // In split screen, we also set {@link
+ // WindowContainer#mExcludeInsetsTypes} but this should only happen after
+ // the IME client visibility was set. Otherwise the insets will we
+ // dispatched too early, and we get a flicker. Thus, only dispatching it
+ // after reporting that the IME is hidden to system server.
+ dispatchEndPositioning(mDisplayId, mCancelled, t);
+ }
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END,
mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index 606a729305b4..90191345147c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -82,7 +82,7 @@ fun calculateInitialBounds(
// For portrait resizeable activities, respect apps fullscreen width but
// apply ideal size height.
Size(
- taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth,
+ taskInfo.appCompatTaskInfo.topActivityAppBounds.width(),
idealSize.height,
)
} else {
@@ -104,7 +104,7 @@ fun calculateInitialBounds(
// apply custom app width.
Size(
customPortraitWidthForLandscapeApp,
- taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight,
+ taskInfo.appCompatTaskInfo.topActivityAppBounds.height(),
)
} else {
// For portrait resizeable activities, simply apply ideal size.
@@ -196,13 +196,8 @@ fun maximizeSizeGivenAspectRatio(
/** Calculates the aspect ratio of an activity from its fullscreen bounds. */
fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
- val appLetterboxWidth = taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth
- val appLetterboxHeight = taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight
- if (taskInfo.appCompatTaskInfo.isTopActivityLetterboxed || !taskInfo.canChangeAspectRatio) {
- return maxOf(appLetterboxWidth, appLetterboxHeight) /
- minOf(appLetterboxWidth, appLetterboxHeight).toFloat()
- }
- val appBounds = taskInfo.configuration.windowConfiguration.appBounds ?: return 1f
+ if (taskInfo.appCompatTaskInfo.topActivityAppBounds.isEmpty) return 1f
+ val appBounds = taskInfo.appCompatTaskInfo.topActivityAppBounds
return maxOf(appBounds.height(), appBounds.width()) /
minOf(appBounds.height(), appBounds.width()).toFloat()
}
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 4f37180baa37..e1c2153014fa 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
@@ -4160,8 +4160,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
configuration.windowConfiguration.appBounds = bounds
}
- appCompatTaskInfo.topActivityLetterboxAppWidth = bounds.width()
- appCompatTaskInfo.topActivityLetterboxAppHeight = bounds.height()
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, bounds.width(), bounds.height())
isResizeable = false
}
@@ -4879,15 +4878,19 @@ class DesktopTasksControllerTest : ShellTestCase() {
appCompatTaskInfo.isSystemFullscreenOverrideEnabled = enableSystemFullscreenOverride
if (deviceOrientation == ORIENTATION_LANDSCAPE) {
- configuration.windowConfiguration.appBounds =
- Rect(0, 0, DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT)
- appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_LONG
- appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_SHORT
+ appCompatTaskInfo.topActivityAppBounds.set(
+ 0,
+ 0,
+ DISPLAY_DIMENSION_LONG,
+ DISPLAY_DIMENSION_SHORT,
+ )
} else {
- configuration.windowConfiguration.appBounds =
- Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
- appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_SHORT
- appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_LONG
+ appCompatTaskInfo.topActivityAppBounds.set(
+ 0,
+ 0,
+ DISPLAY_DIMENSION_SHORT,
+ DISPLAY_DIMENSION_LONG,
+ )
}
if (shouldLetterbox) {
@@ -4897,17 +4900,15 @@ class DesktopTasksControllerTest : ShellTestCase() {
screenOrientation == SCREEN_ORIENTATION_PORTRAIT
) {
// Letterbox to portrait size
- appCompatTaskInfo.setTopActivityLetterboxed(true)
- appCompatTaskInfo.topActivityLetterboxAppWidth = 1200
- appCompatTaskInfo.topActivityLetterboxAppHeight = 1600
+ appCompatTaskInfo.isTopActivityLetterboxed = true
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, 1200, 1600)
} else if (
deviceOrientation == ORIENTATION_PORTRAIT &&
screenOrientation == SCREEN_ORIENTATION_LANDSCAPE
) {
// Letterbox to landscape size
- appCompatTaskInfo.setTopActivityLetterboxed(true)
- appCompatTaskInfo.topActivityLetterboxAppWidth = 1600
- appCompatTaskInfo.topActivityLetterboxAppHeight = 1200
+ appCompatTaskInfo.isTopActivityLetterboxed = true
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, 1600, 1200)
}
}
}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 1bc15d72bacc..cc4a29b31996 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -199,6 +199,7 @@ cc_test {
// This is to suppress warnings/errors from gtest
"-Wno-unnamed-type-template-args",
],
+ require_root: true,
srcs: [
// Helpers/infra for testing.
"tests/CommonHelpers.cpp",
diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp
index 70d14a11830e..970463492c1a 100644
--- a/libs/androidfw/TypeWrappers.cpp
+++ b/libs/androidfw/TypeWrappers.cpp
@@ -16,8 +16,6 @@
#include <androidfw/TypeWrappers.h>
-#include <algorithm>
-
namespace android {
TypeVariant::TypeVariant(const ResTable_type* data) : data(data), mLength(dtohl(data->entryCount)) {
@@ -31,30 +29,44 @@ TypeVariant::TypeVariant(const ResTable_type* data) : data(data), mLength(dtohl(
ALOGE("Type's entry indices extend beyond its boundaries");
mLength = 0;
} else {
- mLength = ResTable_sparseTypeEntry{entryIndices[entryCount - 1]}.idx + 1;
+ mLength = dtohs(ResTable_sparseTypeEntry{entryIndices[entryCount - 1]}.idx) + 1;
}
}
}
TypeVariant::iterator& TypeVariant::iterator::operator++() {
- mIndex++;
+ ++mIndex;
if (mIndex > mTypeVariant->mLength) {
mIndex = mTypeVariant->mLength;
}
- return *this;
-}
-static bool keyCompare(uint32_t entry, uint16_t index) {
- return dtohs(ResTable_sparseTypeEntry{entry}.idx) < index;
+ const ResTable_type* type = mTypeVariant->data;
+ if ((type->flags & ResTable_type::FLAG_SPARSE) == 0) {
+ return *this;
+ }
+
+ // Need to adjust |mSparseIndex| as well if we've passed its current element.
+ const uint32_t entryCount = dtohl(type->entryCount);
+ const auto entryIndices = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
+ if (mSparseIndex >= entryCount) {
+ return *this; // done
+ }
+ const auto element = (const ResTable_sparseTypeEntry*)(entryIndices + mSparseIndex);
+ if (mIndex > dtohs(element->idx)) {
+ ++mSparseIndex;
+ }
+
+ return *this;
}
const ResTable_entry* TypeVariant::iterator::operator*() const {
- const ResTable_type* type = mTypeVariant->data;
if (mIndex >= mTypeVariant->mLength) {
- return NULL;
+ return nullptr;
}
- const uint32_t entryCount = dtohl(mTypeVariant->data->entryCount);
+ const ResTable_type* type = mTypeVariant->data;
+ const uint32_t entryCount = dtohl(type->entryCount);
const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type)
+ dtohl(type->header.size);
const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
@@ -63,18 +75,19 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
sizeof(uint16_t) : sizeof(uint32_t);
if (reinterpret_cast<uintptr_t>(entryIndices) + (indexSize * entryCount) > containerEnd) {
ALOGE("Type's entry indices extend beyond its boundaries");
- return NULL;
+ return nullptr;
}
uint32_t entryOffset;
if (type->flags & ResTable_type::FLAG_SPARSE) {
- auto iter = std::lower_bound(entryIndices, entryIndices + entryCount, mIndex, keyCompare);
- if (iter == entryIndices + entryCount
- || dtohs(ResTable_sparseTypeEntry{*iter}.idx) != mIndex) {
- return NULL;
+ if (mSparseIndex >= entryCount) {
+ return nullptr;
}
-
- entryOffset = static_cast<uint32_t>(dtohs(ResTable_sparseTypeEntry{*iter}.offset)) * 4u;
+ const auto element = (const ResTable_sparseTypeEntry*)(entryIndices + mSparseIndex);
+ if (dtohs(element->idx) != mIndex) {
+ return nullptr;
+ }
+ entryOffset = static_cast<uint32_t>(dtohs(element->offset)) * 4u;
} else if (type->flags & ResTable_type::FLAG_OFFSET16) {
auto entryIndices16 = reinterpret_cast<const uint16_t*>(entryIndices);
entryOffset = offset_from16(entryIndices16[mIndex]);
@@ -83,25 +96,25 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
}
if (entryOffset == ResTable_type::NO_ENTRY) {
- return NULL;
+ return nullptr;
}
if ((entryOffset & 0x3) != 0) {
ALOGE("Index %u points to entry with unaligned offset 0x%08x", mIndex, entryOffset);
- return NULL;
+ return nullptr;
}
const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
reinterpret_cast<uintptr_t>(type) + dtohl(type->entriesStart) + entryOffset);
if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) {
ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex);
- return NULL;
+ return nullptr;
} else if (reinterpret_cast<uintptr_t>(entry) + entry->size() > containerEnd) {
ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex);
- return NULL;
+ return nullptr;
} else if (entry->size() < sizeof(*entry)) {
ALOGE("Entry at index %u is too small (%zu)", mIndex, entry->size());
- return NULL;
+ return nullptr;
}
return entry;
}
diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h
index fb2fad619011..db641b78a4e4 100644
--- a/libs/androidfw/include/androidfw/TypeWrappers.h
+++ b/libs/androidfw/include/androidfw/TypeWrappers.h
@@ -27,24 +27,14 @@ struct TypeVariant {
class iterator {
public:
- iterator& operator=(const iterator& rhs) {
- mTypeVariant = rhs.mTypeVariant;
- mIndex = rhs.mIndex;
- return *this;
- }
-
bool operator==(const iterator& rhs) const {
return mTypeVariant == rhs.mTypeVariant && mIndex == rhs.mIndex;
}
- bool operator!=(const iterator& rhs) const {
- return mTypeVariant != rhs.mTypeVariant || mIndex != rhs.mIndex;
- }
-
iterator operator++(int) {
- uint32_t prevIndex = mIndex;
+ iterator prev = *this;
operator++();
- return iterator(mTypeVariant, prevIndex);
+ return prev;
}
const ResTable_entry* operator->() const {
@@ -60,18 +50,26 @@ struct TypeVariant {
private:
friend struct TypeVariant;
- iterator(const TypeVariant* tv, uint32_t index)
- : mTypeVariant(tv), mIndex(index) {}
+
+ enum class Kind { Begin, End };
+ iterator(const TypeVariant* tv, Kind kind)
+ : mTypeVariant(tv) {
+ mSparseIndex = mIndex = kind == Kind::Begin ? 0 : tv->mLength;
+ // mSparseIndex here is technically past the number of sparse entries, but it is still
+ // ok as it is enough to infer that this is the end iterator.
+ }
+
const TypeVariant* mTypeVariant;
uint32_t mIndex;
+ uint32_t mSparseIndex;
};
iterator beginEntries() const {
- return iterator(this, 0);
+ return iterator(this, iterator::Kind::Begin);
}
iterator endEntries() const {
- return iterator(this, mLength);
+ return iterator(this, iterator::Kind::End);
}
const ResTable_type* data;
diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp
index ed30904ec179..d66e05805484 100644
--- a/libs/androidfw/tests/TypeWrappers_test.cpp
+++ b/libs/androidfw/tests/TypeWrappers_test.cpp
@@ -14,28 +14,42 @@
* limitations under the License.
*/
-#include <algorithm>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
-#include <utils/String8.h>
+#include <androidfw/Util.h>
+
+#include <optional>
+#include <vector>
#include <gtest/gtest.h>
namespace android {
-// create a ResTable_type in memory with a vector of Res_value*
-static ResTable_type* createTypeTable(std::vector<Res_value*>& values,
- bool compact_entry = false,
- bool short_offsets = false)
+using ResValueVector = std::vector<std::optional<Res_value>>;
+
+// create a ResTable_type in memory
+static util::unique_cptr<ResTable_type> createTypeTable(
+ const ResValueVector& in_values, bool compact_entry, bool short_offsets, bool sparse)
{
+ ResValueVector sparse_values;
+ if (sparse) {
+ std::ranges::copy_if(in_values, std::back_inserter(sparse_values),
+ [](auto&& v) { return v.has_value(); });
+ }
+ const ResValueVector& values = sparse ? sparse_values : in_values;
+
ResTable_type t{};
t.header.type = RES_TABLE_TYPE_TYPE;
t.header.headerSize = sizeof(t);
t.header.size = sizeof(t);
t.id = 1;
- t.flags = short_offsets ? ResTable_type::FLAG_OFFSET16 : 0;
+ t.flags = sparse
+ ? ResTable_type::FLAG_SPARSE
+ : short_offsets ? ResTable_type::FLAG_OFFSET16 : 0;
- t.header.size += values.size() * (short_offsets ? sizeof(uint16_t) : sizeof(uint32_t));
+ t.header.size += values.size() *
+ (sparse ? sizeof(ResTable_sparseTypeEntry) :
+ short_offsets ? sizeof(uint16_t) : sizeof(uint32_t));
t.entriesStart = t.header.size;
t.entryCount = values.size();
@@ -53,9 +67,18 @@ static ResTable_type* createTypeTable(std::vector<Res_value*>& values,
memcpy(p_header, &t, sizeof(t));
size_t i = 0, entry_offset = 0;
- uint32_t k = 0;
- for (auto const& v : values) {
- if (short_offsets) {
+ uint32_t sparse_index = 0;
+
+ for (auto const& v : in_values) {
+ if (sparse) {
+ if (!v) {
+ ++i;
+ continue;
+ }
+ const auto p = reinterpret_cast<ResTable_sparseTypeEntry*>(p_offsets) + sparse_index++;
+ p->idx = i;
+ p->offset = (entry_offset >> 2) & 0xffffu;
+ } else if (short_offsets) {
uint16_t *p = reinterpret_cast<uint16_t *>(p_offsets) + i;
*p = v ? (entry_offset >> 2) & 0xffffu : 0xffffu;
} else {
@@ -83,62 +106,92 @@ static ResTable_type* createTypeTable(std::vector<Res_value*>& values,
}
i++;
}
- return reinterpret_cast<ResTable_type*>(data);
+ return util::unique_cptr<ResTable_type>{reinterpret_cast<ResTable_type*>(data)};
}
TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
- std::vector<Res_value *> values;
-
- Res_value *v1 = new Res_value{};
- values.push_back(v1);
-
- values.push_back(nullptr);
-
- Res_value *v2 = new Res_value{};
- values.push_back(v2);
-
- Res_value *v3 = new Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x12345678};
- values.push_back(v3);
+ ResValueVector values;
+
+ values.push_back(std::nullopt);
+ values.push_back(Res_value{});
+ values.push_back(std::nullopt);
+ values.push_back(Res_value{});
+ values.push_back(Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x12345678});
+ values.push_back(std::nullopt);
+ values.push_back(std::nullopt);
+ values.push_back(std::nullopt);
+ values.push_back(Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x87654321});
// test for combinations of compact_entry and short_offsets
- for (size_t i = 0; i < 4; i++) {
- bool compact_entry = i & 0x1, short_offsets = i & 0x2;
- ResTable_type* data = createTypeTable(values, compact_entry, short_offsets);
- TypeVariant v(data);
+ for (size_t i = 0; i < 8; i++) {
+ bool compact_entry = i & 0x1, short_offsets = i & 0x2, sparse = i & 0x4;
+ auto data = createTypeTable(values, compact_entry, short_offsets, sparse);
+ TypeVariant v(data.get());
TypeVariant::iterator iter = v.beginEntries();
ASSERT_EQ(uint32_t(0), iter.index());
- ASSERT_TRUE(NULL != *iter);
- ASSERT_EQ(uint32_t(0), iter->key());
+ ASSERT_TRUE(NULL == *iter);
ASSERT_NE(v.endEntries(), iter);
- iter++;
+ ++iter;
ASSERT_EQ(uint32_t(1), iter.index());
- ASSERT_TRUE(NULL == *iter);
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(uint32_t(1), iter->key());
ASSERT_NE(v.endEntries(), iter);
iter++;
ASSERT_EQ(uint32_t(2), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
+
+ ++iter;
+
+ ASSERT_EQ(uint32_t(3), iter.index());
ASSERT_TRUE(NULL != *iter);
- ASSERT_EQ(uint32_t(2), iter->key());
+ ASSERT_EQ(uint32_t(3), iter->key());
ASSERT_NE(v.endEntries(), iter);
iter++;
- ASSERT_EQ(uint32_t(3), iter.index());
+ ASSERT_EQ(uint32_t(4), iter.index());
ASSERT_TRUE(NULL != *iter);
ASSERT_EQ(iter->is_compact(), compact_entry);
- ASSERT_EQ(uint32_t(3), iter->key());
+ ASSERT_EQ(uint32_t(4), iter->key());
ASSERT_EQ(uint32_t(0x12345678), iter->value().data);
ASSERT_EQ(Res_value::TYPE_STRING, iter->value().dataType);
+ ++iter;
+
+ ASSERT_EQ(uint32_t(5), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
+
+ ++iter;
+
+ ASSERT_EQ(uint32_t(6), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
+
+ ++iter;
+
+ ASSERT_EQ(uint32_t(7), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
+
iter++;
- ASSERT_EQ(v.endEntries(), iter);
+ ASSERT_EQ(uint32_t(8), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(iter->is_compact(), compact_entry);
+ ASSERT_EQ(uint32_t(8), iter->key());
+ ASSERT_EQ(uint32_t(0x87654321), iter->value().data);
+ ASSERT_EQ(Res_value::TYPE_STRING, iter->value().dataType);
- free(data);
+ ++iter;
+
+ ASSERT_EQ(v.endEntries(), iter);
}
}
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index 8af4b7e8f4c8..4fecf4de0312 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -59,7 +59,6 @@ cc_library {
apex_available: [
"//apex_available:platform",
"com.android.os.statsd",
- "test_com.android.os.statsd",
"com.android.uprobestats",
],
}
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index 0c2f3adc2838..023bad26e4f5 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -1459,6 +1459,13 @@ package android.location.provider {
field public static final String ACTION_GEOCODE_PROVIDER = "com.android.location.service.GeocodeProvider";
}
+ @FlaggedApi("android.location.flags.gnss_assistance_interface") public abstract class GnssAssistanceProviderBase {
+ ctor public GnssAssistanceProviderBase(@NonNull android.content.Context, @NonNull String);
+ method @NonNull public final android.os.IBinder getBinder();
+ method public abstract void onRequest(@NonNull android.os.OutcomeReceiver<android.location.GnssAssistance,java.lang.Throwable>);
+ field public static final String ACTION_GNSS_ASSISTANCE_PROVIDER = "android.location.provider.action.GNSS_ASSISTANCE_PROVIDER";
+ }
+
public abstract class LocationProviderBase {
ctor public LocationProviderBase(@NonNull android.content.Context, @NonNull String, @NonNull android.location.provider.ProviderProperties);
method @Nullable public final android.os.IBinder getBinder();
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index c02cc808d60c..1b38982f48c1 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -167,4 +167,4 @@ flag {
namespace: "location"
description: "Flag for GNSS assistance interface"
bug: "209078566"
-} \ No newline at end of file
+}
diff --git a/location/java/android/location/provider/GnssAssistanceProviderBase.java b/location/java/android/location/provider/GnssAssistanceProviderBase.java
new file mode 100644
index 000000000000..f4b26d5033a5
--- /dev/null
+++ b/location/java/android/location/provider/GnssAssistanceProviderBase.java
@@ -0,0 +1,152 @@
+/*
+ * 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.location.provider;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.Intent;
+import android.location.GnssAssistance;
+import android.location.flags.Flags;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+
+/**
+ * Base class for GNSS assistance providers outside the system server.
+ *
+ * <p>GNSS assistance providers should be wrapped in a non-exported service which returns the result
+ * of {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The
+ * service should not be exported so that components other than the system server cannot bind to it.
+ * Alternatively, the service may be guarded by a permission that only system server can obtain. The
+ * service may specify metadata on its capabilities:
+ *
+ * <ul>
+ * <li>"serviceVersion": An integer version code to help tie break if multiple services are
+ * capable of implementing the geocode provider. All else equal, the service with the highest
+ * version code will be chosen. Assumed to be 0 if not specified.
+ * <li>"serviceIsMultiuser": A boolean property, indicating if the service wishes to take
+ * responsibility for handling changes to the current user on the device. If true, the service
+ * will always be bound from the system user. If false, the service will always be bound from
+ * the current user. If the current user changes, the old binding will be released, and a new
+ * binding established under the new user. Assumed to be false if not specified.
+ * </ul>
+ *
+ * <p>The service should have an intent filter in place for the GNSS assistance provider as
+ * specified by the constant in this class.
+ *
+ * <p>GNSS assistance providers are identified by their UID / package name / attribution tag. Based
+ * on this identity, geocode providers may be given some special privileges.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_GNSS_ASSISTANCE_INTERFACE)
+@SystemApi
+public abstract class GnssAssistanceProviderBase {
+
+ /**
+ * The action the wrapping service should have in its intent filter to implement the GNSS
+ * Assistance provider.
+ */
+ public static final String ACTION_GNSS_ASSISTANCE_PROVIDER =
+ "android.location.provider.action.GNSS_ASSISTANCE_PROVIDER";
+
+ final String mTag;
+ @Nullable
+ final String mAttributionTag;
+ final IBinder mBinder;
+
+ /**
+ * Subclasses should pass in a context and an arbitrary tag that may be used for logcat logging
+ * of errors, and thus should uniquely identify the class.
+ */
+ public GnssAssistanceProviderBase(@NonNull Context context, @NonNull String tag) {
+ mTag = tag;
+ mAttributionTag = context.getAttributionTag();
+ mBinder = new GnssAssistanceProviderBase.Service();
+ }
+
+ /**
+ * Returns the IBinder instance that should be returned from the {@link
+ * android.app.Service#onBind(Intent)} method of the wrapping service.
+ */
+ @NonNull
+ public final IBinder getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Requests GNSS assistance data of the given arguments. The given callback must be invoked
+ * once.
+ */
+ public abstract void onRequest(
+ @NonNull OutcomeReceiver<GnssAssistance, Throwable> callback);
+
+ private class Service extends IGnssAssistanceProvider.Stub {
+ @Override
+ public void request(IGnssAssistanceCallback callback) {
+ try {
+ onRequest(new GnssAssistanceProviderBase.SingleUseCallback(callback));
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(mTag, e);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
+ }
+
+ private static class SingleUseCallback implements
+ OutcomeReceiver<GnssAssistance, Throwable> {
+
+ private final AtomicReference<IGnssAssistanceCallback> mCallback;
+
+ SingleUseCallback(IGnssAssistanceCallback callback) {
+ mCallback = new AtomicReference<>(callback);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onError();
+ } catch (RemoteException r) {
+ throw r.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onResult(GnssAssistance result) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onResult(result);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/provider/IGnssAssistanceCallback.aidl b/location/java/android/location/provider/IGnssAssistanceCallback.aidl
new file mode 100644
index 000000000000..ea38d08df6c2
--- /dev/null
+++ b/location/java/android/location/provider/IGnssAssistanceCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.location.GnssAssistance;
+
+/**
+ * Binder interface for GNSS assistance callbacks.
+ * @hide
+ */
+oneway interface IGnssAssistanceCallback {
+ void onError();
+ void onResult(in GnssAssistance result);
+}
diff --git a/location/java/android/location/provider/IGnssAssistanceProvider.aidl b/location/java/android/location/provider/IGnssAssistanceProvider.aidl
new file mode 100644
index 000000000000..1796e9edb347
--- /dev/null
+++ b/location/java/android/location/provider/IGnssAssistanceProvider.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.location.provider.IGnssAssistanceCallback;
+
+/**
+ * Binder interface for services that implement GNSS assistance providers. Do not implement this
+ * directly, extend {@link GnssAssistanceProviderBase} instead.
+ * @hide
+ */
+oneway interface IGnssAssistanceProvider {
+ void request(in IGnssAssistanceCallback callback);
+}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 60584d9c6f72..f42017dc835a 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -226,6 +226,10 @@ public abstract class MediaRoute2ProviderService extends Service {
@GuardedBy("mSessionLock")
private final ArrayMap<String, MediaStreams> mOngoingMediaStreams = new ArrayMap<>();
+ @GuardedBy("mSessionLock")
+ private final ArrayMap<String, RoutingSessionInfo> mPendingSystemSessionReleases =
+ new ArrayMap<>();
+
public MediaRoute2ProviderService() {
mHandler = new Handler(Looper.getMainLooper());
}
@@ -419,7 +423,7 @@ public abstract class MediaRoute2ProviderService extends Service {
}
AudioFormat audioFormat = formats.mAudioFormat;
- var mediaStreamsBuilder = new MediaStreams.Builder();
+ var mediaStreamsBuilder = new MediaStreams.Builder(sessionInfo);
if (audioFormat != null) {
populateAudioStream(audioFormat, uid, mediaStreamsBuilder);
}
@@ -526,8 +530,14 @@ public abstract class MediaRoute2ProviderService extends Service {
RoutingSessionInfo sessionInfo;
synchronized (mSessionLock) {
sessionInfo = mSessionInfos.remove(sessionId);
- maybeReleaseMediaStreams(sessionId);
-
+ if (Flags.enableMirroringInMediaRouter2()) {
+ if (sessionInfo == null) {
+ sessionInfo = maybeReleaseMediaStreams(sessionId);
+ }
+ if (sessionInfo == null) {
+ sessionInfo = mPendingSystemSessionReleases.remove(sessionId);
+ }
+ }
if (sessionInfo == null) {
Log.w(TAG, "notifySessionReleased: Ignoring unknown session info.");
return;
@@ -544,20 +554,26 @@ public abstract class MediaRoute2ProviderService extends Service {
}
}
- /** Releases any system media routing resources associated with the given {@code sessionId}. */
- private boolean maybeReleaseMediaStreams(String sessionId) {
+ /**
+ * Releases any system media routing resources associated with the given {@code sessionId}.
+ *
+ * @return The {@link RoutingSessionInfo} that corresponds to the released media streams, or
+ * null if no streams were released.
+ */
+ @Nullable
+ private RoutingSessionInfo maybeReleaseMediaStreams(String sessionId) {
if (!Flags.enableMirroringInMediaRouter2()) {
- return false;
+ return null;
}
synchronized (mSessionLock) {
var streams = mOngoingMediaStreams.remove(sessionId);
if (streams != null) {
releaseAudioStream(streams.mAudioPolicy, streams.mAudioRecord);
// TODO: b/380431086: Release the video stream once implemented.
- return true;
+ return streams.mSessionInfo;
}
}
- return false;
+ return null;
}
// We cannot reach the code that requires MODIFY_AUDIO_ROUTING without holding it.
@@ -1026,11 +1042,15 @@ public abstract class MediaRoute2ProviderService extends Service {
if (!checkCallerIsSystem()) {
return;
}
- // We proactively release the system media routing once the system requests it, to
- // ensure it happens immediately.
- if (!maybeReleaseMediaStreams(sessionId)
- && !checkSessionIdIsValid(sessionId, "releaseSession")) {
- return;
+ synchronized (mSessionLock) {
+ // We proactively release the system media routing session resources when the
+ // system requests it, to ensure it happens immediately.
+ RoutingSessionInfo releasedSession = maybeReleaseMediaStreams(sessionId);
+ if (releasedSession != null) {
+ mPendingSystemSessionReleases.put(sessionId, releasedSession);
+ } else if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
+ return;
+ }
}
addRequestId(requestId);
@@ -1054,9 +1074,19 @@ public abstract class MediaRoute2ProviderService extends Service {
@Nullable private final AudioPolicy mAudioPolicy;
@Nullable private final AudioRecord mAudioRecord;
+ /**
+ * Holds the last {@link RoutingSessionInfo} associated with these streams.
+ *
+ * @hide
+ */
+ @GuardedBy("MediaRoute2ProviderService.this.mSessionLock")
+ @NonNull
+ private RoutingSessionInfo mSessionInfo;
+
// TODO: b/380431086: Add the video equivalent.
private MediaStreams(Builder builder) {
+ this.mSessionInfo = builder.mSessionInfo;
this.mAudioPolicy = builder.mAudioPolicy;
this.mAudioRecord = builder.mAudioRecord;
}
@@ -1077,9 +1107,19 @@ public abstract class MediaRoute2ProviderService extends Service {
*/
public static final class Builder {
+ @NonNull private RoutingSessionInfo mSessionInfo;
@Nullable private AudioPolicy mAudioPolicy;
@Nullable private AudioRecord mAudioRecord;
+ /**
+ * Constructor.
+ *
+ * @param sessionInfo The {@link RoutingSessionInfo} associated with these streams.
+ */
+ Builder(@NonNull RoutingSessionInfo sessionInfo) {
+ mSessionInfo = requireNonNull(sessionInfo);
+ }
+
/** Populates system media audio-related structures. */
public Builder setAudioStream(
@NonNull AudioPolicy audioPolicy, @NonNull AudioRecord audioRecord) {
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index 31e1eb36ad8d..70f5bb32a5d5 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -2074,15 +2074,19 @@ public class PackageWatchdog {
bootMitigationCounts.put(observer.name, observer.getBootMitigationCount());
}
+ FileOutputStream fileStream = null;
+ ObjectOutputStream objectStream = null;
try {
- FileOutputStream fileStream = new FileOutputStream(new File(filePath));
- ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
+ fileStream = new FileOutputStream(new File(filePath));
+ objectStream = new ObjectOutputStream(fileStream);
objectStream.writeObject(bootMitigationCounts);
objectStream.flush();
- objectStream.close();
- fileStream.close();
} catch (Exception e) {
Slog.i(TAG, "Could not save observers metadata to file: " + e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(objectStream);
+ IoUtils.closeQuietly(fileStream);
}
}
@@ -2233,23 +2237,32 @@ public class PackageWatchdog {
void readAllObserversBootMitigationCountIfNecessary(String filePath) {
File metadataFile = new File(filePath);
if (metadataFile.exists()) {
+ FileInputStream fileStream = null;
+ ObjectInputStream objectStream = null;
+ HashMap<String, Integer> bootMitigationCounts = null;
try {
- FileInputStream fileStream = new FileInputStream(metadataFile);
- ObjectInputStream objectStream = new ObjectInputStream(fileStream);
- HashMap<String, Integer> bootMitigationCounts =
+ fileStream = new FileInputStream(metadataFile);
+ objectStream = new ObjectInputStream(fileStream);
+ bootMitigationCounts =
(HashMap<String, Integer>) objectStream.readObject();
- objectStream.close();
- fileStream.close();
-
- for (int i = 0; i < mAllObservers.size(); i++) {
- final ObserverInternal observer = mAllObservers.valueAt(i);
- if (bootMitigationCounts.containsKey(observer.name)) {
- observer.setBootMitigationCount(
- bootMitigationCounts.get(observer.name));
- }
- }
} catch (Exception e) {
Slog.i(TAG, "Could not read observer metadata file: " + e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(objectStream);
+ IoUtils.closeQuietly(fileStream);
+ }
+
+ if (bootMitigationCounts == null || bootMitigationCounts.isEmpty()) {
+ Slog.i(TAG, "No observer in metadata file");
+ return;
+ }
+ for (int i = 0; i < mAllObservers.size(); i++) {
+ final ObserverInternal observer = mAllObservers.valueAt(i);
+ if (bootMitigationCounts.containsKey(observer.name)) {
+ observer.setBootMitigationCount(
+ bootMitigationCounts.get(observer.name));
+ }
}
}
}
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index ffae5176cebf..ac815f8aca85 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -2021,15 +2021,19 @@ public class PackageWatchdog {
bootMitigationCounts.put(observer.name, observer.getBootMitigationCount());
}
+ FileOutputStream fileStream = null;
+ ObjectOutputStream objectStream = null;
try {
- FileOutputStream fileStream = new FileOutputStream(new File(filePath));
- ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
+ fileStream = new FileOutputStream(new File(filePath));
+ objectStream = new ObjectOutputStream(fileStream);
objectStream.writeObject(bootMitigationCounts);
objectStream.flush();
- objectStream.close();
- fileStream.close();
} catch (Exception e) {
Slog.i(TAG, "Could not save observers metadata to file: " + e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(objectStream);
+ IoUtils.closeQuietly(fileStream);
}
}
@@ -2180,23 +2184,32 @@ public class PackageWatchdog {
void readAllObserversBootMitigationCountIfNecessary(String filePath) {
File metadataFile = new File(filePath);
if (metadataFile.exists()) {
+ FileInputStream fileStream = null;
+ ObjectInputStream objectStream = null;
+ HashMap<String, Integer> bootMitigationCounts = null;
try {
- FileInputStream fileStream = new FileInputStream(metadataFile);
- ObjectInputStream objectStream = new ObjectInputStream(fileStream);
- HashMap<String, Integer> bootMitigationCounts =
+ fileStream = new FileInputStream(metadataFile);
+ objectStream = new ObjectInputStream(fileStream);
+ bootMitigationCounts =
(HashMap<String, Integer>) objectStream.readObject();
- objectStream.close();
- fileStream.close();
-
- for (int i = 0; i < mAllObservers.size(); i++) {
- final ObserverInternal observer = mAllObservers.valueAt(i);
- if (bootMitigationCounts.containsKey(observer.name)) {
- observer.setBootMitigationCount(
- bootMitigationCounts.get(observer.name));
- }
- }
} catch (Exception e) {
Slog.i(TAG, "Could not read observer metadata file: " + e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(objectStream);
+ IoUtils.closeQuietly(fileStream);
+ }
+
+ if (bootMitigationCounts == null || bootMitigationCounts.isEmpty()) {
+ Slog.i(TAG, "No observer in metadata file");
+ return;
+ }
+ for (int i = 0; i < mAllObservers.size(); i++) {
+ final ObserverInternal observer = mAllObservers.valueAt(i);
+ if (bootMitigationCounts.containsKey(observer.name)) {
+ observer.setBootMitigationCount(
+ bootMitigationCounts.get(observer.name));
+ }
}
}
}
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
index 03a2101544be..218983a55e1b 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
@@ -31,7 +31,6 @@ import androidx.preference.CheckBoxPreference;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.widget.preference.selector.R;
-import com.android.settingslib.widget.selectorwithwidgetpreference.flags.Flags;
/**
* Selector preference (checkbox or radio button) with an optional additional widget.
@@ -180,10 +179,8 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
: getContext().getString(R.string.settings_label));
}
- if (Flags.allowSetTitleMaxLines()) {
- TextView title = (TextView) holder.findViewById(android.R.id.title);
- title.setMaxLines(mTitleMaxLines);
- }
+ TextView title = (TextView) holder.findViewById(android.R.id.title);
+ title.setMaxLines(mTitleMaxLines);
}
/**
@@ -244,16 +241,12 @@ public class SelectorWithWidgetPreference extends CheckBoxPreference {
setLayoutResource(R.layout.preference_selector_with_widget);
setIconSpaceReserved(false);
- if (Flags.allowSetTitleMaxLines()) {
- final TypedArray a =
- context.obtainStyledAttributes(
- attrs, R.styleable.SelectorWithWidgetPreference, defStyleAttr,
- defStyleRes);
- mTitleMaxLines =
- a.getInt(R.styleable.SelectorWithWidgetPreference_titleMaxLines,
- DEFAULT_MAX_LINES);
- a.recycle();
- }
+ final TypedArray a =
+ context.obtainStyledAttributes(
+ attrs, R.styleable.SelectorWithWidgetPreference, defStyleAttr, defStyleRes);
+ mTitleMaxLines =
+ a.getInt(R.styleable.SelectorWithWidgetPreference_titleMaxLines, DEFAULT_MAX_LINES);
+ a.recycle();
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 1a043d5015b2..d82b58ec1358 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -97,6 +97,7 @@ flag {
name: "settings_catalyst"
namespace: "android_settings"
description: "Settings catalyst project migration"
+ is_exported: true
bug: "323791114"
is_exported: true
}
@@ -106,6 +107,7 @@ flag {
is_fixed_read_only: true
namespace: "android_settings"
description: "Enable WRITE_SYSTEM_PREFERENCE permission and appop"
+ is_exported: true
bug: "375193223"
is_exported: true
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
index 2b8b3b74dab9..c939c770b63d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
@@ -21,9 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import android.app.Application;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -33,10 +30,8 @@ import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.widget.preference.selector.R;
-import com.android.settingslib.widget.selectorwithwidgetpreference.flags.Flags;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
@@ -45,7 +40,6 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class SelectorWithWidgetPreferenceTest {
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Application mContext;
private SelectorWithWidgetPreference mPreference;
@@ -128,26 +122,6 @@ public class SelectorWithWidgetPreferenceTest {
}
@Test
- @DisableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
- public void onBindViewHolder_titleMaxLinesSet_flagOff_titleMaxLinesMatchesDefault() {
- final int titleMaxLines = 5;
- AttributeSet attributeSet = Robolectric.buildAttributeSet()
- .addAttribute(R.attr.titleMaxLines, String.valueOf(titleMaxLines))
- .build();
- mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
- View view = LayoutInflater.from(mContext)
- .inflate(mPreference.getLayoutResource(), null /* root */);
- PreferenceViewHolder preferenceViewHolder =
- PreferenceViewHolder.createInstanceForTests(view);
-
- mPreference.onBindViewHolder(preferenceViewHolder);
-
- TextView title = (TextView) preferenceViewHolder.findViewById(android.R.id.title);
- assertThat(title.getMaxLines()).isEqualTo(SelectorWithWidgetPreference.DEFAULT_MAX_LINES);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
public void onBindViewHolder_noTitleMaxLinesSet_titleMaxLinesMatchesDefault() {
AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
@@ -163,7 +137,6 @@ public class SelectorWithWidgetPreferenceTest {
}
@Test
- @EnableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
public void onBindViewHolder_titleMaxLinesSet_titleMaxLinesUpdated() {
final int titleMaxLines = 5;
AttributeSet attributeSet = Robolectric.buildAttributeSet()
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 91ac34ac8233..de7c450d8d39 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -148,27 +148,7 @@ public final class DeviceConfigService extends Binder {
// TODO(b/364399200): use filter to skip instead?
return;
}
-
- ArrayList<String> missingFiles = new ArrayList<String>();
- for (String fileName : sAconfigTextProtoFilesOnDevice) {
- File aconfigFile = new File(fileName);
- if (!aconfigFile.exists()) {
- missingFiles.add(fileName);
- }
- }
-
- if (missingFiles.isEmpty()) {
- pw.println("\nAconfig flags:");
- for (String name : MyShellCommand.listAllAconfigFlags(iprovider)) {
- pw.println(name);
- }
- } else {
- pw.println("\nFailed to dump aconfig flags due to missing files:");
- for (String fileName : missingFiles) {
- pw.println(fileName);
- }
- }
- }
+ }
private static HashSet<String> getAconfigFlagNamesInDeviceConfig() {
HashSet<String> nameSet = new HashSet<String>();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 1c4def39eaa0..e01cb84f60ae 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -16,6 +16,19 @@
package com.android.providers.settings;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_DEVICE_SPECIFIC_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_GLOBAL;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_LOCALE;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_LOCK_SETTINGS;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_NETWORK_POLICIES;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SECURE;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SIM_SPECIFIC_SETTINGS;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SIM_SPECIFIC_SETTINGS_2;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SYSTEM;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_NEW_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_SETTINGS_BACKUP_DATA;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -99,22 +112,6 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private static final int NULL_SIZE = -1;
private static final float FONT_SCALE_DEF_VALUE = 1.0f;
- private static final String KEY_SYSTEM = "system";
- private static final String KEY_SECURE = "secure";
- private static final String KEY_GLOBAL = "global";
- private static final String KEY_LOCALE = "locale";
- private static final String KEY_LOCK_SETTINGS = "lock_settings";
- private static final String KEY_SOFTAP_CONFIG = "softap_config";
- private static final String KEY_NETWORK_POLICIES = "network_policies";
- private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
- private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
- private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
- // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
- // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
- // restoring this data.
- private static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
- private static final String KEY_WIFI_SETTINGS_BACKUP_DATA = "wifi_settings_backup_data";
-
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
private static final int STATE_VERSION = 9;
@@ -257,6 +254,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (com.android.server.backup.Flags.enableMetricsSettingsBackupAgents()) {
mBackupRestoreEventLogger = this.getBackupRestoreEventLogger();
+ mSettingsHelper.setBackupRestoreEventLogger(mBackupRestoreEventLogger);
numberOfSettingsPerKey = new HashMap<>();
areAgentMetricsEnabled = true;
}
@@ -412,9 +410,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
mSettingsHelper
.setLocaleData(
localeData,
- size,
- mBackupRestoreEventLogger,
- KEY_LOCALE);
+ size);
break;
case KEY_WIFI_CONFIG :
@@ -552,8 +548,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
if (nBytes > buffer.length) buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
mSettingsHelper
- .setLocaleData(
- buffer, nBytes, mBackupRestoreEventLogger, KEY_LOCALE);
+ .setLocaleData(buffer, nBytes);
// Restore older backups performing the necessary migrations.
if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java
new file mode 100644
index 000000000000..745c2fb5409d
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java
@@ -0,0 +1,61 @@
+/*
+ * 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.providers.settings;
+
+import android.net.Uri;
+import android.provider.Settings;
+
+/**
+ * Class to store the keys used for backup and restore.
+ */
+final class SettingsBackupRestoreKeys {
+ static final String KEY_UNKNOWN = "unknown";
+ static final String KEY_SYSTEM = "system";
+ static final String KEY_SECURE = "secure";
+ static final String KEY_GLOBAL = "global";
+ static final String KEY_LOCALE = "locale";
+ static final String KEY_LOCK_SETTINGS = "lock_settings";
+ static final String KEY_SOFTAP_CONFIG = "softap_config";
+ static final String KEY_NETWORK_POLICIES = "network_policies";
+ static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
+ static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+ static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
+ // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
+ // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
+ // restoring this data.
+ static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
+ static final String KEY_WIFI_SETTINGS_BACKUP_DATA = "wifi_settings_backup_data";
+
+ /**
+ * Returns the key corresponding to the given URI.
+ *
+ * @param uri The URI of the setting's destination.
+ * @return The key corresponding to the given URI, or KEY_UNKNOWN if the URI is not recognized.
+ */
+ static String getKeyFromUri(Uri uri) {
+ if (uri.equals(Settings.Secure.CONTENT_URI)) {
+ return KEY_SECURE;
+ } else if (uri.equals(Settings.System.CONTENT_URI)) {
+ return KEY_SYSTEM;
+ } else if (uri.equals(Settings.Global.CONTENT_URI)) {
+ return KEY_GLOBAL;
+ } else {
+ return KEY_UNKNOWN;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 924c151a99a0..ab8d739feb43 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -17,6 +17,7 @@
package com.android.providers.settings;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.backup.BackupRestoreEventLogger;
@@ -84,6 +85,7 @@ public class SettingsHelper {
private Context mContext;
private AudioManager mAudioManager;
private TelephonyManager mTelephonyManager;
+ @Nullable private BackupRestoreEventLogger mBackupRestoreEventLogger;
/**
* A few settings elements are special in that a restore of those values needs to
@@ -741,11 +743,8 @@ public class SettingsHelper {
*
* @param data the comma separated BCP-47 language tags in bytes.
* @param size the size of the data in bytes.
- * @param backupRestoreEventLogger the logger to log the restore event.
- * @param dataType the data type of the setting for logging purposes.
*/
- /* package */ void setLocaleData(
- byte[] data, int size, BackupRestoreEventLogger backupRestoreEventLogger, String dataType) {
+ /* package */ void setLocaleData(byte[] data, int size) {
final Configuration conf = mContext.getResources().getConfiguration();
// Replace "_" with "-" to deal with older backups.
@@ -772,15 +771,15 @@ public class SettingsHelper {
am.updatePersistentConfigurationWithAttribution(config, mContext.getOpPackageName(),
mContext.getAttributionTag());
- if (Flags.enableMetricsSettingsBackupAgents()) {
- backupRestoreEventLogger
- .logItemsRestored(dataType, localeList.size());
+ if (Flags.enableMetricsSettingsBackupAgents() && mBackupRestoreEventLogger != null) {
+ mBackupRestoreEventLogger
+ .logItemsRestored(SettingsBackupRestoreKeys.KEY_LOCALE, localeList.size());
}
} catch (RemoteException e) {
- if (Flags.enableMetricsSettingsBackupAgents()) {
- backupRestoreEventLogger
+ if (Flags.enableMetricsSettingsBackupAgents() && mBackupRestoreEventLogger != null) {
+ mBackupRestoreEventLogger
.logItemsRestoreFailed(
- dataType,
+ SettingsBackupRestoreKeys.KEY_LOCALE,
localeList.size(),
ERROR_REMOTE_EXCEPTION_SETTING_LOCALE_DATA);
}
@@ -795,4 +794,13 @@ public class SettingsHelper {
AudioManager am = new AudioManager(mContext);
am.reloadAudioSettings();
}
+
+ /**
+ * Sets the backup restore event logger.
+ *
+ * @param backupRestoreEventLogger the logger to log B&R metrics.
+ */
+ void setBackupRestoreEventLogger(BackupRestoreEventLogger backupRestoreEventLogger) {
+ mBackupRestoreEventLogger = backupRestoreEventLogger;
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 55f48e3e367f..f1f03c31f718 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -120,6 +120,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
@@ -2914,6 +2915,14 @@ public class SettingsProvider extends ContentProvider {
};
}
+ @VisibleForTesting
+ void injectServices(UserManager userManager, IPackageManager packageManager,
+ SystemConfigManager sysConfigManager) {
+ mUserManager = userManager;
+ mPackageManager = packageManager;
+ mSysConfigManager = sysConfigManager;
+ }
+
private static final class Arguments {
private static final Pattern WHERE_PATTERN_WITH_PARAM_NO_BRACKETS =
Pattern.compile("[\\s]*name[\\s]*=[\\s]*\\?[\\s]*");
@@ -3080,6 +3089,7 @@ public class SettingsProvider extends ContentProvider {
private static final String SSAID_USER_KEY = "userkey";
+ @GuardedBy("mLock")
private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();
private GenerationRegistry mGenerationRegistry;
@@ -3992,6 +4002,14 @@ public class SettingsProvider extends ContentProvider {
}
}
+ @VisibleForTesting
+ void injectSettings(SettingsState settings, int type, int userId) {
+ int key = makeKey(type, userId);
+ synchronized (mLock) {
+ mSettingsStates.put(key, settings);
+ }
+ }
+
private final class MyHandler extends Handler {
private static final int MSG_NOTIFY_URI_CHANGED = 1;
private static final int MSG_NOTIFY_DATA_CHANGED = 2;
@@ -4023,12 +4041,21 @@ public class SettingsProvider extends ContentProvider {
}
}
- private final class UpgradeController {
+ @VisibleForTesting
+ final class UpgradeController {
private static final int SETTINGS_VERSION = 226;
private final int mUserId;
+ private final Injector mInjector;
+
public UpgradeController(int userId) {
+ this(/* injector= */ null, userId);
+ }
+
+ @VisibleForTesting
+ UpgradeController(Injector injector, int userId) {
+ mInjector = injector == null ? new Injector() : injector;
mUserId = userId;
}
@@ -6136,8 +6163,8 @@ public class SettingsProvider extends ContentProvider {
systemSettings.getSettingLocked(Settings.System.PEAK_REFRESH_RATE);
final Setting minRefreshRateSetting =
systemSettings.getSettingLocked(Settings.System.MIN_REFRESH_RATE);
- float highestRefreshRate = RefreshRateSettingsUtils
- .findHighestRefreshRateForDefaultDisplay(getContext());
+ float highestRefreshRate =
+ mInjector.findHighestRefreshRateForDefaultDisplay(getContext());
if (!peakRefreshRateSetting.isNull()) {
try {
@@ -6318,6 +6345,14 @@ public class SettingsProvider extends ContentProvider {
private long getBitMask(int capability) {
return 1 << (capability - 1);
}
+
+ @VisibleForTesting
+ static class Injector {
+ float findHighestRefreshRateForDefaultDisplay(Context context) {
+ return RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(
+ context);
+ }
+ }
}
/**
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 5cd534e62ea9..bf3afeda448e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -107,7 +107,7 @@ import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
* the same lock to grab the current state to write to disk.
* </p>
*/
-final class SettingsState {
+public class SettingsState {
private static final boolean DEBUG = false;
private static final boolean DEBUG_PERSISTENCE = false;
@@ -1838,7 +1838,7 @@ final class SettingsState {
}
}
- class Setting {
+ public class Setting {
private String name;
private String value;
private String defaultValue;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java
new file mode 100644
index 000000000000..ef537e8c7fc0
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.providers.settings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+import android.provider.Settings;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link SettingsBackupRestoreKeys}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SettingsBackupRestoreKeysTest {
+
+ @Test
+ public void getKeyFromUri_secureUri_returnsSecureKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.Secure.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_SECURE);
+ }
+
+ @Test
+ public void getKeyFromUri_systemUri_returnsSystemKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.System.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_SYSTEM);
+ }
+
+ @Test
+ public void getKeyFromUri_globalUri_returnsGlobalKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.Global.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_GLOBAL);
+ }
+
+ @Test
+ public void getKeyFromUri_unknownUri_returnsUnknownKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Uri.parse("content://unknown")))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_UNKNOWN);
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java
new file mode 100644
index 000000000000..26ff376f828e
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.providers.settings;
+
+import static android.provider.Settings.System.MIN_REFRESH_RATE;
+import static android.provider.Settings.System.PEAK_REFRESH_RATE;
+
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_GLOBAL;
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_SECURE;
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_SYSTEM;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+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 android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.IPackageManager;
+import android.os.Looper;
+import android.os.SystemConfigManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class UpgradeControllerTest {
+ private static final int USER_ID = UserHandle.USER_SYSTEM;
+ private static final float HIGHEST_REFRESH_RATE = 130f;
+
+ private final Context mContext =
+ spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ private final SettingsProvider.SettingsRegistry.UpgradeController.Injector mInjector =
+ new SettingsProvider.SettingsRegistry.UpgradeController.Injector() {
+ @Override
+ float findHighestRefreshRateForDefaultDisplay(Context context) {
+ return HIGHEST_REFRESH_RATE;
+ }
+ };
+ private final SettingsProvider mSettingsProvider = new SettingsProvider() {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+ };
+ private final SettingsProvider.SettingsRegistry mSettingsRegistry =
+ mSettingsProvider.new SettingsRegistry(Looper.getMainLooper());
+ private final SettingsProvider.SettingsRegistry.UpgradeController mUpgradeController =
+ mSettingsRegistry.new UpgradeController(mInjector, USER_ID);
+
+ @Mock
+ private UserManager mUserManager;
+
+ @Mock
+ private IPackageManager mPackageManager;
+
+ @Mock
+ private SystemConfigManager mSysConfigManager;
+
+ @Mock
+ private SettingsState mSystemSettings;
+
+ @Mock
+ private SettingsState mSecureSettings;
+
+ @Mock
+ private SettingsState mGlobalSettings;
+
+ @Mock
+ private SettingsState.Setting mMockSetting;
+
+ @Mock
+ private SettingsState.Setting mPeakRefreshRateSetting;
+
+ @Mock
+ private SettingsState.Setting mMinRefreshRateSetting;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSettingsProvider.attachInfoForTesting(mContext, /* info= */ null);
+ mSettingsProvider.injectServices(mUserManager, mPackageManager, mSysConfigManager);
+ when(mSystemSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mSecureSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mGlobalSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mMockSetting.isNull()).thenReturn(true);
+ when(mMockSetting.getValue()).thenReturn("0");
+
+ when(mSystemSettings.getSettingLocked(PEAK_REFRESH_RATE))
+ .thenReturn(mPeakRefreshRateSetting);
+ when(mSystemSettings.getSettingLocked(MIN_REFRESH_RATE))
+ .thenReturn(mMinRefreshRateSetting);
+
+ mSettingsRegistry.injectSettings(mSystemSettings, SETTINGS_TYPE_SYSTEM, USER_ID);
+ mSettingsRegistry.injectSettings(mSecureSettings, SETTINGS_TYPE_SECURE, USER_ID);
+ mSettingsRegistry.injectSettings(mGlobalSettings, SETTINGS_TYPE_GLOBAL, USER_ID);
+
+ // Lowest version so that all upgrades are run
+ when(mSecureSettings.getVersionLocked()).thenReturn(118);
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_defaultValues() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(true);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(true);
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Should remain unchanged
+ verify(mSystemSettings, never()).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ verify(mSystemSettings, never()).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_enabled() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(false);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(false);
+ when(mPeakRefreshRateSetting.getValue()).thenReturn(String.valueOf(HIGHEST_REFRESH_RATE));
+ when(mMinRefreshRateSetting.getValue()).thenReturn(String.valueOf(HIGHEST_REFRESH_RATE));
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Highest refresh rate gets converted to infinity
+ verify(mSystemSettings).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ eq(String.valueOf(Float.POSITIVE_INFINITY)), /* tag= */ any(),
+ /* makeDefault= */ anyBoolean(), /* packageName= */ any());
+ verify(mSystemSettings).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ eq(String.valueOf(Float.POSITIVE_INFINITY)), /* tag= */ any(),
+ /* makeDefault= */ anyBoolean(), /* packageName= */ any());
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_disabled() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(false);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(false);
+ when(mPeakRefreshRateSetting.getValue()).thenReturn("70f");
+ when(mMinRefreshRateSetting.getValue()).thenReturn("70f");
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Should remain unchanged
+ verify(mSystemSettings, never()).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ verify(mSystemSettings, never()).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ }
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c4d13ba6effa..5ff2d1b07347 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -496,6 +496,13 @@ flag {
}
flag {
+ name: "status_bar_popup_chips"
+ namespace: "systemui"
+ description: "Show rich ongoing processes as chips in the status bar"
+ bug: "372964148"
+}
+
+flag {
name: "promote_notifications_automatically"
namespace: "systemui"
description: "Flag to automatically turn certain notifications into promoted notifications so "
@@ -1237,6 +1244,13 @@ flag {
}
flag {
+ name: "glanceable_hub_back_action"
+ namespace: "systemui"
+ description: "Support back action from glanceable hub"
+ bug: "382771533"
+}
+
+flag {
name: "dream_overlay_updated_font"
namespace: "systemui"
description: "Flag to enable updated font settings for dream overlay"
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
index ca2b9578f2be..7d27a562f536 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -195,7 +195,10 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
// Create the origin leash and add to the transition root leash.
mOriginLeash =
new SurfaceControl.Builder().setName("OriginTransition-origin-leash").build();
- mStartTransaction
+
+ // Create temporary transaction to build
+ final SurfaceControl.Transaction tmpTransaction = new SurfaceControl.Transaction();
+ tmpTransaction
.reparent(mOriginLeash, rootLeash)
.show(mOriginLeash)
.setCornerRadius(mOriginLeash, windowRadius)
@@ -208,14 +211,14 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
int mode = change.getMode();
SurfaceControl leash = change.getLeash();
// Reparent leash to the transition root.
- mStartTransaction.reparent(leash, rootLeash);
+ tmpTransaction.reparent(leash, rootLeash);
if (TransitionUtil.isOpeningMode(mode)) {
openingSurfaces.add(change.getLeash());
// For opening surfaces, ending bounds are base bound. Apply corner radius if
// it's full screen.
Rect bounds = change.getEndAbsBounds();
if (displayBounds.equals(bounds)) {
- mStartTransaction
+ tmpTransaction
.setCornerRadius(leash, windowRadius)
.setWindowCrop(leash, bounds.width(), bounds.height());
}
@@ -226,28 +229,53 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
// it's full screen.
Rect bounds = change.getStartAbsBounds();
if (displayBounds.equals(bounds)) {
- mStartTransaction
+ tmpTransaction
.setCornerRadius(leash, windowRadius)
.setWindowCrop(leash, bounds.width(), bounds.height());
}
}
}
+ if (openingSurfaces.isEmpty() && closingSurfaces.isEmpty()) {
+ logD("prepareUIs: no opening/closing surfaces available, nothing to prepare.");
+ return false;
+ }
+
// Set relative order:
// ---- App1 ----
// ---- origin ----
// ---- App2 ----
+
if (mIsEntry) {
- mStartTransaction
- .setRelativeLayer(mOriginLeash, closingSurfaces.get(0), 1)
- .setRelativeLayer(
- openingSurfaces.get(openingSurfaces.size() - 1), mOriginLeash, 1);
+ if (!closingSurfaces.isEmpty()) {
+ tmpTransaction
+ .setRelativeLayer(mOriginLeash, closingSurfaces.get(0), 1);
+ } else {
+ logW("Missing closing surface is entry transition");
+ }
+ if (!openingSurfaces.isEmpty()) {
+ tmpTransaction
+ .setRelativeLayer(
+ openingSurfaces.get(openingSurfaces.size() - 1), mOriginLeash, 1);
+ } else {
+ logW("Missing opening surface is entry transition");
+ }
+
} else {
- mStartTransaction
- .setRelativeLayer(mOriginLeash, openingSurfaces.get(0), 1)
- .setRelativeLayer(
- closingSurfaces.get(closingSurfaces.size() - 1), mOriginLeash, 1);
+ if (!openingSurfaces.isEmpty()) {
+ tmpTransaction
+ .setRelativeLayer(mOriginLeash, openingSurfaces.get(0), 1);
+ } else {
+ logW("Missing opening surface is exit transition");
+ }
+ if (!closingSurfaces.isEmpty()) {
+ tmpTransaction.setRelativeLayer(
+ closingSurfaces.get(closingSurfaces.size() - 1), mOriginLeash, 1);
+ } else {
+ logW("Missing closing surface is exit transition");
+ }
}
+ mStartTransaction.merge(tmpTransaction);
// Attach origin UIComponent to origin leash.
mOriginTransaction = mOrigin.newTransaction();
@@ -300,6 +328,7 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
}
private void cancel() {
+ logD("cancel()");
if (mAnimator != null) {
mAnimator.cancel();
}
@@ -311,6 +340,10 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
}
}
+ private static void logW(String msg) {
+ Log.w(TAG, msg);
+ }
+
private static void logE(String msg) {
Log.e(TAG, msg);
}
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 5dbedc7045e4..bf3360f0ea14 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
@@ -931,7 +931,9 @@ private fun BoxScope.CommunalHubLazyGrid(
Modifier.requiredSize(dpSize)
.thenIf(!isItemDragging) {
Modifier.animateItem(
- placementSpec = spring(stiffness = Spring.StiffnessMediumLow)
+ placementSpec = spring(stiffness = Spring.StiffnessMediumLow),
+ // See b/376495198 - not supported with AndroidView
+ fadeOutSpec = null,
)
}
.thenIf(isItemDragging) { Modifier.zIndex(1f) },
@@ -980,11 +982,14 @@ private fun BoxScope.CommunalHubLazyGrid(
size = size,
selected = false,
modifier =
- Modifier.requiredSize(dpSize).animateItem().thenIf(
- communalResponsiveGrid()
- ) {
- Modifier.graphicsLayer { alpha = itemAlpha?.value ?: 1f }
- },
+ Modifier.requiredSize(dpSize)
+ .animateItem(
+ // See b/376495198 - not supported with AndroidView
+ fadeOutSpec = null
+ )
+ .thenIf(communalResponsiveGrid()) {
+ Modifier.graphicsLayer { alpha = itemAlpha?.value ?: 1f }
+ },
index = index,
contentListState = contentListState,
interactionHandler = interactionHandler,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index db33e7c628d7..79cf24b9c547 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -35,9 +35,8 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
-import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.modifiers.thenIf
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
@@ -61,7 +60,7 @@ constructor(
private val clockInteractor: KeyguardClockInteractor,
) {
@Composable
- fun SceneScope.DefaultClockLayout(
+ fun ContentScope.DefaultClockLayout(
smartSpacePaddingTop: (Resources) -> Int,
isShadeLayoutWide: Boolean,
modifier: Modifier = Modifier,
@@ -95,7 +94,7 @@ constructor(
}
Column(modifier) {
- SceneTransitionLayout(state) {
+ NestedSceneTransitionLayout(state, Modifier) {
scene(splitShadeLargeClockScene) {
LargeClockWithSmartSpace(
smartSpacePaddingTop = smartSpacePaddingTop,
@@ -134,7 +133,7 @@ constructor(
}
@Composable
- private fun SceneScope.SmallClockWithSmartSpace(
+ private fun ContentScope.SmallClockWithSmartSpace(
smartSpacePaddingTop: (Resources) -> Int,
modifier: Modifier = Modifier,
) {
@@ -159,7 +158,7 @@ constructor(
}
@Composable
- private fun SceneScope.LargeClockWithSmartSpace(
+ private fun ContentScope.LargeClockWithSmartSpace(
smartSpacePaddingTop: (Resources) -> Int,
shouldOffSetClockToOneHalf: Boolean = false,
) {
@@ -200,7 +199,7 @@ constructor(
}
@Composable
- private fun SceneScope.WeatherLargeClockWithSmartSpace(
+ private fun ContentScope.WeatherLargeClockWithSmartSpace(
smartSpacePaddingTop: (Resources) -> Int,
modifier: Modifier = Modifier,
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 2af5ffaee7ed..5790c4af0d77 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -19,6 +19,7 @@ package com.android.systemui.notifications.ui.composable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layoutId
import com.android.compose.animation.scene.ContentScope
@@ -84,7 +85,11 @@ constructor(
viewModel.notificationsPlaceholderViewModelFactory.create()
}
- OverlayShade(modifier = modifier, onScrimClicked = viewModel::onScrimClicked) {
+ OverlayShade(
+ panelAlignment = Alignment.TopStart,
+ modifier = modifier,
+ onScrimClicked = viewModel::onScrimClicked,
+ ) {
Column {
if (viewModel.showHeader) {
val burnIn = rememberBurnIn(clockInteractor)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index b1a19456ab7d..f6c5f588aa95 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -99,7 +99,11 @@ constructor(
val viewModel =
rememberViewModel("QuickSettingsShadeOverlay") { contentViewModelFactory.create() }
- OverlayShade(modifier = modifier, onScrimClicked = viewModel::onScrimClicked) {
+ OverlayShade(
+ panelAlignment = Alignment.TopEnd,
+ modifier = modifier,
+ onScrimClicked = viewModel::onScrimClicked,
+ ) {
Column {
ExpandedShadeHeader(
viewModelFactory = viewModel.shadeHeaderViewModelFactory,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 55fafd5cfeca..8907aec7fd48 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -5,7 +5,6 @@ import androidx.compose.foundation.gestures.Orientation
import com.android.compose.animation.scene.ProgressConverter
import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.transitions
-import com.android.systemui.bouncer.ui.composable.Bouncer
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
@@ -110,17 +109,13 @@ val SceneContainerTransitions = transitions {
// Overlay transitions
- // TODO(b/376659778): Remove this transition once nested STLs are supported.
- from(Scenes.Gone, to = Overlays.NotificationsShade) {
- toNotificationsShadeTransition(translateClock = true)
- }
to(Overlays.NotificationsShade) { toNotificationsShadeTransition() }
to(Overlays.QuickSettingsShade) { toQuickSettingsShadeTransition() }
from(Overlays.NotificationsShade, to = Overlays.QuickSettingsShade) {
notificationsShadeToQuickSettingsShadeTransition()
}
from(Scenes.Gone, to = Overlays.NotificationsShade, key = SlightlyFasterShadeCollapse) {
- toNotificationsShadeTransition(translateClock = true, durationScale = 0.9)
+ toNotificationsShadeTransition(durationScale = 0.9)
}
from(Scenes.Gone, to = Overlays.QuickSettingsShade, key = SlightlyFasterShadeCollapse) {
toQuickSettingsShadeTransition(durationScale = 0.9)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 6bdb36331709..3d62151baf2f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -29,10 +29,7 @@ import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.composable.Shade
import kotlin.time.Duration.Companion.milliseconds
-fun TransitionBuilder.toNotificationsShadeTransition(
- translateClock: Boolean = false,
- durationScale: Double = 1.0,
-) {
+fun TransitionBuilder.toNotificationsShadeTransition(durationScale: Double = 1.0) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
swipeSpec =
spring(
@@ -45,11 +42,6 @@ fun TransitionBuilder.toNotificationsShadeTransition(
elevateInContent = Overlays.NotificationsShade,
)
scaleSize(OverlayShade.Elements.Panel, height = 0f)
- // TODO(b/376659778): This is a temporary hack to have a shared element transition with the
- // lockscreen clock. Remove once nested STLs are supported.
- if (!translateClock) {
- translate(ClockElementKeys.smallClockElementKey)
- }
// Avoid translating the status bar with the shade panel.
translate(NotificationsShade.Elements.StatusBar)
// Slide in the shade panel from the top edge.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 8a5c96da5ac6..cfbe6671db02 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -20,6 +20,9 @@ package com.android.systemui.shade.ui.composable
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.PaddingValues
@@ -41,30 +44,51 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
-import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.effect.rememberOffsetOverscrollEffect
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.res.R
/** Renders a lightweight shade UI container, as an overlay. */
@Composable
-fun SceneScope.OverlayShade(
+fun ContentScope.OverlayShade(
+ panelAlignment: Alignment,
onScrimClicked: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
- Box(modifier) {
+ // TODO(b/384653288) This should be removed when b/378470603 is done.
+ val idleEffect = rememberOffsetOverscrollEffect(Orientation.Vertical)
+ Box(
+ modifier
+ .overscroll(idleEffect)
+ .nestedScroll(
+ remember {
+ object : NestedScrollConnection {
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ return available
+ }
+ }
+ }
+ )
+ .scrollable(rememberScrollableState { 0f }, Orientation.Vertical, idleEffect)
+ ) {
Scrim(onClicked = onScrimClicked)
- Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = Alignment.TopEnd) {
+ Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = panelAlignment) {
Panel(
modifier =
Modifier.element(OverlayShade.Elements.Panel)
@@ -77,7 +101,7 @@ fun SceneScope.OverlayShade(
}
@Composable
-private fun SceneScope.Scrim(onClicked: () -> Unit, modifier: Modifier = Modifier) {
+private fun ContentScope.Scrim(onClicked: () -> Unit, modifier: Modifier = Modifier) {
Spacer(
modifier =
modifier
@@ -89,7 +113,7 @@ private fun SceneScope.Scrim(onClicked: () -> Unit, modifier: Modifier = Modifie
}
@Composable
-private fun SceneScope.Panel(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
+private fun ContentScope.Panel(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Box(modifier = modifier.clip(OverlayShade.Shapes.RoundedCornerPanel)) {
Spacer(
modifier =
@@ -180,7 +204,6 @@ object OverlayShade {
object Dimensions {
val PanelCornerRadius = 46.dp
- val OverscrollLimit = 32.dp
}
object Shapes {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
index 85bdf9264467..cea1e9600741 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt
@@ -163,6 +163,16 @@ class KeyguardDisplayManagerTest : SysuiTestCase() {
}
@Test
+ fun testShow_rearDisplayOuterDefaultActive_occluded() {
+ displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
+
+ whenever(deviceStateHelper.isRearDisplayOuterDefaultActive(secondaryDisplay))
+ .thenReturn(true)
+ whenever(keyguardStateController.isOccluded).thenReturn(true)
+ verify(presentationFactory, never()).create(eq(secondaryDisplay))
+ }
+
+ @Test
fun testShow_presentationCreated() {
displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 41cc6ee182cf..271cd3a4f202 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.back.domain.interactor
+import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
@@ -31,6 +32,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.CommunalBackActionInteractor
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
@@ -93,6 +95,7 @@ class BackActionInteractorTest : SysuiTestCase() {
@Mock private lateinit var onBackInvokedDispatcher: WindowOnBackInvokedDispatcher
@Mock private lateinit var iStatusBarService: IStatusBarService
@Mock private lateinit var headsUpManager: HeadsUpManager
+ @Mock private lateinit var communalBackActionInteractor: CommunalBackActionInteractor
private val keyguardRepository = FakeKeyguardRepository()
private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor by lazy {
@@ -117,6 +120,7 @@ class BackActionInteractorTest : SysuiTestCase() {
windowRootViewVisibilityInteractor,
shadeBackActionInteractor,
qsController,
+ communalBackActionInteractor,
)
}
@@ -306,6 +310,19 @@ class BackActionInteractorTest : SysuiTestCase() {
verify(shadeBackActionInteractor).onBackProgressed(0.4f)
}
+ @Test
+ @EnableFlags(Flags.FLAG_GLANCEABLE_HUB_BACK_ACTION)
+ fun onBackAction_communalCanBeDismissed_communalBackActionInteractorCalled() {
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ powerInteractor.setAwakeForTest()
+ val callback = getBackInvokedCallback()
+ whenever(communalBackActionInteractor.canBeDismissed()).thenReturn(true)
+ callback.onBackInvoked()
+
+ verify(communalBackActionInteractor).onBackPressed()
+ }
+
private fun getBackInvokedCallback(): OnBackInvokedCallback {
testScope.runCurrent()
val captor = argumentCaptor<OnBackInvokedCallback>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt
new file mode 100644
index 000000000000..c365f1cb3872
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorTest.kt
@@ -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 com.android.systemui.communal.domain.interactor
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class CommunalBackActionInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ private var Kosmos.underTest by Fixture { communalBackActionInteractor }
+
+ @Test
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ fun communalShowing_canBeDismissed() =
+ kosmos.runTest {
+ setCommunalAvailable(true)
+ assertThat(underTest.canBeDismissed()).isEqualTo(false)
+ communalInteractor.changeScene(CommunalScenes.Communal, "test")
+ runCurrent()
+ assertThat(underTest.canBeDismissed()).isEqualTo(true)
+ }
+
+ @Test
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ fun onBackPressed_invokesSceneChange() =
+ kosmos.runTest {
+ underTest.onBackPressed()
+ runCurrent()
+ assertThat(communalSceneRepository.currentScene.value).isEqualTo(CommunalScenes.Blank)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 0bfcd242828d..8a9c42d9b64e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -130,19 +130,6 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
}
@Test
- fun tutorialState_startedAndCommunalSceneShowing_stateWillNotUpdate() =
- testScope.runTest {
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
-
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
- goToCommunal()
-
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
- }
-
- @Test
fun tutorialState_completedAndCommunalSceneShowing_stateWillNotUpdate() =
testScope.runTest {
val tutorialSettingState by
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index ee3e241b5754..56e8185ab580 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -81,7 +81,7 @@ class CameraQuickAffordanceConfigTest : SysuiTestCase() {
// Then
verify(cameraGestureHelper)
.launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true), result)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index 50ac26196978..fde9b8ce6a50 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -197,7 +197,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
val dndMode = currentModes!!.single()
assertThat(dndMode.isActive).isFalse()
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
}
@Test
@@ -222,7 +222,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
)
// then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
assertEquals(ZEN_MODE_OFF, spyZenMode.value)
assertNull(spyConditionId.value)
}
@@ -244,7 +244,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
val dndMode = currentModes!!.single()
assertThat(dndMode.isActive).isTrue()
assertThat(zenModeRepository.getModeActiveDuration(dndMode.id)).isNull()
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
}
@Test
@@ -268,7 +268,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
)
// then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
assertNull(spyConditionId.value)
}
@@ -285,7 +285,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
val result = underTest.onTriggered(null)
runCurrent()
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
val dndMode = currentModes!!.single()
assertThat(dndMode.isActive).isTrue()
assertThat(zenModeRepository.getModeActiveDuration(dndMode.id))
@@ -313,7 +313,7 @@ class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
)
// then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
assertEquals(conditionUri, spyConditionId.value)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt
new file mode 100644
index 000000000000..18946f9d7e07
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceHapticViewModelTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+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.keyguard.domain.interactor.keyguardQuickAffordanceHapticViewModelFactory
+import com.android.systemui.keyguard.domain.interactor.keyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceHapticViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class KeyguardQuickAffordanceHapticViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
+ private val configKey = "$slotId::home"
+ private val keyguardQuickAffordanceInteractor = kosmos.keyguardQuickAffordanceInteractor
+ private val viewModelFlow =
+ MutableStateFlow(KeyguardQuickAffordanceViewModel(configKey = configKey, slotId = slotId))
+
+ private val underTest =
+ kosmos.keyguardQuickAffordanceHapticViewModelFactory.create(viewModelFlow)
+
+ @Test
+ fun whenLaunchingFromTriggeredResult_hapticStateIsLaunch() =
+ testScope.runTest {
+ // GIVEN that the result from triggering the affordance launched an activity or dialog
+ val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+ keyguardQuickAffordanceInteractor.setLaunchingFromTriggeredResult(
+ KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(true, configKey)
+ )
+ runCurrent()
+
+ // THEN the haptic state indicates that a launch haptics must play
+ assertThat(hapticState)
+ .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.LAUNCH)
+ }
+
+ @Test
+ fun whenNotLaunchFromTriggeredResult_hapticStateDoesNotEmit() =
+ testScope.runTest {
+ // GIVEN that the result from triggering the affordance did not launch an activity or
+ // dialog
+ val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+ keyguardQuickAffordanceInteractor.setLaunchingFromTriggeredResult(
+ KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(false, configKey)
+ )
+ runCurrent()
+
+ // THEN there is no haptic state to play any feedback
+ assertThat(hapticState)
+ .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.NO_HAPTICS)
+ }
+
+ @Test
+ fun onQuickAffordanceTogglesToActivated_hapticStateIsToggleOn() =
+ testScope.runTest {
+ // GIVEN that an affordance toggles from deactivated to activated
+ val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+ toggleQuickAffordance(on = true)
+
+ // THEN the haptic state reflects that a toggle on haptics should play
+ assertThat(hapticState)
+ .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.TOGGLE_ON)
+ }
+
+ @Test
+ fun onQuickAffordanceTogglesToDeactivated_hapticStateIsToggleOff() =
+ testScope.runTest {
+ // GIVEN that an affordance toggles from activated to deactivated
+ val hapticState by collectLastValue(underTest.quickAffordanceHapticState)
+ toggleQuickAffordance(on = false)
+
+ // THEN the haptic state reflects that a toggle off haptics should play
+ assertThat(hapticState)
+ .isEqualTo(KeyguardQuickAffordanceHapticViewModel.HapticState.TOGGLE_OFF)
+ }
+
+ private fun TestScope.toggleQuickAffordance(on: Boolean) {
+ underTest.updateActivatedHistory(!on)
+ runCurrent()
+ underTest.updateActivatedHistory(on)
+ runCurrent()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
index b15352bfe6ab..173b4e56075c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
@@ -49,14 +49,10 @@ import org.mockito.MockitoAnnotations
class MuteQuickAffordanceConfigTest : SysuiTestCase() {
private lateinit var underTest: MuteQuickAffordanceConfig
- @Mock
- private lateinit var ringerModeTracker: RingerModeTracker
- @Mock
- private lateinit var audioManager: AudioManager
- @Mock
- private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var userFileManager: UserFileManager
+ @Mock private lateinit var ringerModeTracker: RingerModeTracker
+ @Mock private lateinit var audioManager: AudioManager
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var userFileManager: UserFileManager
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
@@ -70,9 +66,12 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
whenever(userTracker.userContext).thenReturn(context)
whenever(userFileManager.getSharedPreferences(any(), any(), any()))
- .thenReturn(context.getSharedPreferences("mutequickaffordancetest", Context.MODE_PRIVATE))
+ .thenReturn(
+ context.getSharedPreferences("mutequickaffordancetest", Context.MODE_PRIVATE)
+ )
- underTest = MuteQuickAffordanceConfig(
+ underTest =
+ MuteQuickAffordanceConfig(
context,
userTracker,
userFileManager,
@@ -81,64 +80,71 @@ class MuteQuickAffordanceConfigTest : SysuiTestCase() {
testScope.backgroundScope,
testDispatcher,
testDispatcher,
- )
+ )
}
@Test
- fun pickerState_volumeFixed_notAvailable() = testScope.runTest {
- //given
- whenever(audioManager.isVolumeFixed).thenReturn(true)
+ fun pickerState_volumeFixed_notAvailable() =
+ testScope.runTest {
+ // given
+ whenever(audioManager.isVolumeFixed).thenReturn(true)
- //when
- val result = underTest.getPickerScreenState()
+ // when
+ val result = underTest.getPickerScreenState()
- //then
- assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice, result)
- }
+ // then
+ assertEquals(
+ KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice,
+ result,
+ )
+ }
@Test
- fun pickerState_volumeNotFixed_available() = testScope.runTest {
- //given
- whenever(audioManager.isVolumeFixed).thenReturn(false)
+ fun pickerState_volumeNotFixed_available() =
+ testScope.runTest {
+ // given
+ whenever(audioManager.isVolumeFixed).thenReturn(false)
- //when
- val result = underTest.getPickerScreenState()
+ // when
+ val result = underTest.getPickerScreenState()
- //then
- assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default(), result)
- }
+ // then
+ assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default(), result)
+ }
@Test
- fun triggered_stateWasPreviouslyNORMAL_currentlySILENT_moveToPreviousState() = testScope.runTest {
- //given
- val ringerModeCapture = argumentCaptor<Int>()
- whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
- underTest.onTriggered(null)
- whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
-
- //when
- val result = underTest.onTriggered(null)
- runCurrent()
- verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture()
-
- //then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
- assertEquals(AudioManager.RINGER_MODE_NORMAL, ringerModeCapture.value)
- }
+ fun triggered_stateWasPreviouslyNORMAL_currentlySILENT_moveToPreviousState() =
+ testScope.runTest {
+ // given
+ val ringerModeCapture = argumentCaptor<Int>()
+ whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
+ underTest.onTriggered(null)
+ whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
+
+ // when
+ val result = underTest.onTriggered(null)
+ runCurrent()
+ verify(audioManager, times(2)).ringerModeInternal = ringerModeCapture.capture()
+
+ // then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
+ assertEquals(AudioManager.RINGER_MODE_NORMAL, ringerModeCapture.value)
+ }
@Test
- fun triggered_stateIsNotSILENT_moveToSILENTringer() = testScope.runTest {
- //given
- val ringerModeCapture = argumentCaptor<Int>()
- whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
-
- //when
- val result = underTest.onTriggered(null)
- runCurrent()
- verify(audioManager).ringerModeInternal = ringerModeCapture.capture()
-
- //then
- assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
- assertEquals(AudioManager.RINGER_MODE_SILENT, ringerModeCapture.value)
- }
-} \ No newline at end of file
+ fun triggered_stateIsNotSILENT_moveToSILENTringer() =
+ testScope.runTest {
+ // given
+ val ringerModeCapture = argumentCaptor<Int>()
+ whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
+
+ // when
+ val result = underTest.onTriggered(null)
+ runCurrent()
+ verify(audioManager).ringerModeInternal = ringerModeCapture.capture()
+
+ // then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false), result)
+ assertEquals(AudioManager.RINGER_MODE_SILENT, ringerModeCapture.value)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index e9b36b8b3b57..9bdc363b3a38 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -88,9 +88,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
Icon.Loaded(
drawable = ICON,
contentDescription =
- ContentDescription.Resource(
- res = R.string.accessibility_wallet_button,
- ),
+ ContentDescription.Resource(res = R.string.accessibility_wallet_button),
)
)
}
@@ -118,9 +116,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
Icon.Loaded(
drawable = ICON,
contentDescription =
- ContentDescription.Resource(
- res = R.string.accessibility_wallet_button,
- ),
+ ContentDescription.Resource(res = R.string.accessibility_wallet_button),
)
)
}
@@ -163,13 +159,9 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
}
assertThat(underTest.onTriggered(expandable))
- .isEqualTo(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled)
+ .isEqualTo(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true))
verify(walletController)
- .startQuickAccessUiIntent(
- activityStarter,
- animationController,
- /* hasCard= */ true,
- )
+ .startQuickAccessUiIntent(activityStarter, animationController, /* hasCard= */ true)
}
@Test
@@ -184,9 +176,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@Test
fun getPickerScreenState_unavailable() =
testScope.runTest {
- setUpState(
- isWalletServiceAvailable = false,
- )
+ setUpState(isWalletServiceAvailable = false)
assertThat(underTest.getPickerScreenState())
.isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
@@ -195,9 +185,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@Test
fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() =
testScope.runTest {
- setUpState(
- isWalletFeatureAvailable = false,
- )
+ setUpState(isWalletFeatureAvailable = false)
assertThat(underTest.getPickerScreenState())
.isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
@@ -206,9 +194,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@Test
fun getPickerScreenState_disabledWhenThereIsNoCard() =
testScope.runTest {
- setUpState(
- hasSelectedCard = false,
- )
+ setUpState(hasSelectedCard = false)
assertThat(underTest.getPickerScreenState())
.isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
@@ -219,7 +205,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
isWalletServiceAvailable: Boolean = true,
isWalletQuerySuccessful: Boolean = true,
hasSelectedCard: Boolean = true,
- cardType: Int = WalletCard.CARD_TYPE_UNKNOWN
+ cardType: Int = WalletCard.CARD_TYPE_UNKNOWN,
) {
val walletClient: QuickAccessWalletClient = mock()
whenever(walletClient.tileIcon).thenReturn(ICON)
@@ -242,11 +228,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
/*cardType= */ cardType,
/*cardImage= */ mock(),
/*contentDescription= */ CARD_DESCRIPTION,
- /*pendingIntent= */ mock()
+ /*pendingIntent= */ mock(),
)
.build()
),
- 0
+ 0,
)
} else {
GetWalletCardsResponse(emptyList(), 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 46d1ebe75899..9de0215022e9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -764,6 +764,28 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
assertThat(launchingAffordance).isFalse()
}
+ @Test
+ fun onQuickAffordanceTriggered_updatesLaunchingFromTriggeredResult() =
+ testScope.runTest {
+ // WHEN selecting and triggering a quick affordance at a slot
+ val key = homeControls.key
+ val slot = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
+ val encodedKey = "$slot::$key"
+ val actionLaunched = true
+ homeControls.onTriggeredResult =
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(actionLaunched)
+ underTest.select(slot, key)
+ runCurrent()
+ underTest.onQuickAffordanceTriggered(encodedKey, expandable = null, slot)
+
+ // THEN the latest triggered result shows that an action launched for the same key and
+ // slot
+ val launchingFromTriggeredResult by
+ collectLastValue(underTest.launchingFromTriggeredResult)
+ assertThat(launchingFromTriggeredResult?.launched).isEqualTo(actionLaunched)
+ assertThat(launchingFromTriggeredResult?.configKey).isEqualTo(encodedKey)
+ }
+
companion object {
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
private val ICON: Icon =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
index ad5eeabf83d2..26fe379f00bf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -118,6 +119,50 @@ class LockscreenSceneTransitionInteractorTest : SysuiTestCase() {
)
}
+ /** STL: Ls -> overlay, then settle with Idle(overlay). */
+ @Test
+ fun transition_overlay_from_ls_scene_end_in_gone() =
+ testScope.runTest {
+ sceneTransitions.value =
+ ObservableTransitionState.Transition.ShowOrHideOverlay(
+ overlay = Overlays.NotificationsShade,
+ fromContent = Scenes.Lockscreen,
+ toContent = Overlays.NotificationsShade,
+ currentScene = Scenes.Lockscreen,
+ currentOverlays = flowOf(emptySet()),
+ progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ previewProgress = flowOf(0f),
+ isInPreviewStage = flowOf(false),
+ )
+
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(step = currentStep!!, state = TransitionState.RUNNING, progress = 0.4f)
+
+ sceneTransitions.value =
+ ObservableTransitionState.Idle(
+ Scenes.Lockscreen,
+ setOf(Overlays.NotificationsShade),
+ )
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
/**
* STL: Ls -> Gone, then settle with Idle(Ls). KTF in this scenario needs to invert the
* transition LS -> UNDEFINED to UNDEFINED -> LS as there is no mechanism in KTF to
@@ -259,6 +304,47 @@ class LockscreenSceneTransitionInteractorTest : SysuiTestCase() {
)
}
+ /** STL: Ls with overlay, then settle with Idle(Ls). */
+ @Test
+ fun transition_overlay_to_ls_scene_end_in_ls() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value =
+ ObservableTransitionState.Transition.ShowOrHideOverlay(
+ overlay = Overlays.NotificationsShade,
+ fromContent = Overlays.NotificationsShade,
+ toContent = Scenes.Lockscreen,
+ currentScene = Scenes.Lockscreen,
+ currentOverlays = flowOf(setOf(Overlays.NotificationsShade)),
+ progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ previewProgress = flowOf(0f),
+ isInPreviewStage = flowOf(false),
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(step = currentStep!!, state = TransitionState.RUNNING, progress = 0.4f)
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
/** STL: Gone -> Ls (AOD), will transition to AOD once */
@Test
fun transition_to_ls_scene_with_changed_next_scene_is_respected_just_once() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 0e3b03f74c02..be504cc0f704 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -18,11 +18,8 @@
package com.android.systemui.keyguard.ui.viewmodel
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.DisableSceneContainer
@@ -75,7 +72,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
private val burnInFlow = MutableStateFlow(BurnInModel())
@Before
- @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
@DisableSceneContainer
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -219,50 +215,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
}
@Test
- @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun translationAndScale_whenFullyDozing_MigrationFlagOff_staysOutOfTopInset() =
- testScope.runTest {
- underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
- val movement by collectLastValue(underTest.movement)
- assertThat(movement?.translationX).isEqualTo(0)
-
- // Set to dozing (on AOD)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ),
- validateStep = false,
- )
-
- // Trigger a change to the burn-in model
- burnInFlow.value = BurnInModel(translationX = 20, translationY = -30, scale = 0.5f)
- assertThat(movement?.translationX).isEqualTo(20)
- // -20 instead of -30, due to inset of 80
- assertThat(movement?.translationY).isEqualTo(-20)
- assertThat(movement?.scale).isEqualTo(0.5f)
- assertThat(movement?.scaleClockOnly).isEqualTo(true)
-
- // Set to the beginning of GONE->AOD transition
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ),
- validateStep = false,
- )
- assertThat(movement?.translationX).isEqualTo(0)
- assertThat(movement?.translationY).isEqualTo(0)
- assertThat(movement?.scale).isEqualTo(1f)
- assertThat(movement?.scaleClockOnly).isEqualTo(true)
- }
-
- @Test
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun translationAndScale_whenFullyDozing_MigrationFlagOn_staysOutOfTopInset() =
testScope.runTest {
underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
@@ -334,7 +286,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
@Test
@DisableSceneContainer
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun translationAndScale_sceneContainerOff_weatherLargeClock() =
testBurnInViewModelForClocks(
isSmallClock = false,
@@ -344,7 +295,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
@Test
@DisableSceneContainer
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun translationAndScale_sceneContainerOff_weatherSmallClock() =
testBurnInViewModelForClocks(
isSmallClock = true,
@@ -354,7 +304,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
@Test
@DisableSceneContainer
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun translationAndScale_sceneContainerOff_nonWeatherLargeClock() =
testBurnInViewModelForClocks(
isSmallClock = false,
@@ -364,7 +313,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
@Test
@DisableSceneContainer
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun translationAndScale_sceneContainerOff_nonWeatherSmallClock() =
testBurnInViewModelForClocks(
isSmallClock = true,
@@ -373,7 +321,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
)
@Test
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
@EnableSceneContainer
fun translationAndScale_sceneContainerOn_weatherLargeClock() =
testBurnInViewModelForClocks(
@@ -383,7 +330,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
)
@Test
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
@EnableSceneContainer
fun translationAndScale_sceneContainerOn_weatherSmallClock() =
testBurnInViewModelForClocks(
@@ -393,7 +339,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
)
@Test
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
@EnableSceneContainer
fun translationAndScale_sceneContainerOn_nonWeatherLargeClock() =
testBurnInViewModelForClocks(
@@ -403,7 +348,6 @@ class AodBurnInViewModelTest : SysuiTestCase() {
)
@Test
- @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
@EnableSceneContainer
@Ignore("b/367659687")
fun translationAndScale_sceneContainerOn_nonWeatherSmallClock() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 95ffc962797d..789477e38b55 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -19,12 +19,10 @@
package com.android.systemui.keyguard.ui.viewmodel
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.view.View
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.shared.model.CommunalScenes
@@ -73,7 +71,6 @@ import platform.test.runner.parameterized.Parameters
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
index 056efb34a0b1..c47a412e226a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
@@ -117,7 +117,6 @@ class QSTileViewModelImplTest : SysuiTestCase() {
"test_spec:\n" +
" QSTileState(" +
"icon=Resource(res=0, contentDescription=Resource(res=0)), " +
- "iconRes=null, " +
"label=test_data, " +
"activationState=INACTIVE, " +
"secondaryLabel=null, " +
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index b88861756889..5c6657b98801 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -8,6 +8,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -231,7 +232,8 @@ public class InternetAdapterTest extends SysuiTestCase {
mViewHolder.onWifiClick(mWifiEntry, mock(View.class));
- verify(mSpyContext).startActivity(any());
+ verify(mInternetDialogController).startActivityForDialog(any());
+ verify(mSpyContext, never()).startActivity(any());
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
index 00460bfe83b2..557f4ea262a3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
@@ -93,8 +93,7 @@ class AirplaneModeMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(R.string.airplane_mode)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
index 632aae035ede..24e46686e23d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
@@ -178,8 +178,7 @@ class AlarmTileMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(R.string.status_bar_alarm)
return QSTileState(
- Icon.Loaded(context.getDrawable(R.drawable.ic_alarm)!!, null),
- R.drawable.ic_alarm,
+ Icon.Loaded(context.getDrawable(R.drawable.ic_alarm)!!, null, R.drawable.ic_alarm),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
index 5385f945946c..2ddaddd5ad35 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
@@ -253,8 +253,7 @@ class BatterySaverTileMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(R.string.battery_detail_switch_title)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
index 356b98eb192e..a3c159820a94 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
@@ -77,8 +77,11 @@ class ColorCorrectionTileMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(R.string.quick_settings_color_correction_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(R.drawable.ic_qs_color_correction)!!, null),
- R.drawable.ic_qs_color_correction,
+ Icon.Loaded(
+ context.getDrawable(R.drawable.ic_qs_color_correction)!!,
+ null,
+ R.drawable.ic_qs_color_correction,
+ ),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
index 8236c4c1e638..608adf183163 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
@@ -253,7 +253,6 @@ class CustomTileMapperTest : SysuiTestCase() {
): QSTileState {
return QSTileState(
icon?.let { com.android.systemui.common.shared.model.Icon.Loaded(icon, null) },
- null,
"test label",
activationState,
"test subtitle",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index 587585ccee2e..a115c1235210 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -73,7 +73,11 @@ class FlashlightMapperTest : SysuiTestCase() {
mapper.map(qsTileConfig, FlashlightTileModel.FlashlightAvailable(true))
val expectedIcon =
- Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_on)!!, null)
+ Icon.Loaded(
+ context.getDrawable(R.drawable.qs_flashlight_icon_on)!!,
+ null,
+ R.drawable.qs_flashlight_icon_on,
+ )
val actualIcon = tileState.icon
assertThat(actualIcon).isEqualTo(expectedIcon)
}
@@ -84,7 +88,11 @@ class FlashlightMapperTest : SysuiTestCase() {
mapper.map(qsTileConfig, FlashlightTileModel.FlashlightAvailable(false))
val expectedIcon =
- Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
+ Icon.Loaded(
+ context.getDrawable(R.drawable.qs_flashlight_icon_off)!!,
+ null,
+ R.drawable.qs_flashlight_icon_off,
+ )
val actualIcon = tileState.icon
assertThat(actualIcon).isEqualTo(expectedIcon)
}
@@ -95,7 +103,11 @@ class FlashlightMapperTest : SysuiTestCase() {
mapper.map(qsTileConfig, FlashlightTileModel.FlashlightTemporarilyUnavailable)
val expectedIcon =
- Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
+ Icon.Loaded(
+ context.getDrawable(R.drawable.qs_flashlight_icon_off)!!,
+ null,
+ R.drawable.qs_flashlight_icon_off,
+ )
val actualIcon = tileState.icon
assertThat(actualIcon).isEqualTo(expectedIcon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
index e81771ec38d5..8f8f7105415f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
@@ -58,8 +58,11 @@ class FontScalingTileMapperTest : SysuiTestCase() {
private fun createFontScalingTileState(): QSTileState =
QSTileState(
- Icon.Loaded(context.getDrawable(R.drawable.ic_qs_font_scaling)!!, null),
- R.drawable.ic_qs_font_scaling,
+ Icon.Loaded(
+ context.getDrawable(R.drawable.ic_qs_font_scaling)!!,
+ null,
+ R.drawable.ic_qs_font_scaling,
+ ),
context.getString(R.string.quick_settings_font_scaling_label),
QSTileState.ActivationState.ACTIVE,
null,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
index 12d604ff6a7c..3d3447da15a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
@@ -102,8 +102,7 @@ class HearingDevicesTileMapperTest : SysuiTestCase() {
val label = context.getString(R.string.quick_settings_hearing_devices_label)
val iconRes = R.drawable.qs_hearing_devices_icon
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
index 9dcf49e02697..b087bbc29bf7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
@@ -82,7 +82,6 @@ class InternetTileMapperTest : SysuiTestCase() {
QSTileState.ActivationState.ACTIVE,
context.getString(R.string.quick_settings_networks_available),
Icon.Loaded(signalDrawable, null),
- null,
context.getString(R.string.quick_settings_internet_label),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -120,8 +119,11 @@ class InternetTileMapperTest : SysuiTestCase() {
createInternetTileState(
QSTileState.ActivationState.ACTIVE,
inputModel.secondaryLabel.loadText(context).toString(),
- Icon.Loaded(context.getDrawable(expectedSatIcon!!.res)!!, null),
- expectedSatIcon.res,
+ Icon.Loaded(
+ context.getDrawable(expectedSatIcon!!.res)!!,
+ null,
+ expectedSatIcon.res,
+ ),
expectedSatIcon.contentDescription.loadContentDescription(context).toString(),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -144,8 +146,7 @@ class InternetTileMapperTest : SysuiTestCase() {
createInternetTileState(
QSTileState.ActivationState.ACTIVE,
context.getString(R.string.quick_settings_networks_available),
- Icon.Loaded(context.getDrawable(wifiRes)!!, contentDescription = null),
- wifiRes,
+ Icon.Loaded(context.getDrawable(wifiRes)!!, null, wifiRes),
context.getString(R.string.quick_settings_internet_label),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -171,8 +172,8 @@ class InternetTileMapperTest : SysuiTestCase() {
Icon.Loaded(
context.getDrawable(R.drawable.ic_qs_no_internet_unavailable)!!,
contentDescription = null,
+ R.drawable.ic_qs_no_internet_unavailable,
),
- R.drawable.ic_qs_no_internet_unavailable,
context.getString(R.string.quick_settings_networks_unavailable),
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -182,13 +183,11 @@ class InternetTileMapperTest : SysuiTestCase() {
activationState: QSTileState.ActivationState,
secondaryLabel: String,
icon: Icon,
- iconRes: Int? = null,
contentDescription: String,
): QSTileState {
val label = context.getString(R.string.quick_settings_internet_label)
return QSTileState(
icon,
- iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
index 30fce73e04da..780d58c83e5b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
@@ -90,8 +90,7 @@ class ColorInversionTileMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(R.string.quick_settings_inversion_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 37e8a6053682..4ebe23dcdef1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -69,7 +69,12 @@ class LocationTileMapperTest : SysuiTestCase() {
fun mapsEnabledDataToOnIconState() {
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
- val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_on)!!, null)
+ val expectedIcon =
+ Icon.Loaded(
+ context.getDrawable(R.drawable.qs_location_icon_on)!!,
+ null,
+ R.drawable.qs_location_icon_on,
+ )
val actualIcon = tileState.icon
Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
}
@@ -78,7 +83,12 @@ class LocationTileMapperTest : SysuiTestCase() {
fun mapsDisabledDataToOffIconState() {
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
- val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_off)!!, null)
+ val expectedIcon =
+ Icon.Loaded(
+ context.getDrawable(R.drawable.qs_location_icon_off)!!,
+ null,
+ R.drawable.qs_location_icon_off,
+ )
val actualIcon = tileState.icon
Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index de3dc5730421..44e6b4d2d0f6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -28,6 +28,7 @@ import com.android.internal.R
import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
@@ -63,7 +64,7 @@ class ModesTileDataInteractorTest : SysuiTestCase() {
fun setUp() {
context.orCreateTestableResources.apply {
addOverride(MODES_DRAWABLE_ID, MODES_DRAWABLE)
- addOverride(R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE)
+ addOverride(BEDTIME_DRAWABLE_ID, BEDTIME_DRAWABLE)
}
val customPackageContext = SysuiTestableContext(context)
@@ -145,24 +146,24 @@ class ModesTileDataInteractorTest : SysuiTestCase() {
// Tile starts with the generic Modes icon.
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
// Add an inactive mode -> Still modes icon
zenModeRepository.addMode(id = "Mode", active = false)
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
// Add an active mode with a default icon: icon should be the mode icon, and the
// iconResId is also populated, because we know it's a system icon.
zenModeRepository.addMode(
id = "Bedtime with default icon",
type = AutomaticZenRule.TYPE_BEDTIME,
- active = true
+ active = true,
)
runCurrent()
assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
- assertThat(tileData?.iconResId).isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
+ assertThat(tileData?.icon!!.res).isEqualTo(BEDTIME_DRAWABLE_ID)
// Add another, less-prioritized mode that has a *custom* icon: for now, icon should
// remain the first mode icon
@@ -177,20 +178,20 @@ class ModesTileDataInteractorTest : SysuiTestCase() {
)
runCurrent()
assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
- assertThat(tileData?.iconResId).isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
+ assertThat(tileData?.icon!!.res).isEqualTo(BEDTIME_DRAWABLE_ID)
// Deactivate more important mode: icon should be the less important, still active mode
// And because it's a package-provided icon, iconResId is not populated.
zenModeRepository.deactivateMode("Bedtime with default icon")
runCurrent()
assertThat(tileData?.icon).isEqualTo(CUSTOM_ICON)
- assertThat(tileData?.iconResId).isNull()
+ assertThat(tileData?.icon!!.res).isNull()
// Deactivate remaining mode: back to the default modes icon
zenModeRepository.deactivateMode("Driving with custom icon")
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
}
@Test
@@ -205,18 +206,18 @@ class ModesTileDataInteractorTest : SysuiTestCase() {
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
// Activate a Mode -> Icon doesn't change.
zenModeRepository.addMode(id = "Mode", active = true)
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
zenModeRepository.deactivateMode(id = "Mode")
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
- assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+ assertThat(tileData?.icon!!.res).isEqualTo(MODES_DRAWABLE_ID)
}
@EnableFlags(Flags.FLAG_MODES_UI)
@@ -256,15 +257,17 @@ class ModesTileDataInteractorTest : SysuiTestCase() {
val TEST_USER = UserHandle.of(1)!!
const val CUSTOM_PACKAGE = "com.some.mode.owner.package"
- val MODES_DRAWABLE_ID = R.drawable.ic_zen_priority_modes
+ const val MODES_DRAWABLE_ID = R.drawable.ic_zen_priority_modes
const val CUSTOM_DRAWABLE_ID = 12345
+ const val BEDTIME_DRAWABLE_ID = R.drawable.ic_zen_mode_type_bedtime
+
val MODES_DRAWABLE = TestStubDrawable("modes_icon")
val BEDTIME_DRAWABLE = TestStubDrawable("bedtime")
val CUSTOM_DRAWABLE = TestStubDrawable("custom")
- val MODES_ICON = MODES_DRAWABLE.asIcon()
- val BEDTIME_ICON = BEDTIME_DRAWABLE.asIcon()
+ val MODES_ICON = Icon.Loaded(MODES_DRAWABLE, null, MODES_DRAWABLE_ID)
+ val BEDTIME_ICON = Icon.Loaded(BEDTIME_DRAWABLE, null, BEDTIME_DRAWABLE_ID)
val CUSTOM_ICON = CUSTOM_DRAWABLE.asIcon()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
index 88b00468573f..04e094f25f5d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
@@ -156,6 +156,10 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() {
}
private fun modelOf(isActivated: Boolean, activeModeNames: List<String>): ModesTileModel {
- return ModesTileModel(isActivated, activeModeNames, TestStubDrawable("icon").asIcon(), 123)
+ return ModesTileModel(
+ isActivated,
+ activeModeNames,
+ TestStubDrawable("icon").asIcon(res = 123),
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
index 4e91d16bf1ec..d73044f6b479 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
@@ -99,18 +99,11 @@ class ModesTileMapperTest : SysuiTestCase() {
@Test
fun state_modelHasIconResId_includesIconResId() {
- val icon = TestStubDrawable("res123").asIcon()
- val model =
- ModesTileModel(
- isActivated = false,
- activeModes = emptyList(),
- icon = icon,
- iconResId = 123,
- )
+ val icon = TestStubDrawable("res123").asIcon(res = 123)
+ val model = ModesTileModel(isActivated = false, activeModes = emptyList(), icon = icon)
val state = underTest.map(config, model)
assertThat(state.icon).isEqualTo(icon)
- assertThat(state.iconRes).isEqualTo(123)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
index 1457f533f5ec..7c853261aa1c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
@@ -289,8 +289,7 @@ class NightDisplayTileMapperTest : SysuiTestCase() {
if (TextUtils.isEmpty(secondaryLabel)) label
else TextUtils.concat(label, ", ", secondaryLabel)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
index 2ac3e081b8f4..b6caa22a3012 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
@@ -58,8 +58,11 @@ class NotesTileMapperTest : SysuiTestCase() {
private fun createNotesTileState(): QSTileState =
QSTileState(
- Icon.Loaded(context.getDrawable(R.drawable.ic_qs_notes)!!, null),
- R.drawable.ic_qs_notes,
+ Icon.Loaded(
+ context.getDrawable(R.drawable.ic_qs_notes)!!,
+ null,
+ R.drawable.ic_qs_notes,
+ ),
context.getString(R.string.quick_settings_notes_label),
QSTileState.ActivationState.INACTIVE,
/* secondaryLabel= */ null,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
index 7782d2b279a8..5b39810e3477 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
@@ -66,11 +66,7 @@ class OneHandedModeTileMapperTest : SysuiTestCase() {
val outputState = mapper.map(config, inputModel)
val expectedState =
- createOneHandedModeTileState(
- QSTileState.ActivationState.INACTIVE,
- subtitleArray[1],
- com.android.internal.R.drawable.ic_qs_one_handed_mode,
- )
+ createOneHandedModeTileState(QSTileState.ActivationState.INACTIVE, subtitleArray[1])
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
@@ -81,23 +77,21 @@ class OneHandedModeTileMapperTest : SysuiTestCase() {
val outputState = mapper.map(config, inputModel)
val expectedState =
- createOneHandedModeTileState(
- QSTileState.ActivationState.ACTIVE,
- subtitleArray[2],
- com.android.internal.R.drawable.ic_qs_one_handed_mode,
- )
+ createOneHandedModeTileState(QSTileState.ActivationState.ACTIVE, subtitleArray[2])
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
}
private fun createOneHandedModeTileState(
activationState: QSTileState.ActivationState,
secondaryLabel: String,
- iconRes: Int,
): QSTileState {
val label = context.getString(R.string.quick_settings_onehanded_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(
+ context.getDrawable(com.android.internal.R.drawable.ic_qs_one_handed_mode)!!,
+ null,
+ com.android.internal.R.drawable.ic_qs_one_handed_mode,
+ ),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
index ed33250a3392..c572ff60b61a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
@@ -93,8 +93,8 @@ class QRCodeScannerTileMapperTest : SysuiTestCase() {
Icon.Loaded(
context.getDrawable(com.android.systemui.res.R.drawable.ic_qr_code_scanner)!!,
null,
+ com.android.systemui.res.R.drawable.ic_qr_code_scanner,
),
- com.android.systemui.res.R.drawable.ic_qr_code_scanner,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
index 85111fd07663..00017f9059de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
@@ -85,8 +85,7 @@ class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
R.drawable.qs_extra_dim_icon_on
else R.drawable.qs_extra_dim_icon_off
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
context.resources
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
index 53671ba38eb6..74010143166b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -180,8 +180,7 @@ class RotationLockTileMapperTest : SysuiTestCase() {
): QSTileState {
val label = context.getString(R.string.quick_settings_rotation_unlocked_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
index 9a450653aa8f..1fb5048dd4c9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
@@ -91,8 +91,7 @@ class DataSaverTileMapperTest : SysuiTestCase() {
else context.resources.getStringArray(R.array.tile_states_saver)[0]
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
index cd683c44a59c..363255695131 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
@@ -110,8 +110,7 @@ class ScreenRecordTileMapperTest : SysuiTestCase() {
val label = context.getString(R.string.quick_settings_screen_record_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
index c569403960d0..e4cd0e0ec215 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
@@ -146,8 +146,7 @@ class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
else context.getString(R.string.quick_settings_mic_label)
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
index 0d2ebe42b7ad..8f5f2d3e6689 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
@@ -69,8 +69,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() {
expandedAccessibilityClass: KClass<out View>? = Switch::class,
): QSTileState {
return QSTileState(
- Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes,
+ Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
index 86321ea04703..2c81f39a03ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
@@ -109,8 +109,7 @@ class WorkModeTileMapperTest : SysuiTestCase() {
val label = testLabel
val iconRes = com.android.internal.R.drawable.stat_sys_managed_profile_status
return QSTileState(
- icon = Icon.Loaded(context.getDrawable(iconRes)!!, null),
- iconRes = iconRes,
+ icon = Icon.Loaded(context.getDrawable(iconRes)!!, null, iconRes),
label = label,
activationState = activationState,
secondaryLabel =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 165e943a0cc0..40f13bbbf908 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -16,10 +16,12 @@
package com.android.systemui.statusbar.chips.notification.ui.viewmodel
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -71,6 +73,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_noNotifs_empty() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -81,6 +84,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_notifMissingStatusBarChipIconView_empty() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -99,6 +103,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_onePromotedNotif_statusBarIconViewMatches() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -122,6 +127,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_onePromotedNotif_connectedDisplaysFlagEnabled_statusBarIconMatches() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -145,6 +151,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_onePromotedNotif_colorMatches() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -175,6 +182,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_onlyForPromotedNotifs() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -208,6 +216,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_connectedDisplaysFlagEnabled_onlyForPromotedNotifs() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -242,6 +251,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_hasShortCriticalText_usesTextInsteadOfTime() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -272,6 +282,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_noTime_isIconOnly() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -294,6 +305,36 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+ fun chips_basicTime_hiddenIfAutomaticallyPromoted() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentModel.Builder("notif").apply {
+ this.time =
+ PromotedNotificationContentModel.When(
+ time = 6543L,
+ mode = PromotedNotificationContentModel.When.Mode.BasicTime,
+ )
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0])
+ .isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+ }
+
+ @Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_basicTime_isShortTimeDelta() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -322,6 +363,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_countUpTime_isTimer() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -349,6 +391,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_countDownTime_isTimer() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -376,6 +419,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_noHeadsUp_showsTime() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -407,6 +451,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_hasHeadsUpByUser_onlyShowsIcon() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -442,6 +487,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_clickingChipNotifiesInteractor() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
new file mode 100644
index 000000000000..14787e169979
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelTest.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.featurepods.popups.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarPopupChips
+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
+@EnableFlags(StatusBarPopupChips.FLAG_NAME)
+@RunWith(AndroidJUnit4::class)
+class StatusBarPopupChipsViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.statusBarPopupChipsViewModel
+
+ @Test
+ fun popupChips_allHidden_empty() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.popupChips)
+ assertThat(latest).isEmpty()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index cdc8bc1e6cbb..a49a66fe26b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -16,12 +16,18 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -36,7 +42,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.app.Person;
import android.content.Context;
+import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.os.Handler;
@@ -66,6 +74,7 @@ import com.android.systemui.statusbar.notification.promoted.shared.model.Promote
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
@@ -155,8 +164,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Test
public void testIncreasedHeadsUpBeingUsed() {
- BindParams params = new BindParams();
- params.usesIncreasedHeadsUpHeight = true;
+ BindParams params = new BindParams(false, false, /* usesIncreasedHeadsUpHeight */ true,
+ REDACTION_TYPE_NONE);
Notification.Builder builder = spy(mBuilder);
mNotificationInflater.inflateNotificationViews(
mRow.getEntry(),
@@ -166,14 +175,15 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
+ mContext,
mSmartReplyStateInflater);
verify(builder).createHeadsUpContentView(true);
}
@Test
public void testIncreasedHeightBeingUsed() {
- BindParams params = new BindParams();
- params.usesIncreasedHeight = true;
+ BindParams params = new BindParams(false, /* usesIncreasedHeight */ true, false,
+ REDACTION_TYPE_NONE);
Notification.Builder builder = spy(mBuilder);
mNotificationInflater.inflateNotificationViews(
mRow.getEntry(),
@@ -183,6 +193,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
+ mContext,
mSmartReplyStateInflater);
verify(builder).createContentView(true);
}
@@ -207,7 +218,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mRow.getEntry().getSbn().getNotification().contentView
= new RemoteViews(mContext.getPackageName(), com.android.systemui.res.R.layout.status_bar);
inflateAndWait(true /* expectingException */, mNotificationInflater, FLAG_CONTENT_VIEW_ALL,
- mRow);
+ REDACTION_TYPE_NONE, mRow);
assertTrue(mRow.getPrivateLayout().getChildCount() == 0);
verify(mRow, times(0)).onNotificationUpdated();
}
@@ -227,7 +238,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mRow.getEntry(),
mRow,
FLAG_CONTENT_VIEW_ALL,
- new BindParams(),
+ new BindParams(false, false, false, REDACTION_TYPE_NONE),
false /* forceInflate */,
null /* callback */);
Assert.assertNull(mRow.getEntry().getRunningTask());
@@ -287,7 +298,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mBuilder.setCustomContentView(new RemoteViews(getContext().getPackageName(),
R.layout.custom_view_dark));
RemoteViews decoratedMediaView = mBuilder.createContentView();
- Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
+ assertFalse("The decorated media style doesn't allow a view to be reapplied!",
NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
}
@@ -385,7 +396,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mRow.getPrivateLayout().removeAllViews();
mRow.getEntry().getSbn().getNotification().contentView =
new RemoteViews(mContext.getPackageName(), R.layout.invalid_notification_height);
- inflateAndWait(true, mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
+ inflateAndWait(true, mNotificationInflater, FLAG_CONTENT_VIEW_ALL, REDACTION_TYPE_NONE,
+ mRow);
assertEquals(0, mRow.getPrivateLayout().getChildCount());
verify(mRow, times(0)).onNotificationUpdated();
}
@@ -455,16 +467,88 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
assertNull(mRow.getEntry().getPromotedNotificationContentModel());
}
+ @Test
+ @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+ public void testSensitiveContentPublicView_messageStyle() throws Exception {
+ String displayName = "Display Name";
+ String messageText = "Message Text";
+ String contentText = "Content Text";
+ Icon personIcon = Icon.createWithResource(mContext,
+ com.android.systemui.res.R.drawable.ic_person);
+ Person testPerson = new Person.Builder()
+ .setName(displayName)
+ .setIcon(personIcon)
+ .build();
+ Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(testPerson);
+ messagingStyle.addMessage(new Notification.MessagingStyle.Message(messageText,
+ System.currentTimeMillis(), testPerson));
+ messagingStyle.setConversationType(Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL);
+ messagingStyle.setShortcutIcon(personIcon);
+ Notification messageNotif = new Notification.Builder(mContext).setSmallIcon(
+ com.android.systemui.res.R.drawable.ic_person).setStyle(messagingStyle).build();
+ ExpandableNotificationRow row = mHelper.createRow(messageNotif);
+ inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_SENSITIVE_CONTENT, row);
+ NotificationContentView publicView = row.getPublicLayout();
+ assertNotNull(publicView);
+ // The display name should be included, but not the content or message text
+ assertFalse(hasText(publicView, messageText));
+ assertFalse(hasText(publicView, contentText));
+ assertTrue(hasText(publicView, displayName));
+ }
+
+ @Test
+ @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+ public void testSensitiveContentPublicView_nonMessageStyle() throws Exception {
+ String contentTitle = "Content Title";
+ String contentText = "Content Text";
+ Notification notif = new Notification.Builder(mContext).setSmallIcon(
+ com.android.systemui.res.R.drawable.ic_person)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .build();
+ ExpandableNotificationRow row = mHelper.createRow(notif);
+ inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_SENSITIVE_CONTENT, row);
+ NotificationContentView publicView = row.getPublicLayout();
+ assertNotNull(publicView);
+ assertFalse(hasText(publicView, contentText));
+ assertTrue(hasText(publicView, contentTitle));
+
+ // The standard public view should not use the content title or text
+ inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_PUBLIC, row);
+ publicView = row.getPublicLayout();
+ assertFalse(hasText(publicView, contentText));
+ assertFalse(hasText(publicView, contentTitle));
+ }
+
+ private static boolean hasText(ViewGroup parent, CharSequence text) {
+ for (int i = 0; i < parent.getChildCount(); i++) {
+ View child = parent.getChildAt(i);
+ if (child instanceof ViewGroup) {
+ if (hasText((ViewGroup) child, text)) {
+ return true;
+ }
+ } else if (child instanceof TextView) {
+ return ((TextView) child).getText().toString().contains(text);
+ }
+ }
+ return false;
+ }
+
private static void inflateAndWait(NotificationContentInflater inflater,
@InflationFlag int contentToInflate,
ExpandableNotificationRow row)
throws Exception {
- inflateAndWait(false /* expectingException */, inflater, contentToInflate, row);
+ inflateAndWait(false /* expectingException */, inflater, contentToInflate,
+ REDACTION_TYPE_NONE, row);
}
private static void inflateAndWait(boolean expectingException,
NotificationContentInflater inflater,
@InflationFlag int contentToInflate,
+ @RedactionType int redactionType,
ExpandableNotificationRow row) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
final ExceptionHolder exceptionHolder = new ExceptionHolder();
@@ -492,7 +576,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
row.getEntry(),
row,
contentToInflate,
- new BindParams(),
+ new BindParams(false, false, false, redactionType),
false /* forceInflate */,
callback /* callback */);
assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 9fb72fba4d71..f25ba2c93c65 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row
import android.app.Notification
import android.app.Person
import android.content.Context
+import android.graphics.drawable.Icon
import android.os.AsyncTask
import android.os.Build
import android.os.CancellationSignal
@@ -34,6 +35,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -45,6 +50,7 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
@@ -110,6 +116,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
}
}
private val promotedNotificationContentExtractor = FakePromotedNotificationContentExtractor()
+ private val conversationNotificationProcessor: ConversationNotificationProcessor = mock()
@Before
fun setUp() {
@@ -126,7 +133,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
NotificationRowContentBinderImpl(
cache,
mock(),
- mock<ConversationNotificationProcessor>(),
+ conversationNotificationProcessor,
mock(),
smartReplyStateInflater,
layoutInflaterFactoryProvider,
@@ -138,14 +145,14 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@Test
fun testIncreasedHeadsUpBeingUsed() {
- val params = BindParams()
- params.usesIncreasedHeadsUpHeight = true
+ val params =
+ BindParams(false, false, /* usesIncreasedHeadsUpHeight */ true, REDACTION_TYPE_NONE)
val builder = spy(builder)
notificationInflater.inflateNotificationViews(
row.entry,
row,
params,
- true /* inflateSynchronously */,
+ true, /* inflateSynchronously */
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
@@ -157,14 +164,13 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@Test
fun testIncreasedHeightBeingUsed() {
- val params = BindParams()
- params.usesIncreasedHeight = true
+ val params = BindParams(false, /* usesIncreasedHeight */ true, false, REDACTION_TYPE_NONE)
val builder = spy(builder)
notificationInflater.inflateNotificationViews(
row.entry,
row,
params,
- true /* inflateSynchronously */,
+ true, /* inflateSynchronously */
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
@@ -193,15 +199,18 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
row.entry.sbn.notification.contentView =
RemoteViews(mContext.packageName, R.layout.status_bar)
inflateAndWait(
- true /* expectingException */,
+ true, /* expectingException */
notificationInflater,
FLAG_CONTENT_VIEW_ALL,
+ REDACTION_TYPE_NONE,
row,
)
Assert.assertTrue(row.privateLayout.childCount == 0)
verify(row, times(0)).onNotificationUpdated()
}
+ @Test fun testInflationOfSensitiveContentPublicView() {}
+
@Test
fun testAsyncTaskRemoved() {
row.entry.abortTask()
@@ -217,8 +226,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
row.entry,
row,
FLAG_CONTENT_VIEW_ALL,
- BindParams(),
- false /* forceInflate */,
+ BindParams(false, false, false, REDACTION_TYPE_NONE),
+ false, /* forceInflate */
null, /* callback */
)
Assert.assertNull(row.entry.runningTask)
@@ -431,7 +440,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
mContext.packageName,
com.android.systemui.tests.R.layout.invalid_notification_height,
)
- inflateAndWait(true, notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+ inflateAndWait(true, notificationInflater, FLAG_CONTENT_VIEW_ALL, REDACTION_TYPE_NONE, row)
Assert.assertEquals(0, row.privateLayout.childCount.toLong())
verify(row, times(0)).onNotificationUpdated()
}
@@ -440,7 +449,13 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@Test
fun testInflatePublicSingleLineView() {
row.publicLayout.removeAllViews()
- inflateAndWait(false, notificationInflater, FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, row)
+ inflateAndWait(
+ false,
+ notificationInflater,
+ FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE,
+ REDACTION_TYPE_NONE,
+ row,
+ )
Assert.assertNotNull(row.publicLayout.mSingleLineView)
Assert.assertTrue(row.publicLayout.mSingleLineView is HybridNotificationView)
}
@@ -448,12 +463,15 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@Test
fun testInflatePublicSingleLineConversationView() {
val testPerson = Person.Builder().setName("Person").build()
+ val style = Notification.MessagingStyle(testPerson)
val messagingBuilder =
Notification.Builder(mContext, "no-id")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text")
- .setStyle(Notification.MessagingStyle(testPerson))
+ .setStyle(style)
+ whenever(conversationNotificationProcessor.processNotification(any(), any(), any()))
+ .thenReturn(style)
val messagingRow = spy(testHelper.createRow(messagingBuilder.build()))
messagingRow.publicLayout.removeAllViews()
@@ -461,6 +479,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
false,
notificationInflater,
FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE,
+ REDACTION_TYPE_NONE,
messagingRow,
)
Assert.assertNotNull(messagingRow.publicLayout.mSingleLineView)
@@ -530,6 +549,80 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
Assert.assertNull(row.entry.promotedNotificationContentModel)
}
+ @Test
+ @Throws(java.lang.Exception::class)
+ @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+ fun testSensitiveContentPublicView_messageStyle() {
+ val displayName = "Display Name"
+ val messageText = "Message Text"
+ val contentText = "Content Text"
+ val personIcon = Icon.createWithResource(mContext, R.drawable.ic_person)
+ val testPerson = Person.Builder().setName(displayName).setIcon(personIcon).build()
+ val messagingStyle = Notification.MessagingStyle(testPerson)
+ messagingStyle.addMessage(
+ Notification.MessagingStyle.Message(messageText, System.currentTimeMillis(), testPerson)
+ )
+ messagingStyle.setConversationType(Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL)
+ messagingStyle.setShortcutIcon(personIcon)
+ val messageNotif =
+ Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_person)
+ .setStyle(messagingStyle)
+ .build()
+ val newRow: ExpandableNotificationRow = testHelper.createRow(messageNotif)
+ inflateAndWait(
+ false,
+ notificationInflater,
+ FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_SENSITIVE_CONTENT,
+ newRow,
+ )
+ // The display name should be included, but not the content or message text
+ val publicView = newRow.publicLayout
+ Assert.assertNotNull(publicView)
+ Assert.assertFalse(hasText(publicView, messageText))
+ Assert.assertFalse(hasText(publicView, contentText))
+ Assert.assertTrue(hasText(publicView, displayName))
+ }
+
+ @Test
+ @Throws(java.lang.Exception::class)
+ @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+ fun testSensitiveContentPublicView_nonMessageStyle() {
+ val contentTitle = "Content Title"
+ val contentText = "Content Text"
+ val notif =
+ Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .build()
+ val newRow: ExpandableNotificationRow = testHelper.createRow(notif)
+ inflateAndWait(
+ false,
+ notificationInflater,
+ FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_SENSITIVE_CONTENT,
+ newRow,
+ )
+ var publicView = newRow.publicLayout
+ Assert.assertNotNull(publicView)
+ Assert.assertFalse(hasText(publicView, contentText))
+ Assert.assertTrue(hasText(publicView, contentTitle))
+
+ // The standard public view should not use the content title or text
+ inflateAndWait(
+ false,
+ notificationInflater,
+ FLAG_CONTENT_VIEW_PUBLIC,
+ REDACTION_TYPE_PUBLIC,
+ newRow,
+ )
+ publicView = newRow.publicLayout
+ Assert.assertFalse(hasText(publicView, contentText))
+ Assert.assertFalse(hasText(publicView, contentTitle))
+ }
+
private class ExceptionHolder {
var exception: Exception? = null
}
@@ -568,13 +661,20 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@InflationFlag contentToInflate: Int,
row: ExpandableNotificationRow,
) {
- inflateAndWait(false /* expectingException */, inflater, contentToInflate, row)
+ inflateAndWait(
+ false /* expectingException */,
+ inflater,
+ contentToInflate,
+ REDACTION_TYPE_NONE,
+ row,
+ )
}
private fun inflateAndWait(
expectingException: Boolean,
inflater: NotificationRowContentBinderImpl,
@InflationFlag contentToInflate: Int,
+ @RedactionType redactionType: Int,
row: ExpandableNotificationRow,
) {
val countDownLatch = CountDownLatch(1)
@@ -603,12 +703,26 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
row.entry,
row,
contentToInflate,
- BindParams(),
- false /* forceInflate */,
+ BindParams(false, false, false, redactionType),
+ false, /* forceInflate */
callback, /* callback */
)
Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS))
exceptionHolder.exception?.let { throw it }
}
+
+ fun hasText(parent: ViewGroup, text: CharSequence): Boolean {
+ for (i in 0 until parent.childCount) {
+ val child = parent.getChildAt(i)
+ if (child is ViewGroup) {
+ if (hasText(child, text)) {
+ return true
+ }
+ } else if (child is TextView) {
+ return child.text.toString().contains(text)
+ }
+ }
+ return false
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index f48fd3c998b1..6bdd86efa8c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -241,7 +241,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
shadeTestUtil.setSplitShade(true)
val horizontalPosition = checkNotNull(dimens).horizontalPosition
- assertIs<HorizontalPosition.FloatAtEnd>(horizontalPosition)
+ assertIs<HorizontalPosition.FloatAtStart>(horizontalPosition)
assertThat(horizontalPosition.width).isEqualTo(200)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
index b560c591af1e..1ee8005fb7ab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
@@ -20,7 +20,9 @@ import android.view.WindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
@@ -31,7 +33,6 @@ import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
@@ -43,7 +44,7 @@ import org.mockito.Mockito.verify
@RunWithLooper(setAsMainLooper = true)
class SystemUIBottomSheetDialogTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val configurationController = mock<ConfigurationController>()
private val config = mock<Configuration>()
private val delegate = mock<DialogDelegate<Dialog>>()
@@ -67,21 +68,17 @@ class SystemUIBottomSheetDialogTest : SysuiTestCase() {
@Test
fun onStart_registersConfigCallback() {
- kosmos.testScope.runTest {
+ kosmos.runTest {
dialog.show()
- runCurrent()
-
verify(configurationController).addCallback(any())
}
}
@Test
fun onStop_unregisterConfigCallback() {
- kosmos.testScope.runTest {
+ kosmos.runTest {
dialog.show()
- runCurrent()
dialog.dismiss()
- runCurrent()
verify(configurationController).removeCallback(any())
}
@@ -89,14 +86,12 @@ class SystemUIBottomSheetDialogTest : SysuiTestCase() {
@Test
fun onConfigurationChanged_calledInDelegate() {
- kosmos.testScope.runTest {
+ kosmos.runTest {
dialog.show()
- runCurrent()
val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
verify(configurationController).addCallback(capture(captor))
captor.value.onConfigChanged(config)
- runCurrent()
verify(delegate).onConfigurationChanged(any(), any())
}
diff --git a/packages/SystemUI/res/drawable/audio_bars_idle.xml b/packages/SystemUI/res/drawable/audio_bars_idle.xml
new file mode 100644
index 000000000000..92a24755fece
--- /dev/null
+++ b/packages/SystemUI/res/drawable/audio_bars_idle.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="168dp"
+ android:height="168dp"
+ android:viewportWidth="168"
+ android:viewportHeight="168">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_2_G"
+ android:translateX="121.161"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="102.911"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="139.661"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index a3bad8f012ac..5ccedeafcb59 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -56,8 +56,8 @@
android:layout_marginTop="@dimen/volume_dialog_components_spacing"
android:background="@drawable/ripple_drawable_20dp"
android:contentDescription="@string/accessibility_volume_settings"
+ android:scaleType="centerInside"
android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
android:tint="@androidprv:color/materialColorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
diff --git a/packages/SystemUI/res/raw/audio_bars_in.json b/packages/SystemUI/res/raw/audio_bars_in.json
new file mode 100644
index 000000000000..c90a59c47d64
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_in.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":18,"w":168,"h":168,"nm":"audio_bars_in","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/audio_bars_out.json b/packages/SystemUI/res/raw/audio_bars_out.json
new file mode 100644
index 000000000000..5eab65e057ab
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_out.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":31,"w":168,"h":168,"nm":"audio_bars_out","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/audio_bars_playing.json b/packages/SystemUI/res/raw/audio_bars_playing.json
new file mode 100644
index 000000000000..6ee8e1915f36
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_playing.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":121,"w":168,"h":168,"nm":"audio_bars_playing","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":38,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[-0.016,-14.1],[5.941,-8.358],[5.961,0],[5.958,8.516],[0,14.274],[-5.958,8.515],[-5.961,0],[-5.972,-8.373]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":70,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":102,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[-0.016,-14.1],[5.941,-8.358],[5.961,0],[5.958,8.516],[0,14.274],[-5.958,8.515],[-5.961,0],[-5.972,-8.373]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":32,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-19.1],[5.957,-13.358],[5.961,0],[5.958,13.641],[0,19.399],[-5.958,13.64],[-5.961,0],[-5.957,-13.373]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":65,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":97,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-19.1],[5.957,-13.358],[5.961,0],[5.958,13.641],[0,19.399],[-5.958,13.64],[-5.961,0],[-5.957,-13.373]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":29,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-15.85],[5.957,-10.108],[5.961,0],[5.958,10.516],[0,16.274],[-5.958,10.515],[-5.961,0],[-5.957,-10.123]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":91,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-15.85],[5.957,-10.108],[5.961,0],[5.958,10.516],[0,16.274],[-5.958,10.515],[-5.961,0],[-5.957,-10.123]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-9.225],[5.957,-3.483],[5.961,0],[5.958,3.766],[0,9.524],[-5.958,3.765],[-5.961,0],[-5.957,-3.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":54,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":86,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-9.225],[5.957,-3.483],[5.961,0],[5.958,3.766],[0,9.524],[-5.958,3.765],[-5.961,0],[-5.957,-3.498]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":19,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":48,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":81,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0}],"markers":[{"tm":60,"cm":"1","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index ab0f788dbb13..ec24c3df36a8 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -23,6 +23,9 @@
<!-- The maximum number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">4</integer>
+ <!-- The number of columns in the Split Shade QuickSettings -->
+ <integer name="quick_settings_split_shade_num_columns">6</integer>
+
<!-- Use collapsed layout for media player in landscape QQS -->
<bool name="config_quickSettingsMediaLandscapeCollapsed">false</bool>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index fc536bdb126b..6f13d637d5c5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -20,6 +20,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+import static com.android.systemui.Flags.glanceableHubBackAction;
import static com.android.systemui.shared.Flags.shadeAllowBackGesture;
import android.annotation.LongDef;
@@ -352,6 +353,10 @@ public class QuickStepContract {
}
// Disable back gesture on the hub, but not when the shade is showing.
if ((sysuiStateFlags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) {
+ // Allow back gesture on Glanceable Hub with back action support.
+ if (glanceableHubBackAction()) {
+ return false;
+ }
// Use QS expanded signal as the notification panel is always considered visible
// expanded when on the lock screen and when opening hub over lock screen. This does
// mean that back gesture is disabled when opening shade over hub while in portrait
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 1083136b570a..acfa08643b63 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -26,11 +26,9 @@ import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.os.Trace;
-import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
-import android.view.DisplayAddress;
import android.view.DisplayInfo;
import android.view.View;
import android.view.WindowManager;
@@ -58,6 +56,9 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Provider;
+/**
+ * Manages Keyguard Presentations for non-primary display(s).
+ */
@SysUISingleton
public class KeyguardDisplayManager {
protected static final String TAG = "KeyguardDisplayManager";
@@ -170,14 +171,17 @@ public class KeyguardDisplayManager {
}
return false;
}
- if (mKeyguardStateController.isOccluded()
- && mDeviceStateHelper.isConcurrentDisplayActive(display)) {
+
+ final boolean deviceStateOccludesKeyguard =
+ mDeviceStateHelper.isConcurrentDisplayActive(display)
+ || mDeviceStateHelper.isRearDisplayOuterDefaultActive(display);
+ if (mKeyguardStateController.isOccluded() && deviceStateOccludesKeyguard) {
if (DEBUG) {
// When activities with FLAG_SHOW_WHEN_LOCKED are shown on top of Keyguard, the
// Keyguard state becomes "occluded". In this case, we should not show the
// KeyguardPresentation, since the activity is presenting content onto the
// non-default display.
- Log.i(TAG, "Do not show KeyguardPresentation when occluded and concurrent"
+ Log.i(TAG, "Do not show KeyguardPresentation when occluded and concurrent or rear"
+ " display is active");
}
return false;
@@ -326,44 +330,45 @@ public class KeyguardDisplayManager {
public static class DeviceStateHelper implements DeviceStateManager.DeviceStateCallback {
@Nullable
- private final DisplayAddress.Physical mRearDisplayPhysicalAddress;
-
- // TODO(b/271317597): These device states should be defined in DeviceStateManager
- private final int mConcurrentState;
- private boolean mIsInConcurrentDisplayState;
+ private DeviceState mDeviceState;
@Inject
DeviceStateHelper(
- @ShadeDisplayAware Context context,
DeviceStateManager deviceStateManager,
@Main Executor mainExecutor) {
-
- final String rearDisplayPhysicalAddress = context.getResources().getString(
- com.android.internal.R.string.config_rearDisplayPhysicalAddress);
- if (TextUtils.isEmpty(rearDisplayPhysicalAddress)) {
- mRearDisplayPhysicalAddress = null;
- } else {
- mRearDisplayPhysicalAddress = DisplayAddress
- .fromPhysicalDisplayId(Long.parseLong(rearDisplayPhysicalAddress));
- }
-
- mConcurrentState = context.getResources().getInteger(
- com.android.internal.R.integer.config_deviceStateConcurrentRearDisplay);
deviceStateManager.registerCallback(mainExecutor, this);
}
@Override
public void onDeviceStateChanged(@NonNull DeviceState state) {
- // When concurrent state ends, the display also turns off. This is enforced in various
- // ExtensionRearDisplayPresentationTest CTS tests. So, we don't need to invoke
- // hide() since that will happen through the onDisplayRemoved callback.
- mIsInConcurrentDisplayState = state.getIdentifier() == mConcurrentState;
+ // When dual display or rear display mode ends, the display also turns off. This is
+ // enforced in various ExtensionRearDisplayPresentationTest CTS tests. So, we don't need
+ // to invoke hide() since that will happen through the onDisplayRemoved callback.
+ mDeviceState = state;
+ }
+
+ /**
+ * @return true if the device is in Dual Display mode, and the specified display is the
+ * rear facing (outer) display.
+ */
+ boolean isConcurrentDisplayActive(@NonNull Display display) {
+ return mDeviceState != null
+ && mDeviceState.hasProperty(
+ DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT)
+ && (display.getFlags() & Display.FLAG_REAR) != 0;
}
- boolean isConcurrentDisplayActive(Display display) {
- return mIsInConcurrentDisplayState
- && mRearDisplayPhysicalAddress != null
- && mRearDisplayPhysicalAddress.equals(display.getAddress());
+ /**
+ * @return true if the device is the updated Rear Display mode, and the specified display is
+ * the inner display. See {@link DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT}.
+ * Note that in this state, the outer display is the default display, while the inner
+ * display is the "rear" display.
+ */
+ boolean isRearDisplayOuterDefaultActive(@NonNull Display display) {
+ return mDeviceState != null
+ && mDeviceState.hasProperty(
+ DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)
+ && (display.getFlags() & Display.FLAG_REAR) != 0;
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 07bd813c2420..40a86dc3713e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -19,13 +19,12 @@ package com.android.keyguard
import android.content.Context
import android.view.View
import com.android.systemui.customization.R as customR
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
-import com.android.systemui.shared.R as sharedR
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shared.R as sharedR
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.END
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.START
@@ -55,16 +54,17 @@ constructor(
var statusViewCentered = false
private val filterKeyguardAndSplitShadeOnly: () -> Boolean = {
- statusBarStateController.getState() == KEYGUARD && !statusViewCentered }
+ statusBarStateController.getState() == KEYGUARD && !statusViewCentered
+ }
private val filterKeyguard: () -> Boolean = { statusBarStateController.getState() == KEYGUARD }
private val translateAnimator by lazy {
- val smartSpaceViews = if (MigrateClocksToBlueprint.isEnabled) {
- // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer]
- val scrollXTranslation = { view: View, translation: Float ->
- view.scrollX = -translation.toInt()
- }
+ // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer]
+ val scrollXTranslation = { view: View, translation: Float ->
+ view.scrollX = -translation.toInt()
+ }
+ val smartSpaceViews =
setOf(
ViewIdToTranslate(
viewId = sharedR.id.date_smartspace_view,
@@ -83,18 +83,8 @@ constructor(
direction = START,
shouldBeAnimated = filterKeyguard,
translateFunc = scrollXTranslation,
- )
+ ),
)
- } else {
- setOf(ViewIdToTranslate(
- viewId = R.id.keyguard_status_area,
- direction = START,
- shouldBeAnimated = filterKeyguard,
- translateFunc = { view, value ->
- (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value
- }
- ))
- }
UnfoldConstantTranslateAnimator(
viewsIdToTranslate =
@@ -102,39 +92,39 @@ constructor(
ViewIdToTranslate(
viewId = customR.id.lockscreen_clock_view_large,
direction = START,
- shouldBeAnimated = filterKeyguardAndSplitShadeOnly
+ shouldBeAnimated = filterKeyguardAndSplitShadeOnly,
),
ViewIdToTranslate(
viewId = customR.id.lockscreen_clock_view,
direction = START,
- shouldBeAnimated = filterKeyguard
+ shouldBeAnimated = filterKeyguard,
),
ViewIdToTranslate(
viewId = R.id.notification_stack_scroller,
direction = END,
- shouldBeAnimated = filterKeyguardAndSplitShadeOnly
- )
+ shouldBeAnimated = filterKeyguardAndSplitShadeOnly,
+ ),
) + smartSpaceViews,
- progressProvider = unfoldProgressProvider
+ progressProvider = unfoldProgressProvider,
)
}
private val shortcutButtonsAnimator by lazy {
UnfoldConstantTranslateAnimator(
viewsIdToTranslate =
- setOf(
- ViewIdToTranslate(
- viewId = R.id.start_button,
- direction = START,
- shouldBeAnimated = filterKeyguard
+ setOf(
+ ViewIdToTranslate(
+ viewId = R.id.start_button,
+ direction = START,
+ shouldBeAnimated = filterKeyguard,
+ ),
+ ViewIdToTranslate(
+ viewId = R.id.end_button,
+ direction = END,
+ shouldBeAnimated = filterKeyguard,
+ ),
),
- ViewIdToTranslate(
- viewId = R.id.end_button,
- direction = END,
- shouldBeAnimated = filterKeyguard
- )
- ),
- progressProvider = unfoldProgressProvider
+ progressProvider = unfoldProgressProvider,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 232b62985ad0..47910f3d25bc 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -21,8 +21,11 @@ import android.window.OnBackAnimationCallback
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import android.window.WindowOnBackInvokedDispatcher
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.glanceableHubBackAction
import com.android.systemui.Flags.predictiveBackAnimateShade
+import com.android.systemui.communal.domain.interactor.CommunalBackActionInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -35,7 +38,6 @@ import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Handles requests to go back either from a button or gesture. */
@SysUISingleton
@@ -50,6 +52,7 @@ constructor(
private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
private val shadeBackActionInteractor: ShadeBackActionInteractor,
private val qsController: QuickSettingsController,
+ private val communalBackActionInteractor: CommunalBackActionInteractor,
) : CoreStartable {
private var isCallbackRegistered = false
@@ -114,6 +117,12 @@ constructor(
if (shadeBackActionInteractor.closeUserSwitcherIfOpen()) {
return true
}
+ if (glanceableHubBackAction()) {
+ if (communalBackActionInteractor.canBeDismissed()) {
+ communalBackActionInteractor.onBackPressed()
+ return true
+ }
+ }
if (shouldBackBeHandled()) {
if (shadeBackActionInteractor.canBeCollapsed()) {
// this is the Shade dismiss animation, so make sure QQS closes when it ends.
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index aef5f1f422d1..e6f02457d320 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -21,14 +21,17 @@ import android.graphics.drawable.Drawable
/**
* Models an icon, that can either be already [loaded][Icon.Loaded] or be a [reference]
- * [Icon.Resource] to a resource.
+ * [Icon.Resource] to a resource. In case of [Loaded], the resource ID [res] is optional.
*/
sealed class Icon {
abstract val contentDescription: ContentDescription?
- data class Loaded(
+ data class Loaded
+ @JvmOverloads
+ constructor(
val drawable: Drawable,
override val contentDescription: ContentDescription?,
+ @DrawableRes val res: Int? = null,
) : Icon()
data class Resource(
@@ -37,6 +40,11 @@ sealed class Icon {
) : Icon()
}
-/** Creates [Icon.Loaded] for a given drawable with an optional [contentDescription]. */
-fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon.Loaded =
- Icon.Loaded(this, contentDescription)
+/**
+ * Creates [Icon.Loaded] for a given drawable with an optional [contentDescription] and an optional
+ * [res].
+ */
+fun Drawable.asIcon(
+ contentDescription: ContentDescription? = null,
+ @DrawableRes res: Int? = null,
+): Icon.Loaded = Icon.Loaded(this, contentDescription, res)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt
new file mode 100644
index 000000000000..2ccf96abff79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractor.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.domain.interactor
+
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+
+/**
+ * {@link CommunalBackActionInteractor} is responsible for handling back gestures on the glanceable
+ * hub. When invoked SystemUI should navigate back to the lockscreen.
+ */
+@SysUISingleton
+class CommunalBackActionInteractor
+@Inject
+constructor(
+ private val communalInteractor: CommunalInteractor,
+ private val communalSceneInteractor: CommunalSceneInteractor,
+ private val sceneInteractor: SceneInteractor,
+) {
+ fun canBeDismissed(): Boolean {
+ return communalInteractor.isCommunalShowing.value
+ }
+
+ fun onBackPressed() {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/384610333): Properly determine whether to go to dream or lockscreen on back.
+ sceneInteractor.changeScene(
+ toScene = Scenes.Lockscreen,
+ loggingReason = "CommunalBackActionInteractor",
+ )
+ } else {
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Blank,
+ loggingReason = "CommunalBackActionInteractor",
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index ea428698e476..947113da0e60 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -285,7 +285,7 @@ constructor(
* use [isIdleOnCommunal].
*/
// TODO(b/323215860): rename to something more appropriate after cleaning up usages
- val isCommunalShowing: Flow<Boolean> =
+ val isCommunalShowing: StateFlow<Boolean> =
flow { emit(SceneContainerFlag.isEnabled) }
.flatMapLatest { sceneContainerEnabled ->
if (sceneContainerEnabled) {
@@ -304,10 +304,10 @@ constructor(
columnName = "isCommunalShowing",
initialValue = false,
)
- .shareIn(
+ .stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- replay = 1,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 9ae106c3ab39..014c0db618e1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -267,6 +267,7 @@ public class FrameworkServicesModule {
}
@Provides
+ @Nullable
@Singleton
static VirtualDeviceManager provideVirtualDeviceManager(Context context) {
return context.getSystemService(VirtualDeviceManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 571b37f43fd4..b272d65a8a11 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -54,6 +54,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.PhoneWindow;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Flags;
import com.android.systemui.ambient.touch.TouchHandler;
import com.android.systemui.ambient.touch.TouchMonitor;
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent;
@@ -210,6 +211,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mCommunalVisible = communalVisible;
updateLifecycleStateLocked();
+ updateGestureBlockingLocked();
});
}
};
@@ -585,7 +587,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private void updateGestureBlockingLocked() {
final boolean shouldBlock = mStarted && !mShadeExpanded && !mBouncerShowing
- && !isDreamInPreviewMode();
+ && !isDreamInPreviewMode()
+ && !(Flags.glanceableHubBackAction() && mCommunalVisible);
if (shouldBlock) {
mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index f549e64ca853..d0065c8b06c6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -39,7 +39,6 @@ import androidx.annotation.VisibleForTesting
import androidx.core.math.MathUtils
import com.android.app.animation.Interpolators
import com.android.internal.R
-import com.android.keyguard.KeyguardClockSwitchController
import com.android.keyguard.KeyguardViewController
import com.android.systemui.Flags.fasterUnlockTransition
import com.android.systemui.dagger.SysUISingleton
@@ -206,7 +205,7 @@ constructor(
fun onUnlockAnimationFinished() {}
}
- /** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */
+ /** The SmartSpace view on the lockscreen. */
var lockscreenSmartspace: View? = null
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index 74ee052f12b9..57f06fbd3bb5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -21,13 +21,13 @@ import android.app.StatusBarManager
import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.pm.PackageManager
-import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.camera.CameraGestureHelper
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.Background
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeDisplayAware
import dagger.Lazy
@@ -65,7 +65,7 @@ constructor(
icon =
Icon.Resource(
R.drawable.ic_camera,
- ContentDescription.Resource(R.string.accessibility_camera_button)
+ ContentDescription.Resource(R.string.accessibility_camera_button),
)
)
} else {
@@ -88,7 +88,7 @@ constructor(
cameraGestureHelper
.get()
.launchCamera(StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE)
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true)
}
private suspend fun isLaunchable(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index e8d3bfac6361..1b8baf657948 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -210,16 +210,16 @@ constructor(
): KeyguardQuickAffordanceConfig.OnTriggeredResult {
return if (ModesUi.isEnabled) {
if (!isAvailable.value) {
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
} else {
val dnd = interactor.dndMode.value
if (dnd == null) {
Log.wtf(TAG, "Triggered DND but it's null!?")
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
if (dnd.isActive) {
interactor.deactivateMode(dnd)
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
} else {
if (interactor.shouldAskForZenDuration(dnd)) {
// NOTE: The dialog handles turning on the mode itself.
@@ -229,16 +229,16 @@ constructor(
)
} else {
interactor.activateMode(dnd)
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
}
}
} else {
when {
- !oldIsAvailable -> KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ !oldIsAvailable -> KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
zenMode != ZEN_MODE_OFF -> {
controller.setZen(ZEN_MODE_OFF, null, TAG)
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
settingsValue == ZEN_DURATION_PROMPT ->
@@ -249,12 +249,12 @@ constructor(
settingsValue == ZEN_DURATION_FOREVER -> {
controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG)
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
else -> {
controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, conditionUri, TAG)
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
index 480ef5e19d8e..e2642a0964c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -18,15 +18,14 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.content.Context
-import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.FlashlightController
import javax.inject.Inject
@@ -50,9 +49,9 @@ constructor(
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
Icon.Resource(
R.drawable.qs_flashlight_icon_on,
- ContentDescription.Resource(R.string.quick_settings_flashlight_label)
+ ContentDescription.Resource(R.string.quick_settings_flashlight_label),
),
- ActivationState.Active
+ ActivationState.Active,
)
}
@@ -61,9 +60,9 @@ constructor(
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
Icon.Resource(
R.drawable.qs_flashlight_icon_off,
- ContentDescription.Resource(R.string.quick_settings_flashlight_label)
+ ContentDescription.Resource(R.string.quick_settings_flashlight_label),
),
- ActivationState.Inactive
+ ActivationState.Inactive,
)
}
@@ -92,14 +91,14 @@ constructor(
} else {
FlashlightState.OffAvailable.toLockScreenState()
},
- TAG
+ TAG,
)
}
override fun onFlashlightError() {
trySendWithFailureLogging(
FlashlightState.OffAvailable.toLockScreenState(),
- TAG
+ TAG,
)
}
@@ -114,7 +113,7 @@ constructor(
FlashlightState.OffAvailable.toLockScreenState()
}
},
- TAG
+ TAG,
)
}
}
@@ -130,7 +129,7 @@ constructor(
flashlightController.setFlashlight(
flashlightController.isAvailable && !flashlightController.isEnabled
)
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
index d335a1806a6d..06da281648a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -111,7 +111,7 @@ constructor(
transitionKey = CommunalTransitionKeys.SimpleFade,
)
}
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 1cf6183fec6c..ade65c38ff3c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -21,10 +21,10 @@ import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.net.Uri
-import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.res.R
import kotlinx.coroutines.flow.Flow
/** Defines interface that can act as data source for a single quick affordance model. */
@@ -71,7 +71,7 @@ interface KeyguardQuickAffordanceConfig {
/** The picker shows the item for selecting this affordance as it normally would. */
data class Default(
/** Optional [Intent] to use to start an activity to configure this affordance. */
- val configureIntent: Intent? = null,
+ val configureIntent: Intent? = null
) : PickerScreenState()
/**
@@ -134,34 +134,39 @@ interface KeyguardQuickAffordanceConfig {
) : LockScreenState()
}
- sealed class OnTriggeredResult {
+ sealed class OnTriggeredResult() {
/**
* Returning this as a result from the [onTriggered] method means that the implementation
* has taken care of the action, the system will do nothing.
+ *
+ * @param[actionLaunched] Whether the implementation handled the action by launching a
+ * dialog or an activity.
*/
- object Handled : OnTriggeredResult()
+ data class Handled(val actionLaunched: Boolean) : OnTriggeredResult()
/**
* Returning this as a result from the [onTriggered] method means that the implementation
* has _not_ taken care of the action and the system should start an activity using the
* given [Intent].
*/
- data class StartActivity(
- val intent: Intent,
- val canShowWhileLocked: Boolean,
- ) : OnTriggeredResult()
+ data class StartActivity(val intent: Intent, val canShowWhileLocked: Boolean) :
+ OnTriggeredResult()
/**
* Returning this as a result from the [onTriggered] method means that the implementation
* has _not_ taken care of the action and the system should show a Dialog using the given
* [AlertDialog] and [Expandable].
*/
- data class ShowDialog(
- val dialog: AlertDialog,
- val expandable: Expandable?,
- ) : OnTriggeredResult()
+ data class ShowDialog(val dialog: AlertDialog, val expandable: Expandable?) :
+ OnTriggeredResult()
}
+ /**
+ * Models an [OnTriggeredResult] that did or did not launch a dialog or activity for a given
+ * config key.
+ */
+ data class LaunchingFromTriggeredResult(val launched: Boolean, val configKey: String)
+
companion object {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
index 1358634a55f8..1c9bc9f39663 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.media.AudioManager
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.ContentDescription
@@ -45,7 +46,6 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
@SysUISingleton
@@ -118,7 +118,7 @@ constructor(
audioManager.ringerModeInternal = newRingerMode
}
}
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
@@ -140,11 +140,11 @@ constructor(
.getSharedPreferences(
MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME,
Context.MODE_PRIVATE,
- userTracker.userId
+ userTracker.userId,
)
.getInt(
LAST_NON_SILENT_RINGER_MODE_KEY,
- ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE
+ ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE,
)
}
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 eafa1cea59f3..cb7702e090d0 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
@@ -30,7 +30,6 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
@@ -72,21 +71,15 @@ constructor(
override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
val hasCards =
getPaymentCards(response.walletCards)?.isNotEmpty() == true
- trySendWithFailureLogging(
- hasCards,
- TAG,
- )
+ trySendWithFailureLogging(hasCards, TAG)
}
override fun onWalletCardRetrievalError(error: GetWalletCardsError) {
Log.e(
TAG,
- "Wallet card retrieval error, message: \"${error?.message}\""
- )
- trySendWithFailureLogging(
- null,
- TAG,
+ "Wallet card retrieval error, message: \"${error?.message}\"",
)
+ trySendWithFailureLogging(null, TAG)
}
}
@@ -94,7 +87,7 @@ constructor(
callback,
QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE,
- QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE
+ QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE,
)
withContext(backgroundDispatcher) {
@@ -107,7 +100,7 @@ constructor(
walletController.unregisterWalletChangeObservers(
QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE,
QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE,
- QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE
+ QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE,
)
}
}
@@ -117,11 +110,7 @@ constructor(
if (hasCards == null) {
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
} else {
- state(
- isWalletAvailable(),
- hasCards,
- walletController.walletClient.tileIcon,
- )
+ state(isWalletAvailable(), hasCards, walletController.walletClient.tileIcon)
}
flowOf(state)
}
@@ -135,28 +124,28 @@ constructor(
explanation =
context.getString(
R.string.wallet_quick_affordance_unavailable_install_the_app
- ),
+ )
)
queryCards().isEmpty() ->
KeyguardQuickAffordanceConfig.PickerScreenState.Disabled(
explanation =
context.getString(
R.string.wallet_quick_affordance_unavailable_configure_the_app
- ),
+ )
)
else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default()
}
}
override fun onTriggered(
- expandable: Expandable?,
+ expandable: Expandable?
): KeyguardQuickAffordanceConfig.OnTriggeredResult {
walletController.startQuickAccessUiIntent(
activityStarter,
expandable?.activityTransitionController(),
/* hasCard= */ true,
)
- return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(true)
}
private suspend fun queryCards(): List<WalletCard> {
@@ -199,10 +188,8 @@ constructor(
Icon.Loaded(
drawable = tileIcon,
contentDescription =
- ContentDescription.Resource(
- res = R.string.accessibility_wallet_button,
- ),
- ),
+ ContentDescription.Resource(res = R.string.accessibility_wallet_button),
+ )
)
} else {
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index ae55825c9842..9c2daf52c5df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -29,7 +29,6 @@ import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
import com.android.systemui.dock.DockManager
@@ -62,6 +61,7 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
@@ -101,6 +101,14 @@ constructor(
val launchingAffordance: StateFlow<Boolean> = repository.get().launchingAffordance.asStateFlow()
/**
+ * Whether a [KeyguardQuickAffordanceConfig.OnTriggeredResult] indicated that the system
+ * launched an activity or showed a dialog.
+ */
+ private val _launchingFromTriggeredResult =
+ MutableStateFlow<KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult?>(null)
+ val launchingFromTriggeredResult = _launchingFromTriggeredResult.asStateFlow()
+
+ /**
* Whether the UI should use the long press gesture to activate quick affordances.
*
* If `false`, the UI goes back to using single taps.
@@ -187,18 +195,45 @@ constructor(
metricsLogger.logOnShortcutTriggered(slotId, configKey)
when (val result = config.onTriggered(expandable)) {
- is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity ->
+ is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity -> {
+ setLaunchingFromTriggeredResult(
+ KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(
+ launched = true,
+ configKey,
+ )
+ )
launchQuickAffordance(
intent = result.intent,
canShowWhileLocked = result.canShowWhileLocked,
expandable = expandable,
)
- is KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled -> Unit
- is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog ->
+ }
+ is KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled -> {
+ setLaunchingFromTriggeredResult(
+ KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(
+ result.actionLaunched,
+ configKey,
+ )
+ )
+ }
+ is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog -> {
+ setLaunchingFromTriggeredResult(
+ KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult(
+ launched = true,
+ configKey,
+ )
+ )
showDialog(result.dialog, result.expandable)
+ }
}
}
+ fun setLaunchingFromTriggeredResult(
+ launchingResult: KeyguardQuickAffordanceConfig.LaunchingFromTriggeredResult?
+ ) {
+ _launchingFromTriggeredResult.value = launchingResult
+ }
+
/**
* Selects an affordance with the given ID on the slot with the given ID.
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index aa44b6d46289..382436cf9397 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.domain.interactor.scenetransition
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.CoreStartable
@@ -38,7 +39,6 @@ import java.util.UUID
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* This class listens to scene framework scene transitions and manages keyguard transition framework
@@ -111,7 +111,10 @@ constructor(
if (currentTransitionId == null) return
if (prevTransition !is ObservableTransitionState.Transition) return
- if (idle.currentScene == prevTransition.toContent) {
+ if (
+ idle.currentScene == prevTransition.toContent ||
+ idle.currentOverlays.contains(prevTransition.toContent)
+ ) {
finishCurrentTransition()
} else {
val targetState =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
index e7803c5e964c..a4a5ba691965 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaVibrations.kt
@@ -17,12 +17,23 @@
package com.android.systemui.keyguard.ui.binder
import android.os.VibrationEffect
+import com.android.systemui.Flags
import kotlin.time.Duration.Companion.milliseconds
object KeyguardBottomAreaVibrations {
- val ShakeAnimationDuration = 300.milliseconds
- const val ShakeAnimationCycles = 5f
+ val ShakeAnimationDuration =
+ if (Flags.msdlFeedback()) {
+ 285.milliseconds
+ } else {
+ 300.milliseconds
+ }
+ val ShakeAnimationCycles =
+ if (Flags.msdlFeedback()) {
+ 3f
+ } else {
+ 5f
+ }
private const val SmallVibrationScale = 0.3f
private const val BigVibrationScale = 0.6f
@@ -32,7 +43,7 @@ object KeyguardBottomAreaVibrations {
.apply {
val vibrationDelayMs =
(ShakeAnimationDuration.inWholeMilliseconds / (ShakeAnimationCycles * 2))
- .toInt()
+ .toInt()
val vibrationCount = ShakeAnimationCycles.toInt() * 2
repeat(vibrationCount) {
@@ -47,29 +58,13 @@ object KeyguardBottomAreaVibrations {
val Activated =
VibrationEffect.startComposition()
- .addPrimitive(
- VibrationEffect.Composition.PRIMITIVE_TICK,
- BigVibrationScale,
- 0,
- )
- .addPrimitive(
- VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
- 0.1f,
- 0,
- )
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, BigVibrationScale, 0)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.1f, 0)
.compose()
val Deactivated =
VibrationEffect.startComposition()
- .addPrimitive(
- VibrationEffect.Composition.PRIMITIVE_TICK,
- BigVibrationScale,
- 0,
- )
- .addPrimitive(
- VibrationEffect.Composition.PRIMITIVE_QUICK_FALL,
- 0.1f,
- 0,
- )
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, BigVibrationScale, 0)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.1f, 0)
.compose()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 8725cdd273df..8a2e3dd791c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -20,6 +20,7 @@ package com.android.systemui.keyguard.ui.binder
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.graphics.drawable.Animatable2
+import android.os.VibrationEffect
import android.util.Size
import android.view.View
import android.view.ViewGroup
@@ -33,25 +34,27 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
+import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceHapticViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.doOnEnd
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.MSDLPlayer
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
/** This is only for a SINGLE Quick affordance */
@SysUISingleton
@@ -60,8 +63,9 @@ class KeyguardQuickAffordanceViewBinder
constructor(
private val falsingManager: FalsingManager?,
private val vibratorHelper: VibratorHelper?,
+ private val msdlPlayer: MSDLPlayer,
private val logger: KeyguardQuickAffordancesLogger,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
+ private val hapticsViewModelFactory: KeyguardQuickAffordanceHapticViewModel.Factory,
) {
private val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
@@ -88,6 +92,12 @@ constructor(
): Binding {
val button = view as ImageView
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
+ val hapticsViewModel =
+ if (Flags.msdlFeedback()) {
+ hapticsViewModelFactory.create(viewModel)
+ } else {
+ null
+ }
val disposableHandle =
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -98,15 +108,12 @@ constructor(
viewModel = buttonModel,
messageDisplayer = messageDisplayer,
)
+ hapticsViewModel?.updateActivatedHistory(buttonModel.isActivated)
}
}
launch {
- updateButtonAlpha(
- view = button,
- viewModel = viewModel,
- alphaFlow = alpha,
- )
+ updateButtonAlpha(view = button, viewModel = viewModel, alphaFlow = alpha)
}
launch {
@@ -117,6 +124,32 @@ constructor(
}
}
}
+
+ if (Flags.msdlFeedback()) {
+ launch {
+ hapticsViewModel
+ ?.quickAffordanceHapticState
+ ?.filter {
+ it !=
+ KeyguardQuickAffordanceHapticViewModel.HapticState
+ .NO_HAPTICS
+ }
+ ?.collect { state ->
+ when (state) {
+ KeyguardQuickAffordanceHapticViewModel.HapticState
+ .TOGGLE_ON -> msdlPlayer.playToken(MSDLToken.SWITCH_ON)
+ KeyguardQuickAffordanceHapticViewModel.HapticState
+ .TOGGLE_OFF ->
+ msdlPlayer.playToken(MSDLToken.SWITCH_OFF)
+ KeyguardQuickAffordanceHapticViewModel.HapticState.LAUNCH ->
+ msdlPlayer.playToken(MSDLToken.LONG_PRESS)
+ KeyguardQuickAffordanceHapticViewModel.HapticState
+ .NO_HAPTICS -> Unit
+ }
+ hapticsViewModel.resetLaunchingFromTriggeredResult()
+ }
+ }
+ }
}
}
@@ -178,7 +211,7 @@ constructor(
com.android.internal.R.color.materialColorOnPrimaryFixed
} else {
com.android.internal.R.color.materialColorOnSurface
- },
+ }
)
)
@@ -221,12 +254,7 @@ constructor(
.getDimensionPixelSize(R.dimen.keyguard_affordance_shake_amplitude)
.toFloat()
val shakeAnimator =
- ObjectAnimator.ofFloat(
- view,
- "translationX",
- -amplitude / 2,
- amplitude / 2,
- )
+ ObjectAnimator.ofFloat(view, "translationX", -amplitude / 2, amplitude / 2)
shakeAnimator.duration =
KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
shakeAnimator.interpolator =
@@ -234,11 +262,17 @@ constructor(
shakeAnimator.doOnEnd { view.translationX = 0f }
shakeAnimator.start()
- vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+ vibratorHelper?.playFeedback(KeyguardBottomAreaVibrations.Shake, msdlPlayer)
logger.logQuickAffordanceTapped(viewModel.configKey)
}
view.onLongClickListener =
- OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
+ OnLongClickListener(
+ falsingManager,
+ viewModel,
+ vibratorHelper,
+ onTouchListener,
+ msdlPlayer,
+ )
} else {
view.setOnClickListener(OnClickListener(viewModel, checkNotNull(falsingManager)))
}
@@ -268,7 +302,7 @@ constructor(
Size(
view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
- ),
+ )
)
}
@@ -297,7 +331,8 @@ constructor(
private val falsingManager: FalsingManager?,
private val viewModel: KeyguardQuickAffordanceViewModel,
private val vibratorHelper: VibratorHelper?,
- private val onTouchListener: KeyguardQuickAffordanceOnTouchListener
+ private val onTouchListener: KeyguardQuickAffordanceOnTouchListener,
+ private val msdlPlayer: MSDLPlayer,
) : View.OnLongClickListener {
override fun onLongClick(view: View): Boolean {
if (falsingManager?.isFalseLongTap(FalsingManager.MODERATE_PENALTY) == true) {
@@ -312,12 +347,13 @@ constructor(
slotId = viewModel.slotId,
)
)
- vibratorHelper?.vibrate(
+ vibratorHelper?.playFeedback(
if (viewModel.isActivated) {
KeyguardBottomAreaVibrations.Activated
} else {
KeyguardBottomAreaVibrations.Deactivated
- }
+ },
+ msdlPlayer,
)
}
@@ -328,7 +364,15 @@ constructor(
override fun onLongClickUseDefaultHapticFeedback(view: View) = false
}
- private data class ConfigurationBasedDimensions(
- val buttonSizePx: Size,
- )
+ private data class ConfigurationBasedDimensions(val buttonSizePx: Size)
+}
+
+private fun VibratorHelper.playFeedback(effect: VibrationEffect, msdlPlayer: MSDLPlayer) {
+ if (!Flags.msdlFeedback()) {
+ vibrate(effect)
+ } else {
+ if (effect == KeyguardBottomAreaVibrations.Shake) {
+ msdlPlayer.playToken(MSDLToken.FAILURE)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index a2ce4ec5ce9b..6d270b219c81 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -127,21 +127,18 @@ object KeyguardRootViewBinder {
if (Flags.nonTouchscreenDevicesBypassFalsing()) {
if (
event.action == MotionEvent.ACTION_DOWN &&
- event.buttonState == MotionEvent.BUTTON_PRIMARY &&
- !event.isTouchscreenSource()
+ event.buttonState == MotionEvent.BUTTON_PRIMARY &&
+ !event.isTouchscreenSource()
) {
consumed = true
} else if (
- event.action == MotionEvent.ACTION_UP &&
- !event.isTouchscreenSource()
+ event.action == MotionEvent.ACTION_UP && !event.isTouchscreenSource()
) {
statusBarKeyguardViewManager?.showBouncer(true)
consumed = true
}
}
- viewModel.setRootViewLastTapPosition(
- Point(event.x.toInt(), event.y.toInt())
- )
+ viewModel.setRootViewLastTapPosition(Point(event.x.toInt(), event.y.toInt()))
}
consumed
}
@@ -172,7 +169,6 @@ object KeyguardRootViewBinder {
launch("$TAG#alpha") {
viewModel.alpha(viewState).collect { alpha ->
view.alpha = alpha
- childViews[statusViewId]?.alpha = alpha
childViews[burnInLayerId]?.alpha = alpha
}
}
@@ -253,18 +249,6 @@ object KeyguardRootViewBinder {
}
launch {
- viewModel.burnInLayerAlpha.collect { alpha ->
- childViews[statusViewId]?.alpha = alpha
- }
- }
-
- launch {
- viewModel.lockscreenStateAlpha(viewState).collect { alpha ->
- childViews[statusViewId]?.alpha = alpha
- }
- }
-
- launch {
viewModel.scale.collect { scaleViewModel ->
if (scaleViewModel.scaleClockOnly) {
// For clocks except weather clock, we have scale transition besides
@@ -553,7 +537,6 @@ object KeyguardRootViewBinder {
return device?.supportsSource(InputDevice.SOURCE_TOUCHSCREEN) == true
}
- private val statusViewId = R.id.keyguard_status_view
private val burnInLayerId = R.id.burn_in_layer
private val aodNotificationIconContainerId = R.id.aod_notification_icon_container
private val largeClockId = customR.id.lockscreen_clock_view_large
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 090b65922d2d..6fb31c0e4191 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -48,19 +48,16 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.core.view.isInvisible
-import com.android.internal.policy.SystemBarUtils
import com.android.keyguard.ClockEventController
-import com.android.keyguard.KeyguardClockSwitch
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder
import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel
-import com.android.systemui.coroutines.newTracingContext
+import com.android.systemui.customization.R as customR
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.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
@@ -80,7 +77,6 @@ import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.systemui.shared.clocks.DefaultClockController
import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants
@@ -91,18 +87,13 @@ import com.android.systemui.util.settings.SecureSettings
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.json.JSONException
import org.json.JSONObject
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.customization.R as customR
/** Renders the preview of the lock screen. */
class KeyguardPreviewRenderer
@@ -110,7 +101,6 @@ class KeyguardPreviewRenderer
@AssistedInject
constructor(
@Application private val context: Context,
- @Application applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
@Main private val mainHandler: Handler,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@@ -157,8 +147,6 @@ constructor(
val surfacePackage: SurfaceControlViewHost.SurfacePackage
get() = checkNotNull(host.surfacePackage)
- private lateinit var largeClockHostView: FrameLayout
- private lateinit var smallClockHostView: FrameLayout
private var smartSpaceView: View? = null
private val disposables = DisposableHandles()
@@ -166,29 +154,18 @@ constructor(
private val shortcutsBindings = mutableSetOf<KeyguardQuickAffordanceViewBinder.Binding>()
- private val coroutineScope: CoroutineScope
-
@Style.Type private var themeStyle: Int? = null
init {
- coroutineScope =
- CoroutineScope(
- applicationScope.coroutineContext +
- Job() +
- newTracingContext("KeyguardPreviewRenderer")
- )
- disposables += DisposableHandle { coroutineScope.cancel() }
clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData())
-
quickAffordancesCombinedViewModel.enablePreviewMode(
initiallySelectedSlotId =
- bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID)
- ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID)
+ ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
- if (MigrateClocksToBlueprint.isEnabled) {
- clockViewModel.shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance
- }
+
+ clockViewModel.shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance
runBlocking(mainDispatcher) {
host =
SurfaceControlViewHost(
@@ -348,6 +325,7 @@ constructor(
smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
}
+ @OptIn(ExperimentalCoroutinesApi::class)
private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
val keyguardRootView = KeyguardRootView(previewContext, null)
rootView.addView(
@@ -358,34 +336,23 @@ constructor(
),
)
- setUpUdfps(
- previewContext,
- if (MigrateClocksToBlueprint.isEnabled) keyguardRootView else rootView,
- )
+ setUpUdfps(previewContext, keyguardRootView)
setupShortcuts(keyguardRootView)
if (!shouldHideClock) {
setUpClock(previewContext, rootView)
- if (MigrateClocksToBlueprint.isEnabled) {
- KeyguardPreviewClockViewBinder.bind(
- keyguardRootView,
- clockViewModel,
- clockRegistry,
- ::updateClockAppearance,
- ClockPreviewConfig(
- previewContext,
- getPreviewShadeLayoutWide(display!!),
- SceneContainerFlag.isEnabled,
- ),
- )
- } else {
- KeyguardPreviewClockViewBinder.bind(
- largeClockHostView,
- smallClockHostView,
- clockViewModel,
- )
- }
+ KeyguardPreviewClockViewBinder.bind(
+ keyguardRootView,
+ clockViewModel,
+ clockRegistry,
+ ::updateClockAppearance,
+ ClockPreviewConfig(
+ previewContext,
+ getPreviewShadeLayoutWide(display!!),
+ SceneContainerFlag.isEnabled,
+ ),
+ )
}
setUpSmartspace(previewContext, rootView)
@@ -451,82 +418,22 @@ constructor(
.inflate(R.layout.udfps_keyguard_preview, parentView, false) as View
// Place the UDFPS view in the proper sensor location
- if (MigrateClocksToBlueprint.isEnabled) {
- val lockId = KeyguardPreviewClockViewBinder.lockId
- finger.id = lockId
- parentView.addView(finger)
- val cs = ConstraintSet()
- cs.clone(parentView as ConstraintLayout)
- cs.apply {
- constrainWidth(lockId, sensorBounds.width())
- constrainHeight(lockId, sensorBounds.height())
- connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top)
- connect(lockId, START, PARENT_ID, START, sensorBounds.left)
- }
- cs.applyTo(parentView)
- } else {
- val fingerprintLayoutParams =
- FrameLayout.LayoutParams(sensorBounds.width(), sensorBounds.height())
- fingerprintLayoutParams.setMarginsRelative(
- sensorBounds.left,
- sensorBounds.top,
- sensorBounds.right,
- sensorBounds.bottom,
- )
- parentView.addView(finger, fingerprintLayoutParams)
+ val lockId = KeyguardPreviewClockViewBinder.lockId
+ finger.id = lockId
+ parentView.addView(finger)
+ val cs = ConstraintSet()
+ cs.clone(parentView as ConstraintLayout)
+ cs.apply {
+ constrainWidth(lockId, sensorBounds.width())
+ constrainHeight(lockId, sensorBounds.height())
+ connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top)
+ connect(lockId, START, PARENT_ID, START, sensorBounds.left)
}
+ cs.applyTo(parentView)
}
private fun setUpClock(previewContext: Context, parentView: ViewGroup) {
val resources = parentView.resources
- if (!MigrateClocksToBlueprint.isEnabled) {
- largeClockHostView = FrameLayout(previewContext)
- largeClockHostView.layoutParams =
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT,
- )
- largeClockHostView.isInvisible = true
- parentView.addView(largeClockHostView)
-
- smallClockHostView = FrameLayout(previewContext)
- val layoutParams =
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT,
- resources.getDimensionPixelSize(customR.dimen.small_clock_height),
- )
- layoutParams.topMargin =
- SystemBarUtils.getStatusBarHeight(previewContext) +
- resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top)
- smallClockHostView.layoutParams = layoutParams
- smallClockHostView.setPaddingRelative(
- /* start = */ resources.getDimensionPixelSize(customR.dimen.clock_padding_start),
- /* top = */ 0,
- /* end = */ 0,
- /* bottom = */ 0,
- )
- smallClockHostView.clipChildren = false
- parentView.addView(smallClockHostView)
- smallClockHostView.isInvisible = true
- }
-
- // TODO (b/283465254): Move the listeners to KeyguardClockRepository
- if (!MigrateClocksToBlueprint.isEnabled) {
- val clockChangeListener =
- object : ClockRegistry.ClockChangeListener {
- override fun onCurrentClockChanged() {
- onClockChanged()
- }
- }
- clockRegistry.registerClockChangeListener(clockChangeListener)
- disposables += DisposableHandle {
- clockRegistry.unregisterClockChangeListener(clockChangeListener)
- }
-
- clockController.registerListeners(parentView)
- disposables += DisposableHandle { clockController.unregisterListeners() }
- }
-
val receiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
@@ -544,38 +451,9 @@ constructor(
},
)
disposables += DisposableHandle { broadcastDispatcher.unregisterReceiver(receiver) }
-
- if (!MigrateClocksToBlueprint.isEnabled) {
- val layoutChangeListener =
- View.OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
- if (clockController.clock !is DefaultClockController) {
- clockController.clock
- ?.largeClock
- ?.events
- ?.onTargetRegionChanged(
- KeyguardClockSwitch.getLargeClockRegion(parentView)
- )
- clockController.clock
- ?.smallClock
- ?.events
- ?.onTargetRegionChanged(
- KeyguardClockSwitch.getSmallClockRegion(parentView)
- )
- }
- }
- parentView.addOnLayoutChangeListener(layoutChangeListener)
- disposables += DisposableHandle {
- parentView.removeOnLayoutChangeListener(layoutChangeListener)
- }
- }
-
- onClockChanged()
}
private suspend fun updateClockAppearance(clock: ClockController, resources: Resources) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- clockController.clock = clock
- }
val colors = wallpaperColors
if (clockRegistry.seedColor == null && colors != null) {
// Seed color null means users do not override any color on the clock. The default
@@ -601,9 +479,7 @@ constructor(
// In clock preview, we should have a seed color for clock
// before setting clock to clockEventController to avoid updateColor with seedColor == null
// So in update colors, it should already have the correct theme in clockFaceController
- if (MigrateClocksToBlueprint.isEnabled) {
- clockController.clock = clock
- }
+ clockController.clock = clock
// When set clock to clockController,it will reset fontsize based on context.resources
// We need to override it with overlaid resources
clock.largeClock.events.onFontSettingChanged(
@@ -611,19 +487,6 @@ constructor(
)
}
- private fun onClockChanged() {
- if (MigrateClocksToBlueprint.isEnabled) {
- return
- }
- coroutineScope.launch {
- val clock = clockRegistry.createCurrentClock()
- clockController.clock = clock
- updateClockAppearance(clock, context.resources)
- updateLargeClock(clock)
- updateSmallClock(clock)
- }
- }
-
private fun setupCommunalTutorialIndicator(keyguardRootView: ConstraintLayout) {
keyguardRootView.findViewById<TextView>(R.id.communal_tutorial_indicator)?.let {
indicatorView ->
@@ -657,34 +520,6 @@ constructor(
}
}
- private fun updateLargeClock(clock: ClockController) {
- if (MigrateClocksToBlueprint.isEnabled) {
- return
- }
- clock.largeClock.events.onTargetRegionChanged(
- KeyguardClockSwitch.getLargeClockRegion(largeClockHostView)
- )
- if (shouldHighlightSelectedAffordance) {
- clock.largeClock.view.alpha = DIM_ALPHA
- }
- largeClockHostView.removeAllViews()
- largeClockHostView.addView(clock.largeClock.view)
- }
-
- private fun updateSmallClock(clock: ClockController) {
- if (MigrateClocksToBlueprint.isEnabled) {
- return
- }
- clock.smallClock.events.onTargetRegionChanged(
- KeyguardClockSwitch.getSmallClockRegion(smallClockHostView)
- )
- if (shouldHighlightSelectedAffordance) {
- clock.smallClock.view.alpha = DIM_ALPHA
- }
- smallClockHostView.removeAllViews()
- smallClockHostView.addView(clock.smallClock.view)
- }
-
private fun getPreviewShadeLayoutWide(display: Display): Boolean {
return if (display.displayId == 0) {
shadeInteractor.isShadeLayoutWide.value
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index 729759a9ad00..5d463f72d8b2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -23,7 +23,6 @@ import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.shade.ShadeDisplayAware
@@ -50,16 +49,13 @@ constructor(
sharedNotificationContainerBinder,
) {
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
constraintSet.apply {
connect(
R.id.nssl_placeholder,
TOP,
PARENT_ID,
TOP,
- context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin),
)
connect(R.id.nssl_placeholder, START, PARENT_ID, START)
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 1c897237fe89..fb311a533aa2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -24,7 +24,6 @@ import com.android.app.animation.Interpolators
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -194,12 +193,7 @@ constructor(
(!useAltAod) && keyguardClockViewModel.clockSize.value == ClockSize.LARGE
val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolated).toInt()
- val translationY =
- if (MigrateClocksToBlueprint.isEnabled) {
- max(params.topInset - params.minViewY, burnInY)
- } else {
- max(params.topInset, params.minViewY + burnInY) - params.minViewY
- }
+ val translationY = max(params.topInset - params.minViewY, burnInY)
BurnInModel(
translationX = MathUtils.lerp(0, burnIn.translationX, interpolated).toInt(),
translationY = translationY,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt
new file mode 100644
index 000000000000..890628c31c55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceHapticViewModel.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+class KeyguardQuickAffordanceHapticViewModel
+@AssistedInject
+constructor(
+ @Assisted quickAffordanceViewModel: Flow<KeyguardQuickAffordanceViewModel>,
+ private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
+) {
+
+ private val activatedHistory = MutableStateFlow(ActivatedHistory(false))
+
+ private val launchingHapticState: Flow<HapticState> =
+ combine(
+ quickAffordanceViewModel.map { it.configKey },
+ quickAffordanceInteractor.launchingFromTriggeredResult,
+ ) { key, launchingResult ->
+ val validKey = key != null && key == launchingResult?.configKey
+ if (validKey && launchingResult?.launched == true) {
+ HapticState.LAUNCH
+ } else {
+ HapticState.NO_HAPTICS
+ }
+ }
+ .distinctUntilChanged()
+
+ private val toggleHapticState: Flow<HapticState> =
+ activatedHistory
+ .map { history ->
+ when {
+ history.previousValue == false && history.currentValue -> HapticState.TOGGLE_ON
+ history.previousValue == true && !history.currentValue -> HapticState.TOGGLE_OFF
+ else -> HapticState.NO_HAPTICS
+ }
+ }
+ .distinctUntilChanged()
+
+ val quickAffordanceHapticState =
+ merge(launchingHapticState, toggleHapticState).distinctUntilChanged()
+
+ fun resetLaunchingFromTriggeredResult() =
+ quickAffordanceInteractor.setLaunchingFromTriggeredResult(null)
+
+ fun updateActivatedHistory(isActivated: Boolean) {
+ activatedHistory.value =
+ ActivatedHistory(
+ currentValue = isActivated,
+ previousValue = activatedHistory.value.currentValue,
+ )
+ }
+
+ enum class HapticState {
+ TOGGLE_ON,
+ TOGGLE_OFF,
+ LAUNCH,
+ NO_HAPTICS,
+ }
+
+ private data class ActivatedHistory(
+ val currentValue: Boolean,
+ val previousValue: Boolean? = null,
+ )
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ quickAffordanceViewModel: Flow<KeyguardQuickAffordanceViewModel>
+ ): KeyguardQuickAffordanceHapticViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 9066d466ceca..eaba5d5a149c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -29,7 +29,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.PulseExpansionInteractor
import com.android.systemui.keyguard.shared.model.Edge
-import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -130,7 +129,6 @@ constructor(
PrimaryBouncerToLockscreenTransitionViewModel,
private val screenOffAnimationController: ScreenOffAnimationController,
private val aodBurnInViewModel: AodBurnInViewModel,
- private val aodAlphaViewModel: AodAlphaViewModel,
private val shadeInteractor: ShadeInteractor,
) {
val burnInLayerVisibility: Flow<Int> =
@@ -284,15 +282,6 @@ constructor(
.distinctUntilChanged()
}
- /** Specific alpha value for elements visible during [KeyguardState.LOCKSCREEN] */
- @Deprecated("only used for legacy status view")
- fun lockscreenStateAlpha(viewState: ViewStateAccessor): Flow<Float> {
- return aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState)
- }
-
- /** For elements that appear and move during the animation -> AOD */
- val burnInLayerAlpha: Flow<Float> = aodAlphaViewModel.alpha
-
val translationY: Flow<Float> = aodBurnInViewModel.movement.map { it.translationY.toFloat() }
val translationX: Flow<StateToValue> =
diff --git a/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt b/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt
new file mode 100644
index 000000000000..dd2525f5ca45
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.lottie
+
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieListener
+import com.airbnb.lottie.LottieTask
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+/**
+ * Suspends until [LottieTask] is finished with a result or a failure.
+ *
+ * @return result of the [LottieTask] when it's successful
+ */
+suspend fun LottieTask<LottieComposition>.await() =
+ suspendCancellableCoroutine<LottieComposition> { continuation ->
+ val resultListener =
+ LottieListener<LottieComposition> { result ->
+ with(continuation) { if (!isCancelled && !isCompleted) resume(result) }
+ }
+ val failureListener =
+ LottieListener<Throwable> { throwable ->
+ with(continuation) {
+ if (!isCancelled && !isCompleted) resumeWithException(throwable)
+ }
+ }
+ addListener(resultListener)
+ addFailureListener(failureListener)
+ continuation.invokeOnCancellation {
+ removeListener(resultListener)
+ removeFailureListener(failureListener)
+ }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index c32bd403d2e8..b4dabbe036e9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -34,13 +34,14 @@ import android.view.ViewGroup
import android.view.ViewGroupOverlay
import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import com.android.keyguard.KeyguardViewController
import com.android.systemui.Flags.mediaControlsLockscreenShadeBugFix
import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -68,7 +69,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapLatest
-import com.android.app.tracing.coroutines.launchTraced as launch
private val TAG: String = MediaHierarchyManager::class.java.simpleName
@@ -115,7 +115,7 @@ constructor(
wakefulnessLifecycle: WakefulnessLifecycle,
shadeInteractor: ShadeInteractor,
private val secureSettings: SecureSettings,
- @Main private val handler: Handler,
+ @Background private val handler: Handler,
@Application private val coroutineScope: CoroutineScope,
private val splitShadeStateController: SplitShadeStateController,
private val logger: MediaViewLogger,
@@ -631,7 +631,7 @@ constructor(
}
}
}
- secureSettings.registerContentObserverForUserSync(
+ secureSettings.registerContentObserverForUserAsync(
Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
settingsObserver,
UserHandle.USER_ALL,
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 311cbfb7e632..b2696aeaabfc 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -132,7 +132,7 @@ constructor(
val isDefaultNotesAppSet =
noteTaskInfoResolver.resolveInfo(
QUICK_AFFORDANCE,
- user = controller.getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+ user = controller.getUserForHandlingNotesTaking(QUICK_AFFORDANCE),
) != null
return when {
isEnabled && isDefaultNotesAppSet -> PickerScreenState.Default()
@@ -158,7 +158,7 @@ constructor(
override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
controller.showNoteTask(entryPoint = QUICK_AFFORDANCE)
- return OnTriggeredResult.Handled
+ return OnTriggeredResult.Handled(true)
}
}
@@ -194,7 +194,7 @@ private fun RoleManager.createNotesRoleFlow(
fun isDefaultNotesAppSetForUser() =
noteTaskInfoResolver.resolveInfo(
QUICK_AFFORDANCE,
- user = noteTaskController.getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+ user = noteTaskController.getUserForHandlingNotesTaking(QUICK_AFFORDANCE),
) != null
trySendBlocking(isDefaultNotesAppSetForUser())
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt b/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt
index ef7e7eb59898..84b995e1cd28 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt
@@ -22,16 +22,21 @@ import com.android.systemui.qs.tileimpl.QSTileImpl
/**
* Creates a [QSTile.Icon] from an [Icon].
- * * [Icon.Loaded] -> [QSTileImpl.DrawableIcon]
+ * * [Icon.Loaded] with null [res] -> [QSTileImpl.DrawableIcon]
+ * * [Icon.Loaded] & with non null [res] -> [QSTileImpl.DrawableIconWithRes]
* * [Icon.Resource] -> [QSTileImpl.ResourceIcon]
*/
fun Icon.asQSTileIcon(): QSTile.Icon {
return when (this) {
is Icon.Loaded -> {
- QSTileImpl.DrawableIcon(this.drawable)
+ if (res == null) {
+ QSTileImpl.DrawableIcon(drawable)
+ } else {
+ QSTileImpl.DrawableIconWithRes(drawable, res)
+ }
}
is Icon.Resource -> {
- QSTileImpl.ResourceIcon.get(this.res)
+ QSTileImpl.ResourceIcon.get(res)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
index 790793eab258..3049a40f18c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
@@ -17,16 +17,16 @@
package com.android.systemui.qs.composefragment.ui
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.ClipOp
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.drawscope.clipRect
-import androidx.compose.ui.graphics.layer.CompositingStrategy
-import androidx.compose.ui.graphics.layer.drawLayer
+import androidx.compose.ui.graphics.graphicsLayer
/**
* Clipping modifier for clipping out the notification scrim as it slides over QS. It will clip out
@@ -34,16 +34,16 @@ import androidx.compose.ui.graphics.layer.drawLayer
* from the QS container.
*/
fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams): Modifier {
- return this.drawWithCache {
+ return this.graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
+ .drawWithContent {
+ drawContent()
val params = clipParams()
val left = -params.leftInset.toFloat()
val right = size.width + params.rightInset.toFloat()
val top = params.top.toFloat()
val bottom = params.bottom.toFloat()
- val graphicsLayer = obtainGraphicsLayer()
- graphicsLayer.compositingStrategy = CompositingStrategy.Offscreen
- graphicsLayer.record {
- drawContent()
+ val clipSize = Size(right - left, bottom - top)
+ if (!clipSize.isEmpty()) {
clipRect {
drawRoundRect(
color = Color.Black,
@@ -54,9 +54,6 @@ fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams
)
}
}
- onDrawWithContent {
- drawLayer(graphicsLayer)
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 873059ee08db..e7fa27159e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -675,17 +675,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
}
private void add() {
- if (addFromPosition(getLayoutPosition())) {
- itemView.announceForAccessibility(
- itemView.getContext().getText(R.string.accessibility_qs_edit_tile_added));
- }
+ addFromPosition(getLayoutPosition());
}
private void remove() {
- if (removeFromPosition(getLayoutPosition())) {
- itemView.announceForAccessibility(
- itemView.getContext().getText(R.string.accessibility_qs_edit_tile_removed));
- }
+ removeFromPosition(getLayoutPosition());
}
boolean isCurrentTile() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
index 989fc0fd6f44..5ba1527dbf69 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
@@ -22,6 +22,7 @@ import android.os.Looper
import android.service.quicksettings.Tile
import com.android.internal.logging.MetricsLogger
import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -92,7 +93,8 @@ constructor(
state?.apply {
this.state = tileState.activationState.legacyState
- icon = maybeLoadResourceIcon(tileState.iconRes ?: R.drawable.ic_qs_notes)
+ icon =
+ maybeLoadResourceIcon((tileState.icon as Icon.Loaded).res ?: R.drawable.ic_qs_notes)
label = tileState.label
contentDescription = tileState.contentDescription
expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 19b45d50c594..7516ca030d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -193,7 +193,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
if (mJob == null) {
mJob = WifiUtils.checkWepAllowed(mContext, mCoroutineScope, wifiEntry.getSsid(),
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, intent -> {
- mInternetDialogController.startActivity(intent, view);
+ mInternetDialogController.startActivityForDialog(intent);
return null;
}, () -> {
wifiConnect(wifiEntry, view);
@@ -211,7 +211,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
true /* connectForCaller */);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
- mContext.startActivity(intent);
+ mInternetDialogController.startActivityForDialog(intent);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index dbe1ae90b3f6..7036ef914a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -781,6 +781,10 @@ public class InternetDialogController implements AccessPointController.AccessPoi
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0, controller);
}
+ void startActivityForDialog(Intent intent) {
+ mActivityStarter.startActivity(intent, false /* dismissShade */);
+ }
+
void launchNetworkSetting(View view) {
startActivity(getSettingsIntent(), view);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index 34c2ec90f1e8..80d429ce2716 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -35,13 +35,13 @@ constructor(@ShadeDisplayAware private val resources: Resources, val theme: Them
override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes =
+ val iconRes =
if (data.isEnabled) {
R.drawable.qs_airplane_icon_on
} else {
R.drawable.qs_airplane_icon_off
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
index a72992db4496..d56d9944dbb8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
@@ -84,8 +84,8 @@ constructor(
secondaryLabel = resources.getString(R.string.qs_alarm_tile_no_alarm)
}
}
- iconRes = R.drawable.ic_alarm
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = R.drawable.ic_alarm
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
sideViewIcon = QSTileState.SideViewIcon.Chevron
contentDescription = label
supportedActions = setOf(QSTileState.UserAction.CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
index e116d8cef2ee..72759c5bb066 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
@@ -38,10 +38,10 @@ constructor(
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.battery_detail_switch_title)
contentDescription = label
- iconRes =
+ val iconRes =
if (data.isPowerSaving) R.drawable.qs_battery_saver_icon_on
else R.drawable.qs_battery_saver_icon_off
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
sideViewIcon = QSTileState.SideViewIcon.None
if (data.isPluggedIn) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
index 21b9f659dde4..e5a0fe8ed048 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
@@ -37,8 +37,8 @@ constructor(
override fun map(config: QSTileConfig, data: ColorCorrectionTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_color_correction)
- iconRes = R.drawable.ic_qs_color_correction
- icon = Icon.Loaded(resources.getDrawable(R.drawable.ic_qs_color_correction)!!, null)
+ val iconRes = R.drawable.ic_qs_color_correction
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
index 2dfb1fc4fe98..32ccba6f1fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -35,14 +35,14 @@ constructor(@ShadeDisplayAware private val resources: Resources, private val the
override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes =
+ val iconRes =
if (data is FlashlightTileModel.FlashlightAvailable && data.isEnabled) {
R.drawable.qs_flashlight_icon_on
} else {
R.drawable.qs_flashlight_icon_off
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
index 7f41cbd322dd..c571b136e18b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
@@ -36,8 +36,8 @@ constructor(
override fun map(config: QSTileConfig, data: FontScalingTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes = R.drawable.ic_qs_font_scaling
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = R.drawable.ic_qs_font_scaling
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
activationState = QSTileState.ActivationState.ACTIVE
sideViewIcon = QSTileState.SideViewIcon.Chevron
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
index 4c302b363c3b..12f71491c7b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
@@ -37,8 +37,8 @@ constructor(
override fun map(config: QSTileConfig, data: HearingDevicesTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.quick_settings_hearing_devices_label)
- iconRes = R.drawable.qs_hearing_devices_icon
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = R.drawable.qs_hearing_devices_icon
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
sideViewIcon = QSTileState.SideViewIcon.Chevron
contentDescription = label
if (data.isAnyActiveHearingDevice) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
index 1a6876d0b765..7ad01e463399 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
@@ -61,11 +61,11 @@ constructor(
when (val dataIcon = data.icon) {
is InternetTileIconModel.ResourceId -> {
- iconRes = dataIcon.resId
icon =
Icon.Loaded(
resources.getDrawable(dataIcon.resId, theme),
contentDescription = null,
+ dataIcon.resId,
)
}
@@ -76,11 +76,11 @@ constructor(
}
is InternetTileIconModel.Satellite -> {
- iconRes = dataIcon.resourceIcon.res // level is inferred from res
icon =
Icon.Loaded(
resources.getDrawable(dataIcon.resourceIcon.res, theme),
contentDescription = null,
+ dataIcon.resourceIcon.res,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
index 8d35b2413bad..05590e803ffa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
@@ -35,7 +35,7 @@ constructor(@ShadeDisplayAware private val resources: Resources, private val the
override fun map(config: QSTileConfig, data: ColorInversionTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_inversion)
-
+ val iconRes: Int
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
@@ -45,7 +45,7 @@ constructor(@ShadeDisplayAware private val resources: Resources, private val the
secondaryLabel = subtitleArray[1]
iconRes = R.drawable.qs_invert_colors_icon_off
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
index 3557c1a4ac9d..afb137e1e92f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
@@ -39,6 +39,7 @@ constructor(@ShadeDisplayAware private val resources: Resources, private val the
Icon.Loaded(
resources.getDrawable(R.drawable.qs_record_issue_icon_on, theme),
null,
+ R.drawable.qs_record_issue_icon_on,
)
} else {
activationState = QSTileState.ActivationState.INACTIVE
@@ -46,6 +47,7 @@ constructor(@ShadeDisplayAware private val resources: Resources, private val the
Icon.Loaded(
resources.getDrawable(R.drawable.qs_record_issue_icon_off, theme),
null,
+ R.drawable.qs_record_issue_icon_off,
)
}
supportedActions = setOf(QSTileState.UserAction.CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
index dfc24a10c491..ced5a4f099a2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -35,13 +35,13 @@ constructor(@ShadeDisplayAware private val resources: Resources, private val the
override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes =
+ val iconRes =
if (data.isEnabled) {
R.drawable.qs_location_icon_on
} else {
R.drawable.qs_location_icon_off
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
label = resources.getString(R.string.quick_settings_location_label)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 9b2880b6d47f..bb08022ee084 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -73,14 +73,15 @@ constructor(
return ModesTileModel(
isActivated = activeModes.isAnyActive(),
icon = tileIcon.icon,
- iconResId = tileIcon.resId,
activeModes = activeModes.modeNames,
)
} else {
return ModesTileModel(
isActivated = activeModes.isAnyActive(),
- icon = context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(),
- iconResId = ModesTile.ICON_RES_ID,
+ icon =
+ context
+ .getDrawable(ModesTile.ICON_RES_ID)!!
+ .asIcon(res = ModesTile.ICON_RES_ID),
activeModes = activeModes.modeNames,
)
}
@@ -92,12 +93,18 @@ constructor(
return if (activeMode != null) {
// ZenIconKey.resPackage is null if its resId is a system icon.
if (activeMode.icon.key.resPackage == null) {
- TileIcon(activeMode.icon.drawable.asIcon(), activeMode.icon.key.resId)
+ TileIcon(
+ activeMode.icon.drawable.asIcon(res = activeMode.icon.key.resId),
+ activeMode.icon.key.resId,
+ )
} else {
TileIcon(activeMode.icon.drawable.asIcon(), null)
}
} else {
- TileIcon(context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(), ModesTile.ICON_RES_ID)
+ TileIcon(
+ context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(res = ModesTile.ICON_RES_ID),
+ ModesTile.ICON_RES_ID,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
index db4812342050..d0eacbc9a957 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
@@ -21,12 +21,10 @@ import com.android.systemui.common.shared.model.Icon
data class ModesTileModel(
val isActivated: Boolean,
val activeModes: List<String>,
- val icon: Icon.Loaded,
-
/**
- * Resource id corresponding to [icon]. Will only be present if it's know to correspond to a
- * resource with a known id in SystemUI (such as resources from `android.R`,
- * `com.android.internal.R`, or `com.android.systemui.res` itself).
+ * icon.res will only be present if it is known to correspond to a resource with a known id in
+ * SystemUI (such as resources from `android.R`, `com.android.internal.R`, or
+ * `com.android.systemui.res` itself).
*/
- val iconResId: Int? = null
+ val icon: Icon.Loaded,
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
index 1507ef4b3b58..99ae3b8db709 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
@@ -34,7 +34,6 @@ constructor(@ShadeDisplayAware private val resources: Resources, val theme: Reso
QSTileDataToStateMapper<ModesTileModel> {
override fun map(config: QSTileConfig, data: ModesTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes = data.iconResId
icon = data.icon
activationState =
if (data.isActivated) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
index 3569e4d0b42c..16b36289ad95 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
@@ -49,7 +49,7 @@ constructor(
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
sideViewIcon = QSTileState.SideViewIcon.None
-
+ val iconRes: Int
if (data.isActivated) {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_nightlight_icon_on
@@ -58,7 +58,7 @@ constructor(
iconRes = R.drawable.qs_nightlight_icon_off
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
secondaryLabel = getSecondaryLabel(data, resources)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
index a5436192af39..ecdd71170cda 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
@@ -35,8 +35,8 @@ constructor(
) : QSTileDataToStateMapper<NotesTileModel> {
override fun map(config: QSTileConfig, data: NotesTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- iconRes = R.drawable.ic_qs_notes
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ val iconRes = R.drawable.ic_qs_notes
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
activationState = QSTileState.ActivationState.INACTIVE
sideViewIcon = QSTileState.SideViewIcon.Chevron
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
index 76f1e8b8760c..5b3ea93ab1ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
@@ -38,8 +38,8 @@ constructor(
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_onehanded)
label = resources.getString(R.string.quick_settings_onehanded_label)
- iconRes = com.android.internal.R.drawable.ic_qs_one_handed_mode
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = com.android.internal.R.drawable.ic_qs_one_handed_mode
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
index c546250e73d2..21e92d3a1972 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
@@ -38,8 +38,8 @@ constructor(
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.qr_code_scanner_title)
contentDescription = label
- iconRes = R.drawable.ic_qr_code_scanner
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = R.drawable.ic_qr_code_scanner
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
sideViewIcon = QSTileState.SideViewIcon.Chevron
supportedActions = setOf(QSTileState.UserAction.CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
index 66d0f96fdcde..66759cdfd1a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
@@ -37,6 +37,7 @@ constructor(
override fun map(config: QSTileConfig, data: ReduceBrightColorsTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
+ val iconRes: Int
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_extra_dim_icon_on
@@ -50,7 +51,7 @@ constructor(
resources
.getStringArray(R.array.tile_states_reduce_brightness)[Tile.STATE_INACTIVE]
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
label =
resources.getString(com.android.internal.R.string.reduce_bright_colors_feature_name)
contentDescription = label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
index a0144221577d..000c7025e32b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
@@ -42,7 +42,7 @@ constructor(
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.quick_settings_rotation_unlocked_label)
contentDescription = resources.getString(R.string.accessibility_quick_settings_rotation)
-
+ val iconRes: Int
if (data.isRotationLocked) {
activationState = QSTileState.ActivationState.INACTIVE
secondaryLabel = EMPTY_SECONDARY_STRING
@@ -57,7 +57,7 @@ constructor(
}
iconRes = R.drawable.qs_auto_rotate_icon_on
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
if (isDeviceFoldable(resources, deviceStateManager)) {
secondaryLabel = getSecondaryLabelWithPosture(activationState)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
index aea4967c546c..1d5cf29f2462 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
@@ -36,6 +36,7 @@ constructor(
override fun map(config: QSTileConfig, data: DataSaverTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
with(data) {
+ val iconRes: Int
if (isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_data_saver_icon_on
@@ -45,7 +46,7 @@ constructor(
iconRes = R.drawable.qs_data_saver_icon_off
secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[1]
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
index f3136e015acf..0a61e3cbe616 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
@@ -38,7 +38,7 @@ constructor(
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.quick_settings_screen_record_label)
supportedActions = setOf(QSTileState.UserAction.CLICK)
-
+ val iconRes: Int
when (data) {
is ScreenRecordModel.Recording -> {
activationState = QSTileState.ActivationState.ACTIVE
@@ -61,7 +61,7 @@ constructor(
resources.getString(R.string.quick_settings_screen_record_start)
}
}
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
contentDescription =
if (TextUtils.isEmpty(secondaryLabel)) label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
index 73e61b7d178e..f54f46c01dee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
@@ -50,8 +50,8 @@ constructor(
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
- iconRes = sensorPrivacyTileResources.getIconRes(data.isBlocked)
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ val iconRes = sensorPrivacyTileResources.getIconRes(data.isBlocked)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
sideViewIcon = QSTileState.SideViewIcon.None
if (data.isBlocked) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
index e9aa46c5f253..5933d65bc61f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
@@ -116,11 +116,11 @@ constructor(@ShadeDisplayAware private val resources: Resources, private val the
}
}
- iconRes =
+ val iconRes =
if (activationState == QSTileState.ActivationState.ACTIVE)
R.drawable.qs_light_dark_theme_icon_on
else R.drawable.qs_light_dark_theme_icon_off
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), null)
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
supportedActions =
if (activationState == QSTileState.ActivationState.UNAVAILABLE)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
index 6a3195a493c8..5b462ba074ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
@@ -41,8 +41,8 @@ constructor(
QSTileState.build(resources, theme, config.uiConfig) {
label = getTileLabel()!!
contentDescription = label
- iconRes = com.android.internal.R.drawable.stat_sys_managed_profile_status
- icon = Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ val iconRes = com.android.internal.R.drawable.stat_sys_managed_profile_status
+ icon = Icon.Loaded(resources.getDrawable(iconRes, theme), null, iconRes)
when (data) {
is WorkModeTileModel.HasActiveProfile -> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 8394be5e0a38..c6af729cd4a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -36,7 +36,6 @@ import kotlin.reflect.KClass
*/
data class QSTileState(
val icon: Icon?,
- val iconRes: Int?,
val label: CharSequence,
val activationState: ActivationState,
val secondaryLabel: CharSequence?,
@@ -58,7 +57,7 @@ data class QSTileState(
): QSTileState {
val iconDrawable = resources.getDrawable(config.iconRes, theme)
return build(
- Icon.Loaded(iconDrawable, null),
+ Icon.Loaded(iconDrawable, null, config.iconRes),
resources.getString(config.labelRes),
builder,
)
@@ -115,7 +114,6 @@ data class QSTileState(
}
class Builder(var icon: Icon?, var label: CharSequence) {
- var iconRes: Int? = null
var activationState: ActivationState = ActivationState.INACTIVE
var secondaryLabel: CharSequence? = null
var supportedActions: Set<UserAction> = setOf(UserAction.CLICK)
@@ -128,7 +126,6 @@ data class QSTileState(
fun build(): QSTileState =
QSTileState(
icon,
- iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 632eeefcb462..c34edc81bfe7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -260,8 +260,8 @@ constructor(
icon =
when (val stateIcon = viewModelState.icon) {
is Icon.Loaded ->
- if (viewModelState.iconRes == null) DrawableIcon(stateIcon.drawable)
- else DrawableIconWithRes(stateIcon.drawable, viewModelState.iconRes)
+ if (stateIcon.res == null) DrawableIcon(stateIcon.drawable)
+ else DrawableIconWithRes(stateIcon.drawable, stateIcon.res)
is Icon.Resource -> ResourceIcon.get(stateIcon.res)
null -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 66af275bc702..a7dbb47bc609 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.chips.notification.ui.viewmodel
import android.view.View
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
@@ -99,6 +100,17 @@ constructor(
)
}
+ if (Flags.promoteNotificationsAutomatically()) {
+ // When we're promoting notifications automatically, the `when` time set on the
+ // notification will likely just be set to the current time, which would cause the chip
+ // to always show "now". We don't want early testers to get that experience since it's
+ // not what will happen at launch, so just don't show any time.
+ // TODO(b/364653005): Only ignore the `when` time if the notification was
+ // *automatically* promoted (as opposed to being legitimately promoted by the
+ // criteria). We'll need to track that status somehow.
+ return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
+ }
+
if (this.promotedContent.time == null) {
return OngoingActivityChipModel.Shown.IconOnly(icon, colors, onClickListener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
new file mode 100644
index 000000000000..9f523fc845ab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/StatusBarPopupChips.kt
@@ -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 com.android.systemui.statusbar.chips.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the status bar popup chips flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object StatusBarPopupChips {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_POPUP_CHIPS
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.statusBarPopupChips()
+
+ /**
+ * 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 not enabled to ensure that the refactor author catches issues in testing.
+ * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+ */
+ @JvmStatic
+ inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(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/featurepods/popups/shared/model/PopupChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
new file mode 100644
index 000000000000..1663aebd7287
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.featurepods.popups.shared.model
+
+import com.android.systemui.common.shared.model.Icon
+
+/**
+ * Ids used to track different types of popup chips. Will be used to ensure only one chip is
+ * displaying its popup at a time.
+ */
+sealed class PopupChipId(val value: String) {
+ data object MediaControls : PopupChipId("MediaControls")
+}
+
+/** Model for individual status bar popup chips. */
+sealed class PopupChipModel {
+ abstract val logName: String
+ abstract val chipId: PopupChipId
+
+ data class Hidden(override val chipId: PopupChipId, val shouldAnimate: Boolean = true) :
+ PopupChipModel() {
+ override val logName = "Hidden(id=$chipId, anim=$shouldAnimate)"
+ }
+
+ data class Shown(
+ override val chipId: PopupChipId,
+ val icon: Icon,
+ val chipText: String,
+ val isToggled: Boolean = false,
+ val onToggle: () -> Unit,
+ val onIconPressed: () -> Unit,
+ ) : PopupChipModel() {
+ override val logName = "Shown(id=$chipId, toggled=$isToggled)"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt
new file mode 100644
index 000000000000..5712be30ccd6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipViewModel.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
+
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Interface for a view model that knows the display requirements for a single type of status bar
+ * popup chip.
+ */
+interface StatusBarPopupChipViewModel {
+ /** A flow modeling the popup chip that should be shown (or not shown). */
+ val chip: StateFlow<PopupChipModel>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
new file mode 100644
index 000000000000..b390f29b166c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModel.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * View model deciding which system process chips to show in the status bar. Emits a list of
+ * PopupChipModels.
+ */
+@SysUISingleton
+class StatusBarPopupChipsViewModel @Inject constructor(@Background scope: CoroutineScope) {
+ private data class PopupChipBundle(
+ val media: PopupChipModel = PopupChipModel.Hidden(chipId = PopupChipId.MediaControls)
+ )
+
+ private val incomingPopupChipBundle: Flow<PopupChipBundle?> =
+ flowOf(null).stateIn(scope, SharingStarted.Lazily, PopupChipBundle())
+
+ val popupChips: Flow<List<PopupChipModel>> =
+ incomingPopupChipBundle
+ .map { _ -> listOf(null).filterIsInstance<PopupChipModel.Shown>() }
+ .stateIn(scope, SharingStarted.Lazily, emptyList())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 80e8f55b897a..d83acf34ca99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.inflation;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -186,6 +187,9 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
if (AsyncHybridViewInflation.isEnabled()) {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_SINGLE_LINE);
+ if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
+ params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
+ }
}
mRowContentBindStage.requestRebind(entry, null);
}
@@ -256,10 +260,10 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
params.setUseMinimized(isMinimized);
- // TODO b/358403414: use the different types of redaction
- boolean needsRedaction = inflaterParams.getRedactionType() != REDACTION_TYPE_NONE;
+ int redactionType = inflaterParams.getRedactionType();
- if (needsRedaction) {
+ params.setRedactionType(redactionType);
+ if (redactionType != REDACTION_TYPE_NONE) {
params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
} else {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
@@ -276,8 +280,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
}
if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
-
- if (inflaterParams.isChildInGroup() && needsRedaction) {
+ if (inflaterParams.isChildInGroup()
+ && redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
} else {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
index 13ad1413e89d..a43f8dbc1b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.promoted
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.ERROR
import com.android.systemui.log.core.LogLevel.INFO
-import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
@@ -27,7 +26,7 @@ import javax.inject.Inject
class PromotedNotificationLogger
@Inject
-constructor(@NotificationLog private val buffer: LogBuffer) {
+constructor(@PromotedNotificationLog private val buffer: LogBuffer) {
fun logExtractionSkipped(entry: NotificationEntry, reason: String) {
buffer.log(
EXTRACTION_TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
new file mode 100644
index 000000000000..0f21514fcc94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class AODPromotedNotificationInteractor
+@Inject
+constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) {
+ val content: Flow<PromotedNotificationContentModel?> =
+ activeNotificationsInteractor.topLevelRepresentativeNotifications.map { notifs ->
+ notifs.firstNotNullOfOrNull { it.promotedContent }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
index fe2dabe1ba8a..74809fd8622f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -28,7 +28,7 @@ import com.android.systemui.statusbar.notification.promoted.PromotedNotification
* like the skeleton view on AOD or the status bar chip.
*/
data class PromotedNotificationContentModel(
- val key: String,
+ val identity: Identity,
// for all styles:
val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
@@ -82,7 +82,7 @@ data class PromotedNotificationContentModel(
fun build() =
PromotedNotificationContentModel(
- key = key,
+ identity = Identity(key, style),
skeletonSmallIcon = skeletonSmallIcon,
appName = appName,
subText = subText,
@@ -103,6 +103,8 @@ data class PromotedNotificationContentModel(
)
}
+ data class Identity(val key: String, val style: Style)
+
/** The timestamp associated with a notification, along with the mode used to display it. */
data class When(val time: Long, val mode: Mode) {
/** The mode used to display a notification's `when` value. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt
new file mode 100644
index 000000000000..adfa6a10814d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/AODPromotedNotificationViewModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.AODPromotedNotificationInteractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Identity
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class AODPromotedNotificationViewModel
+@Inject
+constructor(interactor: AODPromotedNotificationInteractor) {
+ private val content: Flow<PromotedNotificationContentModel?> = interactor.content
+ private val identity: Flow<Identity?> = content.mapNonNullsKeepingNulls { it.identity }
+
+ val notification: Flow<PromotedNotificationViewModel?> =
+ identity.distinctUntilChanged().mapNonNullsKeepingNulls { identity ->
+ val updates = interactor.content.filterNotNull().filter { it.identity == identity }
+ PromotedNotificationViewModel(identity, updates)
+ }
+}
+
+private fun <T, R> Flow<T?>.mapNonNullsKeepingNulls(block: (T) -> R): Flow<R?> = map {
+ it?.let(block)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.kt
new file mode 100644
index 000000000000..f265e0ff33f8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/ui/viewmodel/PromotedNotificationViewModel.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.systemui.statusbar.notification.promoted.ui.viewmodel
+
+import android.graphics.drawable.Icon
+import com.android.internal.widget.NotificationProgressModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class PromotedNotificationViewModel(
+ identity: PromotedNotificationContentModel.Identity,
+ content: Flow<PromotedNotificationContentModel>,
+) {
+ // for all styles:
+
+ val key: String = identity.key
+ val style: Style = identity.style
+
+ val skeletonSmallIcon: Flow<Icon?> = content.map { it.skeletonSmallIcon }
+ val appName: Flow<CharSequence?> = content.map { it.appName }
+ val subText: Flow<CharSequence?> = content.map { it.subText }
+
+ private val time: Flow<When?> = content.map { it.time }
+ val whenTime: Flow<Long?> = time.map { it?.time }
+ val whenMode: Flow<When.Mode?> = time.map { it?.mode }
+
+ val lastAudiblyAlertedMs: Flow<Long> = content.map { it.lastAudiblyAlertedMs }
+ val profileBadgeResId: Flow<Int?> = content.map { it.profileBadgeResId }
+ val title: Flow<CharSequence?> = content.map { it.title }
+ val text: Flow<CharSequence?> = content.map { it.text }
+ val skeletonLargeIcon: Flow<Icon?> = content.map { it.skeletonLargeIcon }
+
+ // for CallStyle:
+ val personIcon: Flow<Icon?> = content.map { it.personIcon }
+ val personName: Flow<CharSequence?> = content.map { it.personName }
+ val verificationIcon: Flow<Icon?> = content.map { it.verificationIcon }
+ val verificationText: Flow<CharSequence?> = content.map { it.verificationText }
+
+ // for ProgressStyle:
+ val progress: Flow<NotificationProgressModel?> = content.map { it.progress }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 6eeb80d45211..70e27a981b49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
@@ -25,6 +26,7 @@ import static com.android.systemui.statusbar.notification.row.NotificationConten
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.app.Notification.MessagingStyle;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
@@ -161,9 +163,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
entry,
mConversationProcessor,
row,
- bindParams.isMinimized,
- bindParams.usesIncreasedHeight,
- bindParams.usesIncreasedHeadsUpHeight,
+ bindParams,
callback,
mRemoteInputManager.getRemoteViewsOnClickHandler(),
/* isMediaFlagEnabled = */ mIsMediaInQS,
@@ -187,13 +187,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
boolean inflateSynchronously,
@InflationFlag int reInflateFlags,
Notification.Builder builder,
+ Context systemUiContext,
Context packageContext,
SmartReplyStateInflater smartRepliesInflater) {
InflationProgress result = createRemoteViews(reInflateFlags,
builder,
- bindParams.isMinimized,
- bindParams.usesIncreasedHeight,
- bindParams.usesIncreasedHeadsUpHeight,
+ bindParams,
+ systemUiContext,
packageContext,
row,
mNotifLayoutInflaterFactoryProvider,
@@ -203,18 +203,20 @@ public class NotificationContentInflater implements NotificationRowContentBinder
result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(),
packageContext, row.getExistingSmartReplyState(), smartRepliesInflater, mLogger);
boolean isConversation = entry.getRanking().isConversation();
+ Notification.MessagingStyle messagingStyle = null;
+ if (isConversation && (AsyncHybridViewInflation.isEnabled()
+ || LockscreenOtpRedaction.isSingleLineViewEnabled())) {
+ messagingStyle = mConversationProcessor
+ .processNotification(entry, builder, mLogger);
+ }
if (AsyncHybridViewInflation.isEnabled()) {
- Notification.MessagingStyle messagingStyle = null;
- if (isConversation) {
- messagingStyle = mConversationProcessor
- .processNotification(entry, builder, mLogger);
- }
SingleLineViewModel viewModel = SingleLineViewInflater
.inflateSingleLineViewModel(
entry.getSbn().getNotification(),
messagingStyle,
builder,
- row.getContext()
+ row.getContext(),
+ false
);
// If the messagingStyle is null, we want to inflate the normal view
isConversation = viewModel.isConversation();
@@ -228,11 +230,22 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mLogger
);
}
-
if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
- result.mPublicInflatedSingleLineViewModel =
- SingleLineViewInflater.inflateRedactedSingleLineViewModel(row.getContext(),
- isConversation);
+ if (bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateSingleLineViewModel(
+ entry.getSbn().getNotification(),
+ messagingStyle,
+ builder,
+ row.getContext(),
+ true);
+ } else {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateRedactedSingleLineViewModel(
+ row.getContext(),
+ isConversation
+ );
+ }
result.mPublicInflatedSingleLineView =
SingleLineViewInflater.inflatePublicSingleLineView(
isConversation,
@@ -411,8 +424,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
- Notification.Builder builder, boolean isMinimized, boolean usesIncreasedHeight,
- boolean usesIncreasedHeadsUpHeight, Context packageContext,
+ Notification.Builder builder, BindParams bindParams, Context systemUiContext,
+ Context packageContext,
ExpandableNotificationRow row,
NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
HeadsUpStyleProvider headsUpStyleProvider,
@@ -423,13 +436,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
logger.logAsyncTaskProgress(entryForLogging, "creating contracted remote view");
- result.newContentView = createContentView(builder, isMinimized,
- usesIncreasedHeight);
+ result.newContentView = createContentView(builder, bindParams.isMinimized,
+ bindParams.usesIncreasedHeight);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
logger.logAsyncTaskProgress(entryForLogging, "creating expanded remote view");
- result.newExpandedView = createExpandedView(builder, isMinimized);
+ result.newExpandedView = createExpandedView(builder, bindParams.isMinimized);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
@@ -439,13 +452,20 @@ public class NotificationContentInflater implements NotificationRowContentBinder
result.newHeadsUpView = builder.createCompactHeadsUpContentView();
} else {
result.newHeadsUpView = builder.createHeadsUpContentView(
- usesIncreasedHeadsUpHeight);
+ bindParams.usesIncreasedHeadsUpHeight);
}
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
logger.logAsyncTaskProgress(entryForLogging, "creating public remote view");
- result.newPublicView = builder.makePublicContentView(isMinimized);
+ if (LockscreenOtpRedaction.isEnabled()
+ && bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ result.newPublicView = createSensitiveContentMessageNotification(
+ row.getEntry().getSbn().getNotification(), builder.getStyle(),
+ systemUiContext, packageContext).createContentView(true);
+ } else {
+ result.newPublicView = builder.makePublicContentView(bindParams.isMinimized);
+ }
}
if (AsyncGroupHeaderViewInflation.isEnabled()) {
@@ -473,6 +493,42 @@ public class NotificationContentInflater implements NotificationRowContentBinder
});
}
+ private static Notification.Builder createSensitiveContentMessageNotification(
+ Notification original,
+ Notification.Style originalStyle,
+ Context systemUiContext,
+ Context packageContext) {
+ Notification.Builder redacted =
+ new Notification.Builder(packageContext, original.getChannelId());
+ redacted.setContentTitle(original.extras.getCharSequence(Notification.EXTRA_TITLE));
+ CharSequence redactedMessage = systemUiContext.getString(
+ R.string.redacted_notification_single_line_text
+ );
+
+ if (originalStyle instanceof MessagingStyle oldStyle) {
+ MessagingStyle newStyle = new MessagingStyle(oldStyle.getUser());
+ newStyle.setConversationTitle(oldStyle.getConversationTitle());
+ newStyle.setGroupConversation(false);
+ newStyle.setConversationType(oldStyle.getConversationType());
+ newStyle.setShortcutIcon(oldStyle.getShortcutIcon());
+ newStyle.setBuilder(redacted);
+ MessagingStyle.Message latestMessage =
+ MessagingStyle.findLatestIncomingMessage(oldStyle.getMessages());
+ if (latestMessage != null) {
+ MessagingStyle.Message newMessage = new MessagingStyle.Message(redactedMessage,
+ latestMessage.getTimestamp(), latestMessage.getSenderPerson());
+ newStyle.addMessage(newMessage);
+ }
+ redacted.setStyle(newStyle);
+ } else {
+ redacted.setContentText(redactedMessage);
+ }
+ redacted.setLargeIcon(original.getLargeIcon());
+ redacted.setSmallIcon(original.getSmallIcon());
+ return redacted;
+ }
+
+
private static void setNotifsViewsInflaterFactory(InflationProgress result,
ExpandableNotificationRow row,
NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
@@ -1118,10 +1174,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final NotificationEntry mEntry;
private final Context mContext;
private final boolean mInflateSynchronously;
- private final boolean mIsMinimized;
- private final boolean mUsesIncreasedHeight;
+ private final BindParams mBindParams;
private final InflationCallback mCallback;
- private final boolean mUsesIncreasedHeadsUpHeight;
private final @InflationFlag int mReInflateFlags;
private final NotifRemoteViewCache mRemoteViewCache;
private final Executor mInflationExecutor;
@@ -1145,9 +1199,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotificationEntry entry,
ConversationNotificationProcessor conversationProcessor,
ExpandableNotificationRow row,
- boolean isMinimized,
- boolean usesIncreasedHeight,
- boolean usesIncreasedHeadsUpHeight,
+ BindParams bindParams,
InflationCallback callback,
RemoteViews.InteractionHandler remoteViewClickHandler,
boolean isMediaFlagEnabled,
@@ -1164,9 +1216,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRemoteViewCache = cache;
mSmartRepliesInflater = smartRepliesInflater;
mContext = mRow.getContext();
- mIsMinimized = isMinimized;
- mUsesIncreasedHeight = usesIncreasedHeight;
- mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
+ mBindParams = bindParams;
mRemoteViewClickHandler = remoteViewClickHandler;
mCallback = callback;
mConversationProcessor = conversationProcessor;
@@ -1236,8 +1286,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mEntry, recoveredBuilder, mLogger);
}
InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
- recoveredBuilder, mIsMinimized, mUsesIncreasedHeight,
- mUsesIncreasedHeadsUpHeight, packageContext, mRow,
+ recoveredBuilder, mBindParams, mContext, packageContext, mRow,
mNotifLayoutInflaterFactoryProvider, mHeadsUpStyleProvider, mLogger);
mLogger.logAsyncTaskProgress(mEntry,
@@ -1264,7 +1313,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mEntry.getSbn().getNotification(),
messagingStyle,
recoveredBuilder,
- mContext
+ mContext,
+ false
);
result.mInflatedSingleLineView =
SingleLineViewInflater.inflatePrivateSingleLineView(
@@ -1277,9 +1327,22 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
- result.mPublicInflatedSingleLineViewModel =
- SingleLineViewInflater.inflateRedactedSingleLineViewModel(mContext,
- isConversation);
+ if (mBindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateSingleLineViewModel(
+ mEntry.getSbn().getNotification(),
+ messagingStyle,
+ recoveredBuilder,
+ mContext,
+ true
+ );
+ } else {
+ result.mPublicInflatedSingleLineViewModel =
+ SingleLineViewInflater.inflateRedactedSingleLineViewModel(
+ mContext,
+ isConversation
+ );
+ }
result.mPublicInflatedSingleLineView =
SingleLineViewInflater.inflatePublicSingleLineView(
isConversation,
@@ -1320,7 +1383,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mCancellationSignal = apply(
mInflationExecutor,
mInflateSynchronously,
- mIsMinimized,
+ mBindParams.isMinimized,
result,
mReInflateFlags,
mRemoteViewCache,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
index 07384afe2d2e..1cef8791e0ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -141,20 +143,33 @@ public interface NotificationRowContentBinder {
*/
class BindParams {
+ public BindParams(boolean minimized, boolean increasedHeight,
+ boolean increasedHeadsUpHeight, int redaction) {
+ isMinimized = minimized;
+ usesIncreasedHeight = increasedHeight;
+ usesIncreasedHeadsUpHeight = increasedHeadsUpHeight;
+ redactionType = redaction;
+ }
+
/**
* Bind a minimized version of the content views.
*/
- public boolean isMinimized;
+ public final boolean isMinimized;
/**
* Use increased height when binding contracted view.
*/
- public boolean usesIncreasedHeight;
+ public final boolean usesIncreasedHeight;
/**
* Use increased height when binding heads up views.
*/
- public boolean usesIncreasedHeadsUpHeight;
+ public final boolean usesIncreasedHeadsUpHeight;
+
+ /**
+ * Controls the type of public view to show, if a public view is requested
+ */
+ public final @RedactionType int redactionType;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 7dcb2de57e56..c619b17f1ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.row
import android.annotation.SuppressLint
import android.app.Notification
+import android.app.Notification.MessagingStyle
import android.content.Context
import android.content.ContextWrapper
import android.content.pm.ApplicationInfo
@@ -42,6 +43,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.NotifInflation
import com.android.systemui.res.R
import com.android.systemui.statusbar.InflationTask
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
import com.android.systemui.statusbar.notification.InflationException
@@ -142,9 +144,7 @@ constructor(
entry,
conversationProcessor,
row,
- bindParams.isMinimized,
- bindParams.usesIncreasedHeight,
- bindParams.usesIncreasedHeadsUpHeight,
+ bindParams,
callback,
remoteInputManager.remoteViewsOnClickHandler,
/* isMediaFlagEnabled = */ smartReplyStateInflater,
@@ -178,10 +178,8 @@ constructor(
reInflateFlags = reInflateFlags,
entry = entry,
builder = builder,
- isMinimized = bindParams.isMinimized,
- usesIncreasedHeight = bindParams.usesIncreasedHeight,
- usesIncreasedHeadsUpHeight = bindParams.usesIncreasedHeadsUpHeight,
- systemUIContext = systemUIContext,
+ bindParams,
+ systemUiContext = systemUIContext,
packageContext = packageContext,
row = row,
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
@@ -370,9 +368,7 @@ constructor(
private val entry: NotificationEntry,
private val conversationProcessor: ConversationNotificationProcessor,
private val row: ExpandableNotificationRow,
- private val isMinimized: Boolean,
- private val usesIncreasedHeight: Boolean,
- private val usesIncreasedHeadsUpHeight: Boolean,
+ private val bindParams: BindParams,
private val callback: InflationCallback?,
private val remoteViewClickHandler: InteractionHandler?,
private val smartRepliesInflater: SmartReplyStateInflater,
@@ -440,10 +436,8 @@ constructor(
reInflateFlags = reInflateFlags,
entry = entry,
builder = recoveredBuilder,
- isMinimized = isMinimized,
- usesIncreasedHeight = usesIncreasedHeight,
- usesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight,
- systemUIContext = context,
+ bindParams = bindParams,
+ systemUiContext = context,
packageContext = packageContext,
row = row,
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
@@ -513,7 +507,7 @@ constructor(
apply(
inflationExecutor,
inflateSynchronously,
- isMinimized,
+ bindParams.isMinimized,
progress,
reInflateFlags,
remoteViewCache,
@@ -670,10 +664,8 @@ constructor(
@InflationFlag reInflateFlags: Int,
entry: NotificationEntry,
builder: Notification.Builder,
- isMinimized: Boolean,
- usesIncreasedHeight: Boolean,
- usesIncreasedHeadsUpHeight: Boolean,
- systemUIContext: Context,
+ bindParams: BindParams,
+ systemUiContext: Context,
packageContext: Context,
row: ExpandableNotificationRow,
notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
@@ -705,9 +697,10 @@ constructor(
createRemoteViews(
reInflateFlags = reInflateFlags,
builder = builder,
- isMinimized = isMinimized,
- usesIncreasedHeight = usesIncreasedHeight,
- usesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight,
+ bindParams = bindParams,
+ entry = entry,
+ systemUiContext = systemUiContext,
+ packageContext = packageContext,
row = row,
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
headsUpStyleProvider = headsUpStyleProvider,
@@ -724,7 +717,8 @@ constructor(
notification = entry.sbn.notification,
messagingStyle = messagingStyle,
builder = builder,
- systemUiContext = systemUIContext,
+ systemUiContext = systemUiContext,
+ redactText = false,
)
} else null
@@ -734,10 +728,20 @@ constructor(
reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE != 0
) {
logger.logAsyncTaskProgress(entry, "inflating public single line view model")
- SingleLineViewInflater.inflateRedactedSingleLineViewModel(
- systemUIContext,
- entry.ranking.isConversation,
- )
+ if (bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+ SingleLineViewInflater.inflateSingleLineViewModel(
+ notification = entry.sbn.notification,
+ messagingStyle = messagingStyle,
+ builder = builder,
+ systemUiContext = systemUiContext,
+ redactText = true,
+ )
+ } else {
+ SingleLineViewInflater.inflateRedactedSingleLineViewModel(
+ systemUiContext,
+ entry.ranking.isConversation,
+ )
+ }
} else null
val headsUpStatusBarModel =
@@ -761,12 +765,50 @@ constructor(
)
}
+ private fun createSensitiveContentMessageNotification(
+ original: Notification,
+ originalStyle: Notification.Style?,
+ sysUiContext: Context,
+ packageContext: Context,
+ ): Notification.Builder {
+ val redacted = Notification.Builder(packageContext, original.channelId)
+ redacted.setContentTitle(original.extras.getCharSequence(Notification.EXTRA_TITLE))
+ val redactedMessage =
+ sysUiContext.getString(R.string.redacted_notification_single_line_text)
+
+ if (originalStyle is MessagingStyle) {
+ val newStyle = MessagingStyle(originalStyle.user)
+ newStyle.conversationTitle = originalStyle.conversationTitle
+ newStyle.isGroupConversation = false
+ newStyle.conversationType = originalStyle.conversationType
+ newStyle.shortcutIcon = originalStyle.shortcutIcon
+ newStyle.setBuilder(redacted)
+ val latestMessage = MessagingStyle.findLatestIncomingMessage(originalStyle.messages)
+ if (latestMessage != null) {
+ val newMessage =
+ MessagingStyle.Message(
+ redactedMessage,
+ latestMessage.timestamp,
+ latestMessage.senderPerson,
+ )
+ newStyle.addMessage(newMessage)
+ }
+ redacted.style = newStyle
+ } else {
+ redacted.setContentText(redactedMessage)
+ }
+ redacted.setLargeIcon(original.getLargeIcon())
+ redacted.setSmallIcon(original.smallIcon)
+ return redacted
+ }
+
private fun createRemoteViews(
@InflationFlag reInflateFlags: Int,
builder: Notification.Builder,
- isMinimized: Boolean,
- usesIncreasedHeight: Boolean,
- usesIncreasedHeadsUpHeight: Boolean,
+ bindParams: BindParams,
+ entry: NotificationEntry,
+ systemUiContext: Context,
+ packageContext: Context,
row: ExpandableNotificationRow,
notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
headsUpStyleProvider: HeadsUpStyleProvider,
@@ -780,7 +822,11 @@ constructor(
entryForLogging,
"creating contracted remote view",
)
- createContentView(builder, isMinimized, usesIncreasedHeight)
+ createContentView(
+ builder,
+ bindParams.isMinimized,
+ bindParams.usesIncreasedHeight,
+ )
} else null
val expanded =
if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
@@ -788,7 +834,7 @@ constructor(
entryForLogging,
"creating expanded remote view",
)
- createExpandedView(builder, isMinimized)
+ createExpandedView(builder, bindParams.isMinimized)
} else null
val headsUp =
if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) {
@@ -800,13 +846,26 @@ constructor(
if (isHeadsUpCompact) {
builder.createCompactHeadsUpContentView()
} else {
- builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight)
+ builder.createHeadsUpContentView(bindParams.usesIncreasedHeadsUpHeight)
}
} else null
val public =
if (reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC != 0) {
logger.logAsyncTaskProgress(entryForLogging, "creating public remote view")
- builder.makePublicContentView(isMinimized)
+ if (
+ LockscreenOtpRedaction.isEnabled &&
+ bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT
+ ) {
+ createSensitiveContentMessageNotification(
+ entry.sbn.notification,
+ builder.style,
+ systemUiContext,
+ packageContext,
+ )
+ .createContentView(bindParams.usesIncreasedHeight)
+ } else {
+ builder.makePublicContentView(bindParams.isMinimized)
+ }
} else null
val normalGroupHeader =
if (
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
index 427fb66ca2d0..bc44cb0e1074 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
@@ -31,6 +33,7 @@ public final class RowContentBindParams {
private boolean mUseIncreasedHeadsUpHeight;
private boolean mViewsNeedReinflation;
private @InflationFlag int mContentViews = DEFAULT_INFLATION_FLAGS;
+ private @RedactionType int mRedactionType = REDACTION_TYPE_NONE;
/**
* Content views that are out of date and need to be rebound.
@@ -58,6 +61,20 @@ public final class RowContentBindParams {
}
/**
+ * @return What type of redaction should be used by the public view (if requested)
+ */
+ public @RedactionType int getRedactionType() {
+ return mRedactionType;
+ }
+
+ /**
+ * Set the redaction type, which controls what sort of public view is shown.
+ */
+ public void setRedactionType(@RedactionType int redactionType) {
+ mRedactionType = redactionType;
+ }
+
+ /**
* Set whether content should use an increased height version of its contracted view.
*/
public void setUseIncreasedCollapsedHeight(boolean useIncreasedHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
index 89fcda949b5b..53f74161e7fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
@@ -72,10 +72,8 @@ public class RowContentBindStage extends BindStage<RowContentBindParams> {
// Bind/unbind with parameters
mBinder.unbindContent(entry, row, contentToUnbind);
- BindParams bindParams = new BindParams();
- bindParams.isMinimized = params.useMinimized();
- bindParams.usesIncreasedHeight = params.useIncreasedHeight();
- bindParams.usesIncreasedHeadsUpHeight = params.useIncreasedHeadsUpHeight();
+ BindParams bindParams = new BindParams(params.useMinimized(), params.useIncreasedHeight(),
+ params.useIncreasedHeadsUpHeight(), params.getRedactionType());
boolean forceInflate = params.needsReinflation();
InflationCallback inflationCallback = new InflationCallback() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index e702f10d7f50..fe2803bfc5d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -51,6 +51,7 @@ internal object SingleLineViewInflater {
* notification, not for legacy messaging notifications
* @param builder the recovered Notification Builder
* @param systemUiContext the context of Android System UI
+ * @param redactText indicates if the text needs to be redacted
* @return the inflated SingleLineViewModel
*/
@JvmStatic
@@ -59,13 +60,21 @@ internal object SingleLineViewInflater {
messagingStyle: MessagingStyle?,
builder: Notification.Builder,
systemUiContext: Context,
+ redactText: Boolean,
): SingleLineViewModel {
if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) {
return SingleLineViewModel(null, null, null)
}
peopleHelper.init(systemUiContext)
var titleText = HybridGroupManager.resolveTitle(notification)
- var contentText = HybridGroupManager.resolveText(notification)
+ var contentText =
+ if (redactText) {
+ systemUiContext.getString(
+ com.android.systemui.res.R.string.redacted_notification_single_line_text
+ )
+ } else {
+ HybridGroupManager.resolveText(notification)
+ }
if (messagingStyle == null) {
return SingleLineViewModel(
@@ -81,7 +90,7 @@ internal object SingleLineViewInflater {
if (conversationTextData?.conversationTitle?.isNotEmpty() == true) {
titleText = conversationTextData.conversationTitle
}
- if (conversationTextData?.conversationText?.isNotEmpty() == true) {
+ if (!redactText && conversationTextData?.conversationText?.isNotEmpty() == true) {
contentText = conversationTextData.conversationText
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
index 42acd7bcdc8a..705845ff984c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
@@ -75,7 +75,7 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) :
constraintSet.apply {
if (SceneContainerFlag.isEnabled) {
when (horizontalPosition) {
- is HorizontalPosition.FloatAtEnd ->
+ is HorizontalPosition.FloatAtStart ->
constrainWidth(nsslId, horizontalPosition.width)
is HorizontalPosition.MiddleToEdge ->
setGuidelinePercent(R.id.nssl_guideline, horizontalPosition.ratio)
@@ -83,13 +83,13 @@ class SharedNotificationContainer(context: Context, attrs: AttributeSet?) :
}
}
+ connect(nsslId, START, startConstraintId, START, marginStart)
if (
!SceneContainerFlag.isEnabled ||
- horizontalPosition !is HorizontalPosition.FloatAtEnd
+ horizontalPosition !is HorizontalPosition.FloatAtStart
) {
- connect(nsslId, START, startConstraintId, START, marginStart)
+ connect(nsslId, END, PARENT_ID, END, marginEnd)
}
- connect(nsslId, END, PARENT_ID, END, marginEnd)
connect(nsslId, BOTTOM, PARENT_ID, BOTTOM, marginBottom)
connect(nsslId, TOP, PARENT_ID, TOP, marginTop)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index b81c71ebe19b..fc8c70fb8e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -247,7 +247,7 @@ constructor(
Split -> HorizontalPosition.MiddleToEdge(ratio = 0.5f)
Dual ->
if (isShadeLayoutWide) {
- HorizontalPosition.FloatAtEnd(
+ HorizontalPosition.FloatAtStart(
width = getDimensionPixelSize(R.dimen.shade_panel_width)
)
} else {
@@ -830,10 +830,10 @@ constructor(
data class MiddleToEdge(val ratio: Float = 0.5f) : HorizontalPosition
/**
- * The container has a fixed [width] and is aligned to the end of the screen. In this
- * layout, the start edge of the container is floating, i.e. unconstrained.
+ * The container has a fixed [width] and is aligned to the start of the screen. In this
+ * layout, the end edge of the container is floating, i.e. unconstrained.
*/
- data class FloatAtEnd(val width: Int) : HorizontalPosition
+ data class FloatAtStart(val width: Int) : HorizontalPosition
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index a382cf921152..e08114f6c3cd 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -21,10 +21,8 @@ import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.os.PowerManager
import android.provider.Settings
-import androidx.core.view.OneShotPreDrawListener
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.ToAodFoldTransitionInteractor
@@ -125,11 +123,7 @@ constructor(
private val shadeFoldAnimator: ShadeFoldAnimator
get() {
- return if (MigrateClocksToBlueprint.isEnabled) {
- foldTransitionInteractor.get().foldAnimator
- } else {
- shadeViewController.shadeFoldAnimator
- }
+ return foldTransitionInteractor.get().foldAnimator
}
private fun setAnimationState(playing: Boolean) {
@@ -164,15 +158,7 @@ constructor(
setAnimationState(playing = true)
shadeFoldAnimator.prepareFoldToAodAnimation()
- // We don't need to wait for the scrim as it is already displayed
- // but we should wait for the initial animation preparations to be drawn
- // (setting initial alpha/translation)
- // TODO(b/254878364): remove this call to NPVC.getView()
- if (!MigrateClocksToBlueprint.isEnabled) {
- shadeFoldAnimator.view?.let { OneShotPreDrawListener.add(it, onReady) }
- } else {
- onReady.run()
- }
+ onReady.run()
} else {
// No animation, call ready callback immediately
onReady.run()
@@ -252,7 +238,7 @@ constructor(
if (isFolded) {
foldToAodLatencyTracker.onFolded()
}
- }
+ },
)
/**
@@ -272,6 +258,7 @@ constructor(
latencyTracker.onActionStart(LatencyTracker.ACTION_FOLD_TO_AOD)
}
}
+
/**
* Called once the Fold -> AOD animation is started.
*
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
index 2e1f82d56fc4..70e342f3eefb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
@@ -17,45 +17,28 @@
package com.android.systemui.volume.dialog.settings.ui.binder
import android.view.View
-import com.android.systemui.lifecycle.WindowLifecycleState
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
-import com.android.systemui.lifecycle.viewModel
+import android.widget.ImageButton
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.settings.ui.viewmodel.VolumeDialogSettingsButtonViewModel
import javax.inject.Inject
-import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@VolumeDialogScope
class VolumeDialogSettingsButtonViewBinder
@Inject
-constructor(private val viewModelFactory: VolumeDialogSettingsButtonViewModel.Factory) {
+constructor(private val viewModel: VolumeDialogSettingsButtonViewModel) {
- fun bind(view: View) {
- with(view) {
- val button = requireViewById<View>(R.id.volume_dialog_settings)
- repeatWhenAttached {
- viewModel(
- traceName = "VolumeDialogViewBinder",
- minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { viewModelFactory.create() },
- ) { viewModel ->
- setSnapshotBinding {
- viewModel.isVisible
- .onEach { isVisible ->
- visibility = if (isVisible) View.VISIBLE else View.GONE
- }
- .launchIn(this)
+ fun CoroutineScope.bind(view: View) {
+ val button = view.requireViewById<ImageButton>(R.id.volume_dialog_settings)
+ viewModel.isVisible
+ .onEach { isVisible -> button.visibility = if (isVisible) View.VISIBLE else View.GONE }
+ .launchIn(this)
- button.setOnClickListener { viewModel.onButtonClicked() }
- }
+ viewModel.icon.onEach { button.setImageDrawable(it) }.launchIn(this)
- awaitCancellation()
- }
- }
- }
+ button.setOnClickListener { viewModel.onButtonClicked() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
index 015d773b2c02..03442dbcde66 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
@@ -14,27 +14,206 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.volume.dialog.settings.ui.viewmodel
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.ColorFilter
+import android.graphics.drawable.Drawable
+import android.media.session.PlaybackState
+import androidx.annotation.ColorInt
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieCompositionFactory
+import com.airbnb.lottie.LottieDrawable
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.SimpleColorFilter
+import com.airbnb.lottie.model.KeyPath
+import com.airbnb.lottie.value.LottieValueCallback
+import com.android.internal.R as internalR
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.lottie.await
+import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.settings.domain.VolumeDialogSettingsButtonInteractor
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+import com.android.systemui.volume.panel.shared.model.filterData
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.resume
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.runningFold
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transform
+import kotlinx.coroutines.suspendCancellableCoroutine
class VolumeDialogSettingsButtonViewModel
-@AssistedInject
-constructor(private val interactor: VolumeDialogSettingsButtonInteractor) {
+@Inject
+constructor(
+ @Application private val context: Context,
+ @UiBackground private val uiBgCoroutineContext: CoroutineContext,
+ @VolumeDialog private val coroutineScope: CoroutineScope,
+ mediaOutputInteractor: MediaOutputInteractor,
+ private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+ private val interactor: VolumeDialogSettingsButtonInteractor,
+) {
+
+ @SuppressLint("UseCompatLoadingForDrawables")
+ private val drawables: Flow<Drawables> =
+ flow {
+ val color = context.getColor(internalR.color.materialColorPrimary)
+ emit(
+ Drawables(
+ start =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_in)
+ .await()
+ .toDrawable { setColor(color) },
+ playing =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_playing)
+ .await()
+ .toDrawable {
+ repeatCount = LottieDrawable.INFINITE
+ repeatMode = LottieDrawable.RESTART
+ setColor(color)
+ },
+ stop =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_out)
+ .await()
+ .toDrawable { setColor(color) },
+ idle = context.getDrawable(R.drawable.audio_bars_idle)!!,
+ )
+ )
+ }
+ .buffer()
+ .flowOn(uiBgCoroutineContext)
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
val isVisible = interactor.isVisible
+ val icon: Flow<Drawable> =
+ mediaOutputInteractor.defaultActiveMediaSession
+ .filterData()
+ .flatMapLatest { session ->
+ if (session == null) {
+ flowOf(null)
+ } else {
+ mediaDeviceSessionInteractor.playbackState(session)
+ }
+ }
+ .runningFold(null) { playbackStates: PlaybackStates?, playbackState: PlaybackState? ->
+ val isCurrentActive = playbackState?.isActive ?: false
+ if (playbackStates != null && isCurrentActive == playbackState?.isActive) {
+ return@runningFold playbackStates
+ }
+ playbackStates?.copy(
+ isPreviousActive = playbackStates.isCurrentActive,
+ isCurrentActive = isCurrentActive,
+ ) ?: PlaybackStates(isPreviousActive = null, isCurrentActive = isCurrentActive)
+ }
+ .filterNotNull()
+ // only apply the most recent state if we wait for the animation.
+ .buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ // distinct again because the changed state might've been dropped by the buffer
+ .distinctUntilChangedBy { it.isCurrentActive }
+ .transform { emitDrawables(it) }
+ .runningFold(null) { previous: Drawable?, current: Drawable ->
+ // wait for the previous animation to finish before starting the new one
+ // this also waits for the current loop of the playing animation to finish
+ (previous as? LottieDrawable)?.awaitFinish()
+ (current as? LottieDrawable)?.start()
+ current
+ }
+ .filterNotNull()
+
+ private suspend fun FlowCollector<Drawable>.emitDrawables(playbackStates: PlaybackStates) {
+ val animations = drawables.first()
+ val stateChanged =
+ playbackStates.isPreviousActive != null &&
+ playbackStates.isPreviousActive != playbackStates.isCurrentActive
+ if (playbackStates.isCurrentActive) {
+ if (stateChanged) {
+ emit(animations.start)
+ }
+ emit(animations.playing)
+ } else {
+ if (stateChanged) {
+ emit(animations.stop)
+ }
+ emit(animations.idle)
+ }
+ }
fun onButtonClicked() {
interactor.onButtonClicked()
}
- @VolumeDialogScope
- @AssistedFactory
- interface Factory {
+ private data class PlaybackStates(val isPreviousActive: Boolean?, val isCurrentActive: Boolean)
+
+ private data class Drawables(
+ val start: LottieDrawable,
+ val playing: LottieDrawable,
+ val stop: LottieDrawable,
+ val idle: Drawable,
+ )
+}
+
+private fun LottieComposition.toDrawable(setup: LottieDrawable.() -> Unit = {}): LottieDrawable =
+ LottieDrawable().also { drawable ->
+ drawable.composition = this
+ drawable.setup()
+ }
- fun create(): VolumeDialogSettingsButtonViewModel
+/** Suspends until current loop of the repeating animation is finished */
+private suspend fun LottieDrawable.awaitFinish() = suspendCancellableCoroutine { continuation ->
+ if (!isRunning) {
+ continuation.resume(Unit)
+ return@suspendCancellableCoroutine
}
+ val listener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationRepeat(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+ }
+ addAnimatorListener(listener)
+ continuation.invokeOnCancellation { removeAnimatorListener(listener) }
+}
+
+/**
+ * Overrides colors of the [LottieDrawable] to a specified [color]
+ *
+ * @see com.airbnb.lottie.LottieAnimationView
+ */
+private fun LottieDrawable.setColor(@ColorInt color: Int) {
+ val callback = LottieValueCallback<ColorFilter>(SimpleColorFilter(color))
+ addValueCallback(KeyPath("**"), LottieProperty.COLOR_FILTER, callback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index f30524638150..faf06b942cab 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -26,7 +26,7 @@ import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
-import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
+import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
import com.google.android.material.slider.LabelFormatter
import com.google.android.material.slider.Slider
import javax.inject.Inject
@@ -84,5 +84,5 @@ private suspend fun Slider.setValueAnimated(
interpolator = DecelerateInterpolator()
addListener(jankListener)
}
- .awaitAnimation<Float> { value = it }
+ .suspendAnimate<Float> { value = it }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
index 10cf615ce0ce..5f124806dac7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator
import android.view.ViewPropertyAnimator
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringAnimation
+import com.airbnb.lottie.LottieDrawable
import kotlin.coroutines.resume
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -66,7 +67,7 @@ suspend fun ViewPropertyAnimator.suspendAnimate(
* is cancelled.
*/
@Suppress("UNCHECKED_CAST")
-suspend fun <T> ValueAnimator.awaitAnimation(onValueChanged: (T) -> Unit) {
+suspend fun <T> ValueAnimator.suspendAnimate(onValueChanged: (T) -> Unit) {
suspendCancellableCoroutine { continuation ->
addListener(
object : AnimatorListenerAdapter() {
@@ -103,6 +104,29 @@ suspend fun SpringAnimation.suspendAnimate(
}
}
+/**
+ * Starts the animation and suspends until it's finished. Cancels the animation if the running
+ * coroutine is cancelled.
+ */
+suspend fun LottieDrawable.suspendAnimate() = suspendCancellableCoroutine { continuation ->
+ val listener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ continuation.resumeIfCan(Unit)
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ continuation.resumeIfCan(Unit)
+ }
+ }
+ addAnimatorListener(listener)
+ start()
+ continuation.invokeOnCancellation {
+ removeAnimatorListener(listener)
+ stop()
+ }
+}
+
private fun <T> CancellableContinuation<T>.resumeIfCan(value: T) {
if (!isCancelled && !isCompleted) {
resume(value)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
index 6e1ebc820b08..12e624cae4d4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
@@ -19,10 +19,10 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto
import android.media.session.MediaController
import android.media.session.PlaybackState
import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -38,7 +38,7 @@ import kotlinx.coroutines.withContext
/** Allows to observe and change [MediaDeviceSession] state. */
@OptIn(ExperimentalCoroutinesApi::class)
-@VolumePanelScope
+@SysUISingleton
class MediaDeviceSessionInteractor
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index b3848a6d7817..2973e11c365d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -24,12 +24,13 @@ import androidx.annotation.WorkerThread
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.shared.model.Result
import com.android.systemui.volume.panel.shared.model.filterData
import com.android.systemui.volume.panel.shared.model.wrapInResult
@@ -54,13 +55,13 @@ import kotlinx.coroutines.withContext
/** Provides observable models about the current media session state. */
@OptIn(ExperimentalCoroutinesApi::class)
-@VolumePanelScope
+@SysUISingleton
class MediaOutputInteractor
@Inject
constructor(
private val localMediaRepositoryFactory: LocalMediaRepositoryFactory,
private val packageManager: PackageManager,
- @VolumePanelScope private val coroutineScope: CoroutineScope,
+ @Application private val coroutineScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
mediaControllerRepository: MediaControllerRepository,
private val mediaControllerInteractor: MediaControllerInteractor,
@@ -77,7 +78,7 @@ constructor(
.onStart { emit(activeSessions) }
}
.map { getMediaControllers(it) }
- .stateIn(coroutineScope, SharingStarted.Eagerly, MediaControllers(null, null))
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), MediaControllers(null, null))
/** [MediaDeviceSessions] that contains currently active sessions. */
val activeMediaDeviceSessions: Flow<MediaDeviceSessions> =
@@ -89,7 +90,11 @@ constructor(
)
}
.flowOn(backgroundCoroutineContext)
- .stateIn(coroutineScope, SharingStarted.Eagerly, MediaDeviceSessions(null, null))
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(),
+ MediaDeviceSessions(null, null),
+ )
/** Returns the default [MediaDeviceSession] from [activeMediaDeviceSessions] */
val defaultActiveMediaSession: StateFlow<Result<MediaDeviceSession?>> =
@@ -104,7 +109,7 @@ constructor(
}
.wrapInResult()
.flowOn(backgroundCoroutineContext)
- .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading())
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), Result.Loading())
private val localMediaRepository: Flow<LocalMediaRepository> =
defaultActiveMediaSession
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 32fa160fc29f..21dd5bc068f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -344,7 +344,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
canShowWhileLocked = canShowWhileLocked,
)
} else {
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
underTest.onQuickAffordanceTriggered(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index eb19a9c2528d..1ce128c2403a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -347,7 +347,7 @@ class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() {
canShowWhileLocked = canShowWhileLocked,
)
} else {
- KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled(false)
}
underTest.onQuickAffordanceTriggered(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index b26f0a6e71a3..782b24825bcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -557,6 +557,13 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
}
@Test
+ public void startActivityForDialog_always_startActivityWithoutDismissShade() {
+ mInternetDialogController.startActivityForDialog(mock(Intent.class));
+
+ verify(mActivityStarter).startActivity(any(Intent.class), eq(false) /* dismissShade */);
+ }
+
+ @Test
public void launchWifiDetailsSetting_withNoWifiEntryKey_doNothing() {
mInternetDialogController.launchWifiDetailsSetting(null /* key */, mDialogLaunchView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
index 503fa789cb80..1eb88c5a5616 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
@@ -89,6 +89,7 @@ class SingleLineViewBinderTest : SysuiTestCase() {
messagingStyle = null,
builder = notificationBuilder,
systemUiContext = context,
+ redactText = false,
)
// WHEN: binds the viewHolder
@@ -149,6 +150,7 @@ class SingleLineViewBinderTest : SysuiTestCase() {
messagingStyle = style,
builder = notificationBuilder,
systemUiContext = context,
+ redactText = false,
)
// WHEN: binds the view
SingleLineViewBinder.bind(viewModel, view)
@@ -197,6 +199,7 @@ class SingleLineViewBinderTest : SysuiTestCase() {
messagingStyle = null,
builder = notificationBuilder,
systemUiContext = context,
+ redactText = false,
)
// WHEN: binds the view with the view model
SingleLineViewBinder.bind(viewModel, view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
index d3666321c8e4..ef70e277832e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
@@ -379,7 +379,8 @@ class SingleLineViewInflaterTest : SysuiTestCase() {
this,
if (isConversation) messagingStyle else null,
builder,
- context
+ context,
+ false
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt
new file mode 100644
index 000000000000..57c8fd066ea8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalBackActionInteractorKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+
+val Kosmos.communalBackActionInteractor by
+ Kosmos.Fixture {
+ CommunalBackActionInteractor(
+ communalInteractor = communalInteractor,
+ communalSceneInteractor = communalSceneInteractor,
+ sceneInteractor = sceneInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
index 548b5646f5d4..5d206691b520 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
@@ -30,7 +30,7 @@ class FakeKeyguardQuickAffordanceConfig(
override val pickerIconResourceId: Int = 0,
) : KeyguardQuickAffordanceConfig {
- var onTriggeredResult: OnTriggeredResult = OnTriggeredResult.Handled
+ var onTriggeredResult: OnTriggeredResult = OnTriggeredResult.Handled(false)
private val _lockScreenState =
MutableStateFlow<KeyguardQuickAffordanceConfig.LockScreenState>(
@@ -41,9 +41,7 @@ class FakeKeyguardQuickAffordanceConfig(
override fun pickerName(): String = pickerName
- override fun onTriggered(
- expandable: Expandable?,
- ): OnTriggeredResult {
+ override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
return onTriggeredResult
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt
new file mode 100644
index 000000000000..d857157137b6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceHapticViewModelKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceHapticViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.flow.Flow
+
+val Kosmos.keyguardQuickAffordanceHapticViewModelFactory by
+ Kosmos.Fixture {
+ object : KeyguardQuickAffordanceHapticViewModel.Factory {
+ override fun create(
+ quickAffordanceViewModel: Flow<KeyguardQuickAffordanceViewModel>
+ ): KeyguardQuickAffordanceHapticViewModel =
+ KeyguardQuickAffordanceHapticViewModel(
+ quickAffordanceViewModel,
+ keyguardQuickAffordanceInteractor,
+ )
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index e47310727905..abbfa93edd17 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -87,7 +87,6 @@ val Kosmos.keyguardRootViewModel by Fixture {
primaryBouncerToLockscreenTransitionViewModel,
screenOffAnimationController = screenOffAnimationController,
aodBurnInViewModel = aodBurnInViewModel,
- aodAlphaViewModel = aodAlphaViewModel,
shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
index ab1c1818bf80..aa29808bd9ee 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
@@ -45,7 +45,6 @@ private constructor(failureMetadata: FailureMetadata, subject: QSTileState?) :
other ?: return
}
check("icon").that(actual.icon).isEqualTo(other.icon)
- check("iconRes").that(actual.iconRes).isEqualTo(other.iconRes)
check("label").that(actual.label).isEqualTo(other.label)
check("activationState").that(actual.activationState).isEqualTo(other.activationState)
check("secondaryLabel").that(actual.secondaryLabel).isEqualTo(other.secondaryLabel)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
new file mode 100644
index 000000000000..62cdc87f980f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/featurepods/popups/ui/viewmodel/StatusBarPopupChipsViewModelKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.popups.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+
+val Kosmos.statusBarPopupChipsViewModel: StatusBarPopupChipsViewModel by
+ Kosmos.Fixture { StatusBarPopupChipsViewModel(testScope.backgroundScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
index a3572754ab19..f31697e82a45 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
@@ -70,6 +70,14 @@ class FakeSettings : SecureSettings, SystemSettings, UserSettingsProxy {
}
}
+ fun getContentObservers(uri: Uri, userHandle: Int): List<ContentObserver> {
+ if (userHandle == UserHandle.USER_ALL) {
+ return contentObserversAllUsers[uri.toString()] ?: listOf()
+ } else {
+ return contentObservers[SettingsKey(userHandle, uri.toString())] ?: listOf()
+ }
+ }
+
override fun getContentResolver(): ContentResolver {
throw UnsupportedOperationException("FakeSettings.getContentResolver is not implemented")
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 3441d94facda..9ceca5d1dbfe 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1589,7 +1589,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
* lock because this calls out to WindowManagerService.
*/
void addWindowTokensForAllDisplays() {
- final Display[] displays = mDisplayManager.getDisplays();
+ Display[] displays = {};
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ displays = mDisplayManager.getDisplays();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
for (int i = 0; i < displays.length; i++) {
final int displayId = displays[i].getDisplayId();
addWindowTokenForDisplay(displayId);
@@ -1625,7 +1631,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
public void onRemoved() {
- final Display[] displays = mDisplayManager.getDisplays();
+ Display[] displays = {};
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ displays = mDisplayManager.getDisplays();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
for (int i = 0; i < displays.length; i++) {
final int displayId = displays[i].getDisplayId();
onDisplayRemoved(displayId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 4944cafeb83d..4b8770b3cd35 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RECONFIGURATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
@@ -31,6 +32,7 @@ import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROA
import static com.android.server.am.BroadcastConstants.getDeviceConfigBoolean;
import android.annotation.NonNull;
+import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.ForegroundServiceTypePolicy;
import android.content.ComponentName;
@@ -55,6 +57,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
import dalvik.annotation.optimization.NeverCompile;
@@ -181,6 +184,12 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION =
"follow_up_oomadj_update_wait_duration";
+ /*
+ * Oom score cutoff beyond which any process that does not have the CPU_TIME capability will be
+ * frozen.
+ */
+ static final String KEY_FREEZER_CUTOFF_ADJ = "freezer_cutoff_adj";
+
private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024;
private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -267,6 +276,9 @@ final class ActivityManagerConstants extends ContentObserver {
*/
private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L;
+ /** The default value to {@link #KEY_FREEZER_CUTOFF_ADJ} */
+ private static final int DEFAULT_FREEZER_CUTOFF_ADJ = ProcessList.CACHED_APP_MIN_ADJ;
+
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
*/
@@ -1171,6 +1183,14 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION;
/**
+ * The cutoff adj for the freezer, app processes with adj greater than this value will be
+ * eligible for the freezer.
+ *
+ * @see #KEY_FREEZER_CUTOFF_ADJ
+ */
+ public int FREEZER_CUTOFF_ADJ = DEFAULT_FREEZER_CUTOFF_ADJ;
+
+ /**
* Indicates whether PSS profiling in AppProfiler is disabled or not.
*/
static final String KEY_DISABLE_APP_PROFILER_PSS_PROFILING =
@@ -1194,6 +1214,7 @@ final class ActivityManagerConstants extends ContentObserver {
new OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(Properties properties) {
+ boolean oomAdjusterConfigUpdated = false;
for (String name : properties.getKeyset()) {
if (name == null) {
return;
@@ -1372,6 +1393,11 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_TIERED_CACHED_ADJ_UI_TIER_SIZE:
updateUseTieredCachedAdj();
break;
+ case KEY_FREEZER_CUTOFF_ADJ:
+ FREEZER_CUTOFF_ADJ = properties.getInt(KEY_FREEZER_CUTOFF_ADJ,
+ DEFAULT_FREEZER_CUTOFF_ADJ);
+ oomAdjusterConfigUpdated = true;
+ break;
case KEY_DISABLE_APP_PROFILER_PSS_PROFILING:
updateDisableAppProfilerPssProfiling();
break;
@@ -1389,6 +1415,13 @@ final class ActivityManagerConstants extends ContentObserver {
break;
}
}
+ if (oomAdjusterConfigUpdated) {
+ final ActivityManagerInternal ami = LocalServices.getService(
+ ActivityManagerInternal.class);
+ if (ami != null) {
+ ami.updateOomAdj(OOM_ADJ_REASON_RECONFIGURATION);
+ }
+ }
}
};
@@ -2534,6 +2567,9 @@ final class ActivityManagerConstants extends ContentObserver {
pw.print(" "); pw.print(KEY_ENABLE_NEW_OOMADJ);
pw.print("="); pw.println(ENABLE_NEW_OOMADJ);
+ pw.print(" "); pw.print(KEY_FREEZER_CUTOFF_ADJ);
+ pw.print("="); pw.println(FREEZER_CUTOFF_ADJ);
+
pw.print(" "); pw.print(KEY_DISABLE_APP_PROFILER_PSS_PROFILING);
pw.print("="); pw.println(APP_PROFILER_PSS_PROFILING_DISABLED);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 2f5362f53361..d335529a006a 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -25,9 +25,11 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BIND_SERVICE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_COMPONENT_DISABLED;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_EXECUTING_SERVICE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FOLLOW_UP;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_END;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RECONFIGURATION;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_TASK;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RESTRICTION_CHANGE;
@@ -203,6 +205,10 @@ public class CachedAppOptimizer {
FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_RESTRICTION_CHANGE;
static final int UNFREEZE_REASON_COMPONENT_DISABLED =
FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_COMPONENT_DISABLED;
+ static final int UNFREEZE_REASON_OOM_ADJ_FOLLOW_UP =
+ FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_OOM_ADJ_FOLLOW_UP;
+ static final int UNFREEZE_REASON_OOM_ADJ_RECONFIGURATION =
+ FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_OOM_ADJ_RECONFIGURATION;
@IntDef(prefix = {"UNFREEZE_REASON_"}, value = {
UNFREEZE_REASON_NONE,
@@ -234,6 +240,8 @@ public class CachedAppOptimizer {
UNFREEZE_REASON_EXECUTING_SERVICE,
UNFREEZE_REASON_RESTRICTION_CHANGE,
UNFREEZE_REASON_COMPONENT_DISABLED,
+ UNFREEZE_REASON_OOM_ADJ_FOLLOW_UP,
+ UNFREEZE_REASON_OOM_ADJ_RECONFIGURATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UnfreezeReason {}
@@ -2451,8 +2459,8 @@ public class CachedAppOptimizer {
synchronized (mAm.mPidsSelfLocked) {
pr = mAm.mPidsSelfLocked.get(blocked);
}
- if (pr != null
- && pr.mState.getCurAdj() < ProcessList.FREEZER_CUTOFF_ADJ) {
+ if (pr != null && pr.mState.getCurAdj()
+ < mAm.mConstants.FREEZER_CUTOFF_ADJ) {
Slog.d(TAG_AM, app.processName + " (" + pid + ") blocks "
+ pr.processName + " (" + blocked + ")");
// Found at least one blocked non-cached process
@@ -2539,6 +2547,10 @@ public class CachedAppOptimizer {
return UNFREEZE_REASON_RESTRICTION_CHANGE;
case OOM_ADJ_REASON_COMPONENT_DISABLED:
return UNFREEZE_REASON_COMPONENT_DISABLED;
+ case OOM_ADJ_REASON_FOLLOW_UP:
+ return UNFREEZE_REASON_OOM_ADJ_FOLLOW_UP;
+ case OOM_ADJ_REASON_RECONFIGURATION:
+ return UNFREEZE_REASON_OOM_ADJ_RECONFIGURATION;
default:
return UNFREEZE_REASON_NONE;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index aadf6f61956c..9c569db99797 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -55,6 +55,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_END;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RECONFIGURATION;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_TASK;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RESTRICTION_CHANGE;
@@ -105,7 +106,6 @@ import static com.android.server.am.ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ;
-import static com.android.server.am.ProcessList.FREEZER_CUTOFF_ADJ;
import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ;
import static com.android.server.am.ProcessList.HOME_APP_ADJ;
import static com.android.server.am.ProcessList.INVALID_ADJ;
@@ -232,6 +232,8 @@ public class OomAdjuster {
return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED;
case OOM_ADJ_REASON_FOLLOW_UP:
return AppProtoEnums.OOM_ADJ_REASON_FOLLOW_UP;
+ case OOM_ADJ_REASON_RECONFIGURATION:
+ return AppProtoEnums.OOM_ADJ_REASON_RECONFIGURATION;
default:
return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO;
}
@@ -288,6 +290,8 @@ public class OomAdjuster {
return OOM_ADJ_REASON_METHOD + "_componentDisabled";
case OOM_ADJ_REASON_FOLLOW_UP:
return OOM_ADJ_REASON_METHOD + "_followUp";
+ case OOM_ADJ_REASON_RECONFIGURATION:
+ return OOM_ADJ_REASON_METHOD + "_reconfiguration";
default:
return "_unknown";
}
@@ -4079,7 +4083,7 @@ public class OomAdjuster {
}
// Reasons to freeze:
- if (proc.mState.getCurAdj() >= FREEZER_CUTOFF_ADJ) {
+ if (proc.mState.getCurAdj() >= mConstants.FREEZER_CUTOFF_ADJ) {
// Oomscore is in a high enough state, it is safe to freeze.
return true;
}
@@ -4098,9 +4102,8 @@ public class OomAdjuster {
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 oomAdjChanged = (state.getCurAdj() >= mConstants.FREEZER_CUTOFF_ADJ
+ ^ oldOomAdj >= mConstants.FREEZER_CUTOFF_ADJ) || oldOomAdj == UNKNOWN_ADJ;
final boolean shouldNotFreezeChanged = opt.shouldNotFreezeAdjSeq() == mAdjSeq;
final boolean hasCpuCapability =
(PROCESS_CAPABILITY_CPU_TIME & app.mState.getCurCapability())
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 70febcd63455..bddde9d589f3 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -383,12 +383,6 @@ public final class ProcessList {
private static final long LMKD_RECONNECT_DELAY_MS = 1000;
/**
- * The cuttoff adj for the freezer, app processes with adj greater than this value will be
- * eligible for the freezer.
- */
- static final int FREEZER_CUTOFF_ADJ = CACHED_APP_MIN_ADJ;
-
- /**
* Apps have no access to the private data directories of any other app, even if the other
* app has made them world-readable.
*/
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 98f738c38d63..49149e1fa415 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1699,7 +1699,7 @@ class ProcessRecord implements WindowProcessListener {
return mService.mOomAdjuster.mCachedAppOptimizer.useFreezer()
&& !mOptRecord.isFreezeExempt()
&& !mOptRecord.shouldNotFreeze()
- && mState.getCurAdj() >= ProcessList.FREEZER_CUTOFF_ADJ;
+ && mState.getCurAdj() >= mService.mConstants.FREEZER_CUTOFF_ADJ;
}
public void forEachConnectionHost(Consumer<ProcessRecord> consumer) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 87f87c76725e..c82933c5069e 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -158,6 +158,7 @@ public class SettingsToPropertiesMapper {
"aoc",
"app_widgets",
"arc_next",
+ "art_cloud",
"art_mainline",
"art_performance",
"attack_tools",
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2985ad330bc6..5740e16dc886 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2400,6 +2400,10 @@ public final class DisplayManagerService extends SystemService {
sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
applyDisplayChangedLocked(display);
+
+ if (mDisplayTopologyCoordinator != null) {
+ mDisplayTopologyCoordinator.onDisplayChanged(display.getDisplayInfoLocked());
+ }
}
private void applyDisplayChangedLocked(@NonNull LogicalDisplay display) {
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 5b78726cc421..461a9f3f2a0d 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -85,13 +85,26 @@ class DisplayTopologyCoordinator {
}
/**
+ * Update the topology with display changes.
+ * @param info The new display info
+ */
+ void onDisplayChanged(DisplayInfo info) {
+ synchronized (mSyncRoot) {
+ if (mTopology.updateDisplay(info.displayId, getWidth(info), getHeight(info))) {
+ sendTopologyUpdateLocked();
+ }
+ }
+ }
+
+ /**
* Remove a display from the topology.
* @param displayId The logical display ID
*/
void onDisplayRemoved(int displayId) {
synchronized (mSyncRoot) {
- mTopology.removeDisplay(displayId);
- sendTopologyUpdateLocked();
+ if (mTopology.removeDisplay(displayId)) {
+ sendTopologyUpdateLocked();
+ }
}
}
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java
new file mode 100644
index 000000000000..6cab60c05b8e
--- /dev/null
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyGnssAssistanceProvider.java
@@ -0,0 +1,97 @@
+/*
+ * 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.location.provider.proxy;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.location.provider.GnssAssistanceProviderBase;
+import android.location.provider.IGnssAssistanceCallback;
+import android.location.provider.IGnssAssistanceProvider;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.servicewatcher.CurrentUserServiceSupplier;
+import com.android.server.servicewatcher.ServiceWatcher;
+
+/**
+ * Proxy for IGnssAssitanceProvider implementations.
+ */
+public class ProxyGnssAssistanceProvider {
+
+ private static final String TAG = "GnssAssistanceProxy";
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ public static ProxyGnssAssistanceProvider createAndRegister(Context context) {
+ ProxyGnssAssistanceProvider proxy = new ProxyGnssAssistanceProvider(context);
+ if (proxy.register()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private final ServiceWatcher mServiceWatcher;
+
+ private ProxyGnssAssistanceProvider(Context context) {
+ mServiceWatcher =
+ ServiceWatcher.create(
+ context,
+ TAG,
+ CurrentUserServiceSupplier.createFromConfig(
+ context,
+ GnssAssistanceProviderBase.ACTION_GNSS_ASSISTANCE_PROVIDER,
+ com.android.internal.R.bool.config_enableGnssAssistanceOverlay,
+ com.android.internal.R.string
+ .config_gnssAssistanceProviderPackageName),
+ /* serviceListener= */ null);
+ }
+
+ private boolean register() {
+ boolean resolves = mServiceWatcher.checkServiceResolves();
+ if (resolves) {
+ mServiceWatcher.register();
+ }
+ return resolves;
+ }
+
+ /**
+ * Request GNSS assistance.
+ */
+ public void request(IGnssAssistanceCallback callback) {
+ mServiceWatcher.runOnBinder(
+ new ServiceWatcher.BinderOperation() {
+ @Override
+ public void run(IBinder binder) throws RemoteException {
+ IGnssAssistanceProvider.Stub.asInterface(binder).request(callback);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ try {
+ Log.w(TAG, "Error on requesting GnssAssistance: " + t);
+ callback.onError();
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 0438a1bac662..0d6e502cf965 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -179,8 +179,29 @@ abstract class MediaRoute2Provider {
void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
void onSessionCreated(@NonNull MediaRoute2Provider provider,
long requestId, @Nullable RoutingSessionInfo sessionInfo);
- void onSessionUpdated(@NonNull MediaRoute2Provider provider,
- @NonNull RoutingSessionInfo sessionInfo);
+
+ /**
+ * Called when there's a session info change.
+ *
+ * <p>If the provided {@code sessionInfo} has a null {@link
+ * RoutingSessionInfo#getClientPackageName()}, that means that it's applicable to all
+ * packages. We call this type of routing session "global". This is typically used for
+ * system provided {@link RoutingSessionInfo}. However, some applications may be exempted
+ * from the global routing sessions, because their media is being routed using a session
+ * different from the global routing session.
+ *
+ * @param provider The provider that owns the session that changed.
+ * @param sessionInfo The new {@link RoutingSessionInfo}.
+ * @param packageNamesWithRoutingSessionOverrides The names of packages that are not
+ * affected by global session changes. This set may only be non-empty when the {@code
+ * sessionInfo} is for the global session, and therefore has no {@link
+ * RoutingSessionInfo#getClientPackageName()}.
+ */
+ void onSessionUpdated(
+ @NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo,
+ Set<String> packageNamesWithRoutingSessionOverrides);
+
void onSessionReleased(@NonNull MediaRoute2Provider provider,
@NonNull RoutingSessionInfo sessionInfo);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 80d3c5c5c5ec..d6f7d3bdd4a4 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -499,6 +500,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
synchronized (mLock) {
var systemMediaSessionCallback = mRequestIdToSystemSessionRequest.get(requestId);
if (systemMediaSessionCallback != null) {
+ mRequestIdToSystemSessionRequest.remove(requestId);
mSystemSessionCallbacks.put(newSession.getOriginalId(), systemMediaSessionCallback);
systemMediaSessionCallback.onSessionUpdate(newSession);
return;
@@ -674,7 +676,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
private void dispatchSessionUpdated(RoutingSessionInfo session) {
mHandler.sendMessage(
- obtainMessage(mCallback::onSessionUpdated, this, session));
+ obtainMessage(
+ mCallback::onSessionUpdated,
+ this,
+ session,
+ /* packageNamesWithRoutingSessionOverrides= */ Set.of()));
}
private void dispatchSessionReleased(RoutingSessionInfo session) {
@@ -717,6 +723,19 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
for (RoutingSessionInfo sessionInfo : mSessionInfos) {
mCallback.onSessionReleased(this, sessionInfo);
}
+ if (Flags.enableMirroringInMediaRouter2()) {
+ for (var callback : mSystemSessionCallbacks.values()) {
+ callback.onSessionReleased();
+ }
+ mSystemSessionCallbacks.clear();
+ int requestsSize = mRequestIdToSystemSessionRequest.size();
+ for (int i = 0; i < requestsSize; i++) {
+ var callback = mRequestIdToSystemSessionRequest.valueAt(i);
+ var requestId = mRequestIdToSystemSessionRequest.keyAt(i);
+ callback.onRequestFailed(requestId, REASON_REJECTED);
+ }
+ mSystemSessionCallbacks.clear();
+ }
mSessionInfos.clear();
mReleasingSessions.clear();
mRequestIdToSessionCreationRequest.clear();
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 83ac05d9d4c3..5e6737a485af 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -2634,10 +2634,17 @@ class MediaRouter2ServiceImpl {
}
@Override
- public void onSessionUpdated(@NonNull MediaRoute2Provider provider,
- @NonNull RoutingSessionInfo sessionInfo) {
- sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler,
- this, provider, sessionInfo));
+ public void onSessionUpdated(
+ @NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo,
+ Set<String> packageNamesWithRoutingSessionOverrides) {
+ sendMessage(
+ PooledLambda.obtainMessage(
+ UserHandler::onSessionInfoChangedOnHandler,
+ this,
+ provider,
+ sessionInfo,
+ packageNamesWithRoutingSessionOverrides));
}
@Override
@@ -3148,10 +3155,31 @@ class MediaRouter2ServiceImpl {
toOriginalRequestId(uniqueRequestId), sessionInfo);
}
- private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
- @NonNull RoutingSessionInfo sessionInfo) {
+ /**
+ * Implementation of {@link MediaRoute2Provider.Callback#onSessionUpdated}.
+ *
+ * <p>Must run on the thread that corresponds to this {@link UserHandler}.
+ */
+ private void onSessionInfoChangedOnHandler(
+ @NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo,
+ Set<String> packageNamesWithRoutingSessionOverrides) {
List<ManagerRecord> managers = getManagerRecords();
for (ManagerRecord manager : managers) {
+ if (Flags.enableMirroringInMediaRouter2()) {
+ String targetPackageName = manager.mTargetPackageName;
+ boolean skipDueToOverride =
+ targetPackageName != null
+ && packageNamesWithRoutingSessionOverrides.contains(
+ targetPackageName);
+ boolean sessionIsForTargetPackage =
+ TextUtils.isEmpty(sessionInfo.getClientPackageName()) // is global.
+ || TextUtils.equals(
+ targetPackageName, sessionInfo.getClientPackageName());
+ if (skipDueToOverride || !sessionIsForTargetPackage) {
+ continue;
+ }
+ }
manager.notifySessionUpdated(sessionInfo);
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 4aec3678af8b..60fced1e0c51 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -62,7 +62,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";
private final AudioManager mAudioManager;
- private final Handler mHandler;
+ protected final Handler mHandler;
private final Context mContext;
private final UserHandle mUser;
@@ -116,7 +116,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
() -> {
publishProviderState();
if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
});
@@ -129,7 +129,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
() -> {
publishProviderState();
if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
}));
}
@@ -161,7 +161,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
public void setCallback(Callback callback) {
super.setCallback(callback);
notifyProviderState();
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
@Override
@@ -296,7 +296,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()
&& updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
}
@@ -643,7 +643,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
notifyProviderState();
}
- void notifySessionInfoUpdated() {
+ void notifyGlobalSessionInfoUpdated() {
if (mCallback == null) {
return;
}
@@ -656,7 +656,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
sessionInfo = mSessionInfos.get(0);
}
- mCallback.onSessionUpdated(this, sessionInfo);
+ mCallback.onSessionUpdated(
+ this, sessionInfo, /* packageNamesWithRoutingSessionOverrides= */ Set.of());
}
@Override
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
index a27a14b87d53..8931e3a1426e 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
@@ -44,6 +44,7 @@ import com.android.server.media.MediaRoute2ProviderServiceProxy.SystemMediaSessi
import java.util.Collections;
import java.util.Map;
+import java.util.Set;
import java.util.stream.Stream;
/**
@@ -82,7 +83,7 @@ import java.util.stream.Stream;
/** Maps request ids to pending session creation callbacks. */
@GuardedBy("mLock")
- private final LongSparseArray<PendingSessionCreationCallbackImpl> mPendingSessionCreations =
+ private final LongSparseArray<SystemMediaSessionCallbackImpl> mPendingSessionCreations =
new LongSparseArray<>();
private static final ComponentName COMPONENT_NAME =
@@ -157,7 +158,7 @@ import java.util.stream.Stream;
}
}
var pendingCreationCallback =
- new PendingSessionCreationCallbackImpl(
+ new SystemMediaSessionCallbackImpl(
targetProviderProxyId, requestId, clientPackageName);
mPendingSessionCreations.put(requestId, pendingCreationCallback);
targetProviderProxyRecord.requestCreateSystemMediaSession(
@@ -242,7 +243,7 @@ import java.util.stream.Stream;
}
updateSessionInfo();
notifyProviderState();
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
@Override
@@ -252,7 +253,7 @@ import java.util.stream.Stream;
updateProviderInfo();
}
updateSessionInfo();
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
/**
@@ -302,6 +303,43 @@ import java.util.stream.Stream;
setProviderState(builder.build());
}
+ @Override
+ /* package */ void notifyGlobalSessionInfoUpdated() {
+ if (mCallback == null) {
+ return;
+ }
+
+ RoutingSessionInfo sessionInfo;
+ Set<String> packageNamesWithRoutingSessionOverrides;
+ synchronized (mLock) {
+ if (mSessionInfos.isEmpty()) {
+ return;
+ }
+ packageNamesWithRoutingSessionOverrides = mPackageNameToSessionRecord.keySet();
+ sessionInfo = mSessionInfos.getFirst();
+ }
+
+ mCallback.onSessionUpdated(this, sessionInfo, packageNamesWithRoutingSessionOverrides);
+ }
+
+ private void onSessionOverrideUpdated(RoutingSessionInfo sessionInfo) {
+ // TODO: b/362507305 - Consider adding routes from other provider services. This is not a
+ // trivial change because a provider1-route to provider2-route transfer has seemingly two
+ // possible approachies. Either we first release the current session and then create the new
+ // one, in which case the audio is briefly going to leak through the system route. On the
+ // other hand, if we first create the provider2 session, then there will be a period during
+ // which there will be two overlapping routing policies asking for the exact same media
+ // stream.
+ var builder = new RoutingSessionInfo.Builder(sessionInfo);
+ mLastSystemProviderInfo.getRoutes().stream()
+ .map(MediaRoute2Info::getOriginalId)
+ .forEach(builder::addTransferableRoute);
+ mCallback.onSessionUpdated(
+ /* provider= */ this,
+ builder.build(),
+ /* packageNamesWithRoutingSessionOverrides= */ Set.of());
+ }
+
/**
* Equivalent to {@link #asSystemRouteId}, except it takes a unique route id instead of a
* original id.
@@ -432,13 +470,15 @@ import java.util.stream.Stream;
}
}
- private class PendingSessionCreationCallbackImpl implements SystemMediaSessionCallback {
+ private class SystemMediaSessionCallbackImpl implements SystemMediaSessionCallback {
private final String mProviderId;
private final long mRequestId;
private final String mClientPackageName;
+ // Accessed only on mHandler.
+ @Nullable private SystemMediaSessionRecord mSessionRecord;
- private PendingSessionCreationCallbackImpl(
+ private SystemMediaSessionCallbackImpl(
String providerId, long requestId, String clientPackageName) {
mProviderId = providerId;
mRequestId = requestId;
@@ -446,27 +486,51 @@ import java.util.stream.Stream;
}
@Override
- public void onSessionUpdate(RoutingSessionInfo sessionInfo) {
- SystemMediaSessionRecord systemMediaSessionRecord =
- new SystemMediaSessionRecord(mProviderId, sessionInfo);
- synchronized (mLock) {
- mPackageNameToSessionRecord.put(mClientPackageName, systemMediaSessionRecord);
- mPendingSessionCreations.remove(mRequestId);
- }
+ public void onSessionUpdate(@NonNull RoutingSessionInfo sessionInfo) {
+ mHandler.post(
+ () -> {
+ if (mSessionRecord != null) {
+ mSessionRecord.onSessionUpdate(sessionInfo);
+ }
+ SystemMediaSessionRecord systemMediaSessionRecord =
+ new SystemMediaSessionRecord(mProviderId, sessionInfo);
+ RoutingSessionInfo translatedSession;
+ synchronized (mLock) {
+ mSessionRecord = systemMediaSessionRecord;
+ mPackageNameToSessionRecord.put(
+ mClientPackageName, systemMediaSessionRecord);
+ mPendingSessionCreations.remove(mRequestId);
+ translatedSession = systemMediaSessionRecord.mTranslatedSessionInfo;
+ }
+ onSessionOverrideUpdated(translatedSession);
+ });
}
@Override
public void onRequestFailed(long requestId, @Reason int reason) {
- synchronized (mLock) {
- mPendingSessionCreations.remove(mRequestId);
- }
- notifyRequestFailed(requestId, reason);
+ mHandler.post(
+ () -> {
+ if (mSessionRecord != null) {
+ mSessionRecord.onRequestFailed(requestId, reason);
+ }
+ synchronized (mLock) {
+ mPendingSessionCreations.remove(mRequestId);
+ }
+ notifyRequestFailed(requestId, reason);
+ });
}
@Override
public void onSessionReleased() {
- // Unexpected. The session hasn't yet been created.
- throw new IllegalStateException();
+ mHandler.post(
+ () -> {
+ if (mSessionRecord != null) {
+ mSessionRecord.onSessionReleased();
+ } else {
+ // Should never happen. The session hasn't yet been created.
+ throw new IllegalStateException();
+ }
+ });
}
}
@@ -494,12 +558,13 @@ import java.util.stream.Stream;
}
@Override
- public void onSessionUpdate(RoutingSessionInfo sessionInfo) {
+ public void onSessionUpdate(@NonNull RoutingSessionInfo sessionInfo) {
+ RoutingSessionInfo translatedSessionInfo = mTranslatedSessionInfo;
synchronized (mLock) {
mSourceSessionInfo = sessionInfo;
mTranslatedSessionInfo = asSystemProviderSession(sessionInfo);
}
- notifySessionInfoUpdated();
+ onSessionOverrideUpdated(translatedSessionInfo);
}
@Override
@@ -512,7 +577,7 @@ import java.util.stream.Stream;
synchronized (mLock) {
removeSelfFromSessionMap();
}
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
@GuardedBy("SystemMediaRoute2Provider2.this.mLock")
@@ -536,6 +601,7 @@ import java.util.stream.Stream;
var builder =
new RoutingSessionInfo.Builder(session)
.setProviderId(mUniqueId)
+ .setSystemSession(true)
.clearSelectedRoutes()
.clearSelectableRoutes()
.clearDeselectableRoutes()
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 0c3c46c75eee..7f88e7463208 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -479,6 +479,7 @@ public class Notifier {
case PowerManager.PARTIAL_WAKE_LOCK:
return BatteryStats.WAKE_TYPE_PARTIAL;
+ case PowerManager.FULL_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
return BatteryStats.WAKE_TYPE_FULL;
@@ -503,6 +504,31 @@ public class Notifier {
}
}
+ @VisibleForTesting
+ int getWakelockMonitorTypeForLogging(int flags) {
+ switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ case PowerManager.FULL_WAKE_LOCK, PowerManager.SCREEN_DIM_WAKE_LOCK,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+ return PowerManager.FULL_WAKE_LOCK;
+ case PowerManager.DRAW_WAKE_LOCK:
+ return PowerManager.DRAW_WAKE_LOCK;
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+ if (mSuspendWhenScreenOffDueToProximityConfig) {
+ return -1;
+ }
+ return PowerManager.PARTIAL_WAKE_LOCK;
+ case PowerManager.PARTIAL_WAKE_LOCK:
+ return PowerManager.PARTIAL_WAKE_LOCK;
+ case PowerManager.DOZE_WAKE_LOCK:
+ // Doze wake locks are an internal implementation detail of the
+ // communication between dream manager service and power manager
+ // service. They have no additive battery impact.
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
/**
* Notifies that the device is changing wakefulness.
* This function may be called even if the previous change hasn't finished in
@@ -1288,7 +1314,7 @@ public class Notifier {
if (mBatteryStatsInternal == null) {
return;
}
- final int type = flags & PowerManager.WAKE_LOCK_LEVEL_MASK;
+ final int type = getWakelockMonitorTypeForLogging(flags);
if (workSource == null || workSource.isEmpty()) {
final int mappedUid = mBatteryStatsInternal.getOwnerUid(ownerUid);
mFrameworkStatsLogger.wakelockStateChanged(mappedUid, tag, type, eventType);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 83b273c04648..a01fa48f2af2 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -10355,7 +10355,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
if (!isVisibleRequested()) {
// TODO(b/294925498): Remove this finishing check once we have accurate ready tracking.
- if (task != null && task.getPausingActivity() == this) {
+ if (task != null && task.getPausingActivity() == this
+ // Display is asleep, so nothing will be visible anyways.
+ && !mDisplayContent.isSleeping()) {
// Visibility of starting activities isn't calculated until pause-complete, so if
// this is not paused yet, don't consider it ready.
return false;
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java b/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
index caff96ba4a9f..4fac81b06680 100644
--- a/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java
@@ -35,8 +35,6 @@ import android.annotation.NonNull;
import android.content.res.Configuration;
import android.graphics.Rect;
-import com.android.window.flags.Flags;
-
/**
* Encapsulate overrides and configurations about app compat reachability.
*/
@@ -157,33 +155,27 @@ class AppCompatReachabilityOverrides {
}
/**
- * @return {@value true} if the vertical reachability should be allowed in case of
+ * @return {@code true} if the vertical reachability should be allowed in case of
* thin letterboxing.
*/
boolean allowVerticalReachabilityForThinLetterbox() {
- if (!Flags.disableThinLetterboxingPolicy()) {
- return true;
- }
// When the flag is enabled we allow vertical reachability only if the
// app is not thin letterboxed vertically.
return !isVerticalThinLetterboxed();
}
/**
- * @return {@value true} if the horizontal reachability should be enabled in case of
+ * @return {@code true} if the horizontal reachability should be enabled in case of
* thin letterboxing.
*/
boolean allowHorizontalReachabilityForThinLetterbox() {
- if (!Flags.disableThinLetterboxingPolicy()) {
- return true;
- }
// When the flag is enabled we allow horizontal reachability only if the
// app is not thin pillarboxed.
return !isHorizontalThinLetterboxed();
}
/**
- * @return {@value true} if the resulting app is letterboxed in a way defined as thin.
+ * @return {@code true} if the resulting app is letterboxed in a way defined as thin.
*/
boolean isVerticalThinLetterboxed() {
final int thinHeight = mAppCompatConfiguration.getThinLetterboxHeightPx();
@@ -200,7 +192,7 @@ class AppCompatReachabilityOverrides {
}
/**
- * @return {@value true} if the resulting app is pillarboxed in a way defined as thin.
+ * @return {@code true} if the resulting app is pillarboxed in a way defined as thin.
*/
boolean isHorizontalThinLetterboxed() {
final int thinWidth = mAppCompatConfiguration.getThinLetterboxWidthPx();
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 0369a0ff4c76..9f88bc952351 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -164,15 +164,13 @@ final class AppCompatUtils {
appCompatTaskInfo.setIsFromLetterboxDoubleTap(reachabilityOverrides.isFromDoubleTap());
+ appCompatTaskInfo.topActivityAppBounds.set(getAppBounds(top));
final boolean isTopActivityLetterboxed = top.areBoundsLetterboxed();
appCompatTaskInfo.setTopActivityLetterboxed(isTopActivityLetterboxed);
if (isTopActivityLetterboxed) {
final Rect bounds = top.getBounds();
- final Rect appBounds = getAppBounds(top);
appCompatTaskInfo.topActivityLetterboxWidth = bounds.width();
appCompatTaskInfo.topActivityLetterboxHeight = bounds.height();
- appCompatTaskInfo.topActivityLetterboxAppWidth = appBounds.width();
- appCompatTaskInfo.topActivityLetterboxAppHeight = appBounds.height();
// TODO(b/379824541) Remove duplicate information.
appCompatTaskInfo.topActivityLetterboxBounds = bounds;
// We need to consider if letterboxed or pillarboxed.
@@ -281,8 +279,7 @@ final class AppCompatUtils {
info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
- info.topActivityLetterboxAppHeight = TaskInfo.PROPERTY_VALUE_UNSET;
- info.topActivityLetterboxAppWidth = TaskInfo.PROPERTY_VALUE_UNSET;
+ info.topActivityAppBounds.setEmpty();
info.topActivityLetterboxBounds = null;
info.cameraCompatTaskInfo.freeformCameraCompatMode =
CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_UNSPECIFIED;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9d9c53dfe0f4..db62cebf7603 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2945,7 +2945,7 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
if (callingPid != MY_PID) {
- throw new WindowManager.InvalidDisplayException(
+ throw new IllegalArgumentException(
"attachWindowContextToDisplayContent: trying to attach to a"
+ " non-existing display:" + displayId);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 60130d1f97be..f10b7b9a95a4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -91,6 +91,7 @@ import android.server.ServerProtoEnums;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
+import android.tracing.perfetto.InitArguments;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Dumpable;
@@ -792,6 +793,12 @@ public final class SystemServer implements Dumpable {
private void run() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
try {
+ if (android.tracing.Flags.systemServerLargePerfettoShmemBuffer()) {
+ // Explicitly initialize a 4 MB shmem buffer for Perfetto producers (b/382369925)
+ android.tracing.perfetto.Producer.init(new InitArguments(
+ InitArguments.PERFETTO_BACKEND_SYSTEM, 4 * 1024));
+ }
+
t.traceBegin("InitBeforeStartServices");
// Record the process start information in sys props.
@@ -3114,10 +3121,10 @@ public final class SystemServer implements Dumpable {
if (com.android.ranging.flags.Flags.rangingStackEnabled()) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)
|| context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_RTT)
+ PackageManager.FEATURE_WIFI_AWARE)
|| (com.android.ranging.flags.Flags.rangingCsEnabled()
&& context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_BLUETOOTH_LE_CHANNEL_SOUNDING))) {
+ PackageManager.FEATURE_BLUETOOTH_LE))) {
t.traceBegin("RangingService");
// TODO: b/375264320 - Remove after RELEASE_RANGING_STACK is ramped to next.
try {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index 5d427139a857..c65024f8f9d5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -26,6 +26,7 @@ import org.junit.Test
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -43,7 +44,7 @@ class DisplayTopologyCoordinatorTest {
@Before
fun setUp() {
- displayInfo.displayId = 2
+ displayInfo.displayId = Display.DEFAULT_DISPLAY
displayInfo.logicalWidth = 300
displayInfo.logicalHeight = 200
displayInfo.logicalDensityDpi = 100
@@ -90,6 +91,44 @@ class DisplayTopologyCoordinatorTest {
}
@Test
+ fun updateDisplay() {
+ whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat()))
+ .thenReturn(true)
+
+ coordinator.onDisplayChanged(displayInfo)
+
+ verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
+ }
+
+ @Test
+ fun updateDisplay_notChanged() {
+ whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat()))
+ .thenReturn(false)
+
+ coordinator.onDisplayChanged(displayInfo)
+
+ verify(mockTopologyChangedCallback, never()).invoke(any())
+ }
+
+ @Test
+ fun removeDisplay() {
+ whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(true)
+
+ coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)
+
+ verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
+ }
+
+ @Test
+ fun removeDisplay_notChanged() {
+ whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(false)
+
+ coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)
+
+ verify(mockTopologyChangedCallback, never()).invoke(any())
+ }
+
+ @Test
fun getTopology_copy() {
assertThat(coordinator.topology).isEqualTo(mockTopologyCopy)
}
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 96741e0b1e87..469bd66b7e7b 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -21,6 +21,7 @@ import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
@@ -889,6 +890,32 @@ public class NotifierTest {
"my.package.name", false, null, null);
}
+ @Test
+ public void getWakelockMonitorTypeForLogging_evaluatesWakelockLevel() {
+ createNotifier();
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.SCREEN_DIM_WAKE_LOCK),
+ PowerManager.FULL_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK), PowerManager.FULL_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.DRAW_WAKE_LOCK),
+ PowerManager.DRAW_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.PARTIAL_WAKE_LOCK),
+ PowerManager.PARTIAL_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK),
+ PowerManager.PARTIAL_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.DOZE_WAKE_LOCK), -1);
+
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
+ .thenReturn(true);
+
+ createNotifier();
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK), -1);
+ }
+
private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
index 1edbcd527bf4..463254caa845 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
@@ -23,14 +23,10 @@ import static org.mockito.Mockito.spy;
import android.compat.testing.PlatformCompatChangeRule;
import android.graphics.Rect;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
-import com.android.window.flags.Flags;
-
import junit.framework.Assert;
import org.junit.Rule;
@@ -125,8 +121,7 @@ public class AppCompatReachabilityOverridesTest extends WindowTestsBase {
}
@Test
- @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
- public void testAllowReachabilityForThinLetterboxWithFlagEnabled() {
+ public void testAllowReachabilityForThinLetterbox_disableForThinLetterboxing() {
runTestScenario((robot) -> {
robot.activity().createActivityWithComponent();
@@ -142,24 +137,6 @@ public class AppCompatReachabilityOverridesTest extends WindowTestsBase {
});
}
- @Test
- @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
- public void testAllowReachabilityForThinLetterboxWithFlagDisabled() {
- runTestScenario((robot) -> {
- robot.activity().createActivityWithComponent();
-
- robot.configureIsVerticalThinLetterboxed(/* isThin */ true);
- robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ true);
- robot.configureIsHorizontalThinLetterboxed(/* isThin */ true);
- robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ true);
-
- robot.configureIsVerticalThinLetterboxed(/* isThin */ false);
- robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ true);
- robot.configureIsHorizontalThinLetterboxed(/* isThin */ false);
- robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ true);
- });
- }
-
/**
* Runs a test scenario providing a Robot.
*/
diff --git a/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
index 281837920548..860d9f680c4c 100644
--- a/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
+++ b/tests/Input/src/com/android/test/input/KeyCharacterMapTest.kt
@@ -16,10 +16,17 @@
package com.android.test.input
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+
import android.view.KeyCharacterMap
import android.view.KeyEvent
+import com.android.hardware.input.Flags
+
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Rule
import org.junit.Test
/**
@@ -30,26 +37,38 @@ import org.junit.Test
*
*/
class KeyCharacterMapTest {
+ @get:Rule
+ val setFlagsRule = SetFlagsRule()
+
@Test
+ @EnableFlags(Flags.FLAG_REMOVE_FALLBACK_MODIFIERS)
fun testGetFallback() {
// Based off of VIRTUAL kcm fallbacks.
val keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD)
// One modifier fallback.
- assertEquals(
- keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
- KeyEvent.META_CTRL_ON).keyCode,
- KeyEvent.KEYCODE_LANGUAGE_SWITCH)
+ val oneModifierFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
+ KeyEvent.META_CTRL_ON)
+ assertEquals(KeyEvent.KEYCODE_LANGUAGE_SWITCH, oneModifierFallback.keyCode)
+ assertEquals(0, oneModifierFallback.metaState)
// Multiple modifier fallback.
- assertEquals(
- keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_DEL,
- KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON).keyCode,
- KeyEvent.KEYCODE_BACK)
+ val twoModifierFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_DEL,
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON)
+ assertEquals(KeyEvent.KEYCODE_BACK, twoModifierFallback.keyCode)
+ assertEquals(0, twoModifierFallback.metaState)
// No default button, fallback only.
- assertEquals(
- keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_BUTTON_A, 0).keyCode,
- KeyEvent.KEYCODE_DPAD_CENTER)
+ val keyOnlyFallback =
+ keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_BUTTON_A, 0)
+ assertEquals(KeyEvent.KEYCODE_DPAD_CENTER, keyOnlyFallback.keyCode)
+ assertEquals(0, keyOnlyFallback.metaState)
+
+ // A key event that is not an exact match for a fallback. Expect a null return.
+ // E.g. Ctrl + Space -> LanguageSwitch
+ // Ctrl + Alt + Space -> Ctrl + Alt + Space (No fallback).
+ val noMatchFallback = keyCharacterMap.getFallbackAction(KeyEvent.KEYCODE_SPACE,
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON)
+ assertNull(noMatchFallback)
}
}
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 661df4d0fe33..e24fe07f959b 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -683,8 +683,6 @@ class ChunkPrinter {
item->PrettyPrint(printer_);
printer_->Print(")");
}
-
- printer_->Print("\n");
}
void PrintQualifiers(uint32_t qualifiers) const {
@@ -763,11 +761,13 @@ class ChunkPrinter {
bool PrintTableType(const ResTable_type* chunk) {
printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id)));
- printer_->Print(StringPrintf(
- " name: %s",
- android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1)
- .c_str()));
+ const auto name =
+ android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1);
+ printer_->Print(StringPrintf(" name: %s", name.c_str()));
printer_->Print(StringPrintf(" flags: 0x%02x", android::util::DeviceToHost32(chunk->flags)));
+ printer_->Print(android::util::DeviceToHost32(chunk->flags) & ResTable_type::FLAG_SPARSE
+ ? " (SPARSE)"
+ : " (DENSE)");
printer_->Print(
StringPrintf(" entryCount: %u", android::util::DeviceToHost32(chunk->entryCount)));
printer_->Print(
@@ -777,8 +777,7 @@ class ChunkPrinter {
config.copyFromDtoH(chunk->config);
printer_->Print(StringPrintf(" config: %s\n", config.to_string().c_str()));
- const ResourceType* type = ParseResourceType(
- android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1));
+ const ResourceType* type = ParseResourceType(name);
printer_->Indent();
@@ -817,11 +816,8 @@ class ChunkPrinter {
for (size_t i = 0; i < map_entry_count; i++) {
PrintResValue(&(maps[i].value), config, type);
- printer_->Print(StringPrintf(
- " name: %s name-id:%d\n",
- android::util::GetString(key_pool_, android::util::DeviceToHost32(maps[i].name.ident))
- .c_str(),
- android::util::DeviceToHost32(maps[i].name.ident)));
+ printer_->Print(StringPrintf(" name-id: 0x%08x\n",
+ android::util::DeviceToHost32(maps[i].name.ident)));
}
} else {
printer_->Print("\n");
@@ -829,6 +825,8 @@ class ChunkPrinter {
// Print the value of the entry
Res_value value = entry->value();
PrintResValue(&value, config, type);
+
+ printer_->Print("\n");
}
printer_->Undent();