summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp3
-rw-r--r--cmds/bootanimation/BootAnimation.cpp30
-rw-r--r--core/java/android/animation/AnimationHandler.java14
-rw-r--r--core/java/android/app/DisabledWallpaperManager.java10
-rw-r--r--core/java/android/app/WallpaperManager.java33
-rw-r--r--core/java/android/os/SystemVibrator.java18
-rw-r--r--core/java/android/provider/Settings.java3
-rw-r--r--core/java/android/service/controls/ControlsProviderService.java14
-rw-r--r--core/java/android/service/dreams/DreamManagerInternal.java15
-rw-r--r--core/java/android/service/voice/HotwordAudioStream.aidl (renamed from packages/SystemUI/src/com/android/systemui/ChooserSelectorResourceHelper.java)16
-rw-r--r--core/java/android/service/voice/HotwordAudioStream.java444
-rw-r--r--core/java/android/service/voice/HotwordDetectedResult.java93
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java66
-rw-r--r--core/java/android/window/WindowContainerTransaction.java36
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java9
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java8
-rw-r--r--core/java/com/android/internal/policy/DecorView.java2
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java5
-rw-r--r--core/tests/coretests/src/android/os/VibratorTest.java62
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java7
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java51
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java7
-rw-r--r--data/etc/services.core.protolog.json24
-rw-r--r--graphics/java/android/graphics/Canvas.java23
-rw-r--r--graphics/java/android/graphics/Paint.java7
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/OWNERS5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java84
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java216
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java130
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java277
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java147
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt130
-rw-r--r--media/jni/android_media_MediaCodec.cpp32
-rw-r--r--packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default.xml33
-rw-r--r--packages/SettingsLib/res/values/carrierid_icon_overrides.xml32
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/MobileIconCarrierIdOverrides.kt142
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/mobile/MobileIconCarrierIdOverridesTest.java140
-rw-r--r--packages/SystemUI/Android.bp2
-rw-r--r--packages/SystemUI/AndroidManifest.xml10
-rw-r--r--packages/SystemUI/compose/features/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/res-keyguard/values-land/dimens.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/drawable/internet_dialog_selected_effect.xml26
-rw-r--r--packages/SystemUI/res/layout/internet_connectivity_dialog.xml18
-rw-r--r--packages/SystemUI/res/layout/media_session_view.xml11
-rw-r--r--packages/SystemUI/res/layout/qs_dialog_secondary_mobile_network.xml63
-rw-r--r--packages/SystemUI/res/layout/screen_record_options.xml87
-rw-r--r--packages/SystemUI/res/layout/screen_share_dialog.xml94
-rw-r--r--packages/SystemUI/res/layout/screenshot_static.xml41
-rw-r--r--packages/SystemUI/res/layout/wireless_charging_layout.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/dimens.xml1
-rw-r--r--packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw720dp-port/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/config.xml6
-rw-r--r--packages/SystemUI/res/values/dimens.xml10
-rw-r--r--packages/SystemUI/res/values/strings.xml31
-rw-r--r--packages/SystemUI/res/values/styles.xml2
-rw-r--r--packages/SystemUI/res/xml/media_session_collapsed.xml10
-rw-r--r--packages/SystemUI/res/xml/media_session_expanded.xml10
-rw-r--r--packages/SystemUI/res/xml/qqs_header.xml12
-rw-r--r--packages/SystemUI/res/xml/qs_header_new.xml13
-rw-r--r--packages/SystemUI/shared/Android.bp9
-rw-r--r--packages/SystemUI/shared/proguard.flags4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt72
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt111
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java42
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java5
-rw-r--r--packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt91
-rw-r--r--packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt79
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java42
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java109
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java23
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconView.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt48
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/ChooserSelector.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt207
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java118
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/BatterySpecs.kt109
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt302
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt297
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt115
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt)116
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java189
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java153
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt110
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt184
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/CameraLauncher.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java265
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt140
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCache.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java503
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt (renamed from packages/SystemUI/src/com/android/systemui/ripple/MultiRippleController.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt (renamed from packages/SystemUI/src/com/android/systemui/ripple/MultiRippleView.kt)20
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt (renamed from packages/SystemUI/src/com/android/systemui/ripple/RippleAnimation.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt (renamed from packages/SystemUI/src/com/android/systemui/ripple/RippleAnimationConfig.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt (renamed from packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt)124
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt (renamed from packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt)53
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt (renamed from packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt148
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt121
-rw-r--r--packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt123
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt106
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java6
-rw-r--r--packages/SystemUI/tests/AndroidManifest.xml21
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt87
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt180
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/battery/BatterySpecsTest.kt101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java346
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt112
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt122
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt351
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt144
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt254
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt90
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt)40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt)138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java169
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java61
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt109
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt59
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.java137
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt119
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java63
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java179
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/ripple/MultiRippleControllerTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleViewTest.kt58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/ripple/RippleAnimationTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt151
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt12
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java37
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java37
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java28
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java9
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java9
-rw-r--r--services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java9
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java25
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java28
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java14
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java155
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java11
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java16
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java8
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java48
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java11
-rw-r--r--services/core/java/com/android/server/power/PowerManagerShellCommand.java70
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java19
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java14
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java19
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java36
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java68
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java56
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java52
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java37
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java11
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java37
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java48
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java35
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java233
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java75
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java26
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java17
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java45
408 files changed, 12910 insertions, 3727 deletions
diff --git a/Android.bp b/Android.bp
index df6fdaa5fdf6..caec3a21e89f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -151,6 +151,9 @@ java_library {
visibility: [
// DO NOT ADD ANY MORE ENTRIES TO THIS LIST
"//external/robolectric-shadows:__subpackages__",
+ //This will eventually replace the item above, and serves the
+ //same purpose.
+ "//external/robolectric:__subpackages__",
"//frameworks/layoutlib:__subpackages__",
],
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 50c8e933d25f..3f6046f01f11 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -132,14 +132,14 @@ static const char IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE[] = R"(
uniform sampler2D uTexture;
uniform float uFade;
uniform float uColorProgress;
- uniform vec4 uStartColor0;
- uniform vec4 uStartColor1;
- uniform vec4 uStartColor2;
- uniform vec4 uStartColor3;
- uniform vec4 uEndColor0;
- uniform vec4 uEndColor1;
- uniform vec4 uEndColor2;
- uniform vec4 uEndColor3;
+ uniform vec3 uStartColor0;
+ uniform vec3 uStartColor1;
+ uniform vec3 uStartColor2;
+ uniform vec3 uStartColor3;
+ uniform vec3 uEndColor0;
+ uniform vec3 uEndColor1;
+ uniform vec3 uEndColor2;
+ uniform vec3 uEndColor3;
varying highp vec2 vUv;
void main() {
vec4 mask = texture2D(uTexture, vUv);
@@ -152,12 +152,12 @@ static const char IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE[] = R"(
* step(cWhiteMaskThreshold, g)
* step(cWhiteMaskThreshold, b)
* step(cWhiteMaskThreshold, a);
- vec4 color = r * mix(uStartColor0, uEndColor0, uColorProgress)
+ vec3 color = r * mix(uStartColor0, uEndColor0, uColorProgress)
+ g * mix(uStartColor1, uEndColor1, uColorProgress)
+ b * mix(uStartColor2, uEndColor2, uColorProgress)
+ a * mix(uStartColor3, uEndColor3, uColorProgress);
- color = mix(color, vec4(vec3((r + g + b + a) * 0.25), 1.0), useWhiteMask);
- gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a;
+ color = mix(color, vec3((r + g + b + a) * 0.25), useWhiteMask);
+ gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade));
})";
static const char IMAGE_FRAG_SHADER_SOURCE[] = R"(
precision mediump float;
@@ -1440,12 +1440,12 @@ void BootAnimation::initDynamicColors() {
for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
float *startColor = mAnimation->startColors[i];
float *endColor = mAnimation->endColors[i];
- glUniform4f(glGetUniformLocation(mImageShader,
+ glUniform3f(glGetUniformLocation(mImageShader,
(U_START_COLOR_PREFIX + std::to_string(i)).c_str()),
- startColor[0], startColor[1], startColor[2], 1 /* alpha */);
- glUniform4f(glGetUniformLocation(mImageShader,
+ startColor[0], startColor[1], startColor[2]);
+ glUniform3f(glGetUniformLocation(mImageShader,
(U_END_COLOR_PREFIX + std::to_string(i)).c_str()),
- endColor[0], endColor[1], endColor[2], 1 /* alpha */);
+ endColor[0], endColor[1], endColor[2]);
}
mImageColorProgressLocation = glGetUniformLocation(mImageShader, U_COLOR_PROGRESS);
}
diff --git a/core/java/android/animation/AnimationHandler.java b/core/java/android/animation/AnimationHandler.java
index 1403ba2744b3..dcabf57cb8a9 100644
--- a/core/java/android/animation/AnimationHandler.java
+++ b/core/java/android/animation/AnimationHandler.java
@@ -219,12 +219,14 @@ public class AnimationHandler {
return;
}
for (int i = 0; i < mAnimationCallbacks.size(); ++i) {
- Animator animator = ((Animator) mAnimationCallbacks.get(i));
- if (animator != null
- && animator.getTotalDuration() == Animator.DURATION_INFINITE
- && !animator.isPaused()) {
- mPausedAnimators.add(animator);
- animator.pause();
+ AnimationFrameCallback callback = mAnimationCallbacks.get(i);
+ if (callback instanceof Animator) {
+ Animator animator = ((Animator) callback);
+ if (animator.getTotalDuration() == Animator.DURATION_INFINITE
+ && !animator.isPaused()) {
+ mPausedAnimators.add(animator);
+ animator.pause();
+ }
}
}
};
diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java
index ae3a9e6668ab..0d14c0bcf1c9 100644
--- a/core/java/android/app/DisabledWallpaperManager.java
+++ b/core/java/android/app/DisabledWallpaperManager.java
@@ -193,6 +193,16 @@ final class DisabledWallpaperManager extends WallpaperManager {
}
@Override
+ public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which) {
+ return unsupported();
+ }
+
+ @Override
+ public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which, int userId) {
+ return unsupported();
+ }
+
+ @Override
public int getWallpaperId(int which) {
return unsupportedInt();
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index ff69491352ff..c99fa3d3177c 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1320,18 +1320,16 @@ public class WallpaperManager {
}
/**
- * Returns the information about the wallpaper if the current wallpaper is
- * a live wallpaper component. Otherwise, if the wallpaper is a static image,
- * this returns null.
+ * Returns the information about the home screen wallpaper if its current wallpaper is a live
+ * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
*/
public WallpaperInfo getWallpaperInfo() {
return getWallpaperInfo(mContext.getUserId());
}
/**
- * Returns the information about the wallpaper if the current wallpaper is
- * a live wallpaper component. Otherwise, if the wallpaper is a static image,
- * this returns null.
+ * Returns the information about the home screen wallpaper if its current wallpaper is a live
+ * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
*
* @param userId Owner of the wallpaper.
* @hide
@@ -1350,6 +1348,29 @@ public class WallpaperManager {
}
/**
+ * Returns the information about the home screen wallpaper if its current wallpaper is a live
+ * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
+ *
+ * @param which Specifies wallpaper destination (home or lock).
+ * @hide
+ */
+ public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which) {
+ return getWallpaperInfo();
+ }
+
+ /**
+ * Returns the information about the designated wallpaper if its current wallpaper is a live
+ * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
+ *
+ * @param which Specifies wallpaper destination (home or lock).
+ * @param userId Owner of the wallpaper.
+ * @hide
+ */
+ public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which, int userId) {
+ return getWallpaperInfo(userId);
+ }
+
+ /**
* Get the ID of the current wallpaper of the given kind. If there is no
* such wallpaper configured, returns a negative number.
*
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 6091bf9088d3..bf72b1d7a035 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.hardware.vibrator.IVibrator;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Range;
@@ -313,8 +314,14 @@ public class SystemVibrator extends Vibrator {
private static final float EPSILON = 1e-5f;
public MultiVibratorInfo(VibratorInfo[] vibrators) {
+ // Need to use an extra constructor to share the computation in super initialization.
+ this(vibrators, frequencyProfileIntersection(vibrators));
+ }
+
+ private MultiVibratorInfo(VibratorInfo[] vibrators,
+ VibratorInfo.FrequencyProfile mergedProfile) {
super(/* id= */ -1,
- capabilitiesIntersection(vibrators),
+ capabilitiesIntersection(vibrators, mergedProfile.isEmpty()),
supportedEffectsIntersection(vibrators),
supportedBrakingIntersection(vibrators),
supportedPrimitivesAndDurationsIntersection(vibrators),
@@ -323,14 +330,19 @@ public class SystemVibrator extends Vibrator {
integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax),
integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax),
floatPropertyIntersection(vibrators, VibratorInfo::getQFactor),
- frequencyProfileIntersection(vibrators));
+ mergedProfile);
}
- private static int capabilitiesIntersection(VibratorInfo[] infos) {
+ private static int capabilitiesIntersection(VibratorInfo[] infos,
+ boolean frequencyProfileIsEmpty) {
int intersection = ~0;
for (VibratorInfo info : infos) {
intersection &= info.getCapabilities();
}
+ if (frequencyProfileIsEmpty) {
+ // Revoke frequency control if the merged frequency profile ended up empty.
+ intersection &= ~IVibrator.CAP_FREQUENCY_CONTROL;
+ }
return intersection;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a955dbba97e7..5c8c117481b0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -18441,6 +18441,9 @@ public final class Settings {
/**
* Activity Action: For system or preinstalled apps to show their {@link Activity} embedded
* in Settings app on large screen devices.
+ *
+ * Developers should resolve the Intent action before using it.
+ *
* <p>
* Input: {@link #EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI} must be included to
* specify the intent for the activity which will be embedded in Settings app.
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index 47b16a351806..d2a4ae282061 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -55,6 +55,20 @@ public abstract class ControlsProviderService extends Service {
"android.service.controls.ControlsProviderService";
/**
+ * Manifest metadata to show a custom embedded activity as part of device controls.
+ *
+ * The value of this metadata must be the {@link ComponentName} as a string of an activity in
+ * the same package that will be launched as part of a TaskView.
+ *
+ * The activity must be exported, enabled and protected by
+ * {@link Manifest.permission.BIND_CONTROLS}.
+ *
+ * @hide
+ */
+ public static final String META_DATA_PANEL_ACTIVITY =
+ "android.service.controls.META_DATA_PANEL_ACTIVITY";
+
+ /**
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index 295171ca9bbd..cd38e8a01d62 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -16,7 +16,6 @@
package android.service.dreams;
-import android.content.ComponentName;
/**
* Dream manager local system service interface.
@@ -54,17 +53,9 @@ public abstract class DreamManagerInternal {
public abstract void requestDream();
/**
- * Called by the ActivityTaskManagerService to verify that the startDreamActivity
- * request comes from the current active dream component.
+ * Whether dreaming can start given user settings and the current dock/charge state.
*
- * This function and its call path should not acquire the DreamManagerService lock
- * to avoid deadlock with the ActivityTaskManager lock.
- *
- * TODO: Make this interaction push-based - the DreamManager should inform the
- * ActivityTaskManager whenever the active dream component changes.
- *
- * @param doze If true returns the current active doze component. Otherwise, returns the
- * active dream component.
+ * @param isScreenOn True if the screen is currently on.
*/
- public abstract ComponentName getActiveDreamComponent(boolean doze);
+ public abstract boolean canStartDreaming(boolean isScreenOn);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ChooserSelectorResourceHelper.java b/core/java/android/service/voice/HotwordAudioStream.aidl
index 7a2de7b6a78d..9550c830aecc 100644
--- a/packages/SystemUI/src/com/android/systemui/ChooserSelectorResourceHelper.java
+++ b/core/java/android/service/voice/HotwordAudioStream.aidl
@@ -14,18 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui;
+package android.service.voice;
-import androidx.annotation.StringRes;
-
-import com.android.internal.R;
-
-/** Helper class for referencing resources */
-class ChooserSelectorResourceHelper {
-
- private ChooserSelectorResourceHelper() {
- }
-
- @StringRes
- static final int CONFIG_CHOOSER_ACTIVITY = R.string.config_chooserActivity;
-}
+parcelable HotwordAudioStream;
diff --git a/core/java/android/service/voice/HotwordAudioStream.java b/core/java/android/service/voice/HotwordAudioStream.java
new file mode 100644
index 000000000000..5442860df007
--- /dev/null
+++ b/core/java/android/service/voice/HotwordAudioStream.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.AudioTimestamp;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import java.util.Objects;
+
+/**
+ * Represents an audio stream supporting the hotword detection.
+ *
+ * @hide
+ */
+public final class HotwordAudioStream implements Parcelable {
+
+ /**
+ * The {@link AudioFormat} of the audio stream.
+ */
+ @NonNull
+ @UnsupportedAppUsage
+ private final AudioFormat mAudioFormat;
+
+ /**
+ * This stream starts with the audio bytes used for hotword detection, but continues streaming
+ * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ */
+ @NonNull
+ @UnsupportedAppUsage
+ private final ParcelFileDescriptor mAudioStreamParcelFileDescriptor;
+
+ /**
+ * The timestamp when the audio stream was captured by the Audio platform.
+ *
+ * <p>
+ * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying
+ * AudioRecord. The {@link HotwordDetectionService} is expected to optionally populate this
+ * field by {@link AudioRecord#getTimestamp}.
+ * </p>
+ *
+ * <p>
+ * This timestamp can be used in conjunction with the
+ * {@link HotwordDetectedResult#getHotwordOffsetMillis()} and
+ * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to
+ * timestamps.
+ * </p>
+ *
+ * @see #getAudioStreamParcelFileDescriptor()
+ */
+ @Nullable
+ @UnsupportedAppUsage
+ private final AudioTimestamp mTimestamp;
+
+ private static AudioTimestamp defaultTimestamp() {
+ return null;
+ }
+
+ /**
+ * The metadata associated with the audio stream.
+ */
+ @NonNull
+ @UnsupportedAppUsage
+ private final PersistableBundle mMetadata;
+
+ private static PersistableBundle defaultMetadata() {
+ return new PersistableBundle();
+ }
+
+ private String timestampToString() {
+ if (mTimestamp == null) {
+ return "";
+ }
+ return "TimeStamp:"
+ + " framePos=" + mTimestamp.framePosition
+ + " nanoTime=" + mTimestamp.nanoTime;
+ }
+
+ private void parcelTimestamp(Parcel dest, int flags) {
+ if (mTimestamp != null) {
+ // mTimestamp is not null, we write it to the parcel, set true.
+ dest.writeBoolean(true);
+ dest.writeLong(mTimestamp.framePosition);
+ dest.writeLong(mTimestamp.nanoTime);
+ } else {
+ // mTimestamp is null, we don't write any value out, set false.
+ dest.writeBoolean(false);
+ }
+ }
+
+ @Nullable
+ private static AudioTimestamp unparcelTimestamp(Parcel in) {
+ // If it is true, it means we wrote the value to the parcel before, parse it.
+ // Otherwise, return null.
+ if (in.readBoolean()) {
+ final AudioTimestamp timeStamp = new AudioTimestamp();
+ timeStamp.framePosition = in.readLong();
+ timeStamp.nanoTime = in.readLong();
+ return timeStamp;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Provides an instance of {@link Builder} with state corresponding to this instance.
+ * @hide
+ */
+ public Builder buildUpon() {
+ return new Builder(mAudioFormat, mAudioStreamParcelFileDescriptor)
+ .setTimestamp(mTimestamp)
+ .setMetadata(mMetadata);
+ }
+
+ /* package-private */
+ HotwordAudioStream(
+ @NonNull AudioFormat audioFormat,
+ @NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor,
+ @Nullable AudioTimestamp timestamp,
+ @NonNull PersistableBundle metadata) {
+ this.mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ this.mAudioStreamParcelFileDescriptor = audioStreamParcelFileDescriptor;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioStreamParcelFileDescriptor);
+ this.mTimestamp = timestamp;
+ this.mMetadata = metadata;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMetadata);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The {@link AudioFormat} of the audio stream.
+ */
+ @UnsupportedAppUsage
+ @NonNull
+ public AudioFormat getAudioFormat() {
+ return mAudioFormat;
+ }
+
+ /**
+ * This stream starts with the audio bytes used for hotword detection, but continues streaming
+ * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ */
+ @UnsupportedAppUsage
+ @NonNull
+ public ParcelFileDescriptor getAudioStreamParcelFileDescriptor() {
+ return mAudioStreamParcelFileDescriptor;
+ }
+
+ /**
+ * The timestamp when the audio stream was captured by the Audio platform.
+ *
+ * <p>
+ * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying
+ * AudioRecord. The {@link HotwordDetectionService} is expected to optionally populate this
+ * field by {@link AudioRecord#getTimestamp}.
+ * </p>
+ *
+ * <p>
+ * This timestamp can be used in conjunction with the
+ * {@link HotwordDetectedResult#getHotwordOffsetMillis()} and
+ * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to
+ * timestamps.
+ * </p>
+ *
+ * @see #getAudioStreamParcelFileDescriptor()
+ */
+ @UnsupportedAppUsage
+ @Nullable
+ public AudioTimestamp getTimestamp() {
+ return mTimestamp;
+ }
+
+ /**
+ * The metadata associated with the audio stream.
+ */
+ @UnsupportedAppUsage
+ @NonNull
+ public PersistableBundle getMetadata() {
+ return mMetadata;
+ }
+
+ @Override
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "HotwordAudioStream { "
+ + "audioFormat = " + mAudioFormat + ", "
+ + "audioStreamParcelFileDescriptor = " + mAudioStreamParcelFileDescriptor + ", "
+ + "timestamp = " + timestampToString() + ", "
+ + "metadata = " + mMetadata + " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(HotwordAudioStream other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ HotwordAudioStream that = (HotwordAudioStream) o;
+ //noinspection PointlessBooleanExpression
+ return Objects.equals(mAudioFormat, that.mAudioFormat)
+ && Objects.equals(mAudioStreamParcelFileDescriptor,
+ that.mAudioStreamParcelFileDescriptor)
+ && Objects.equals(mTimestamp, that.mTimestamp)
+ && Objects.equals(mMetadata, that.mMetadata);
+ }
+
+ @Override
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Objects.hashCode(mAudioFormat);
+ _hash = 31 * _hash + Objects.hashCode(mAudioStreamParcelFileDescriptor);
+ _hash = 31 * _hash + Objects.hashCode(mTimestamp);
+ _hash = 31 * _hash + Objects.hashCode(mMetadata);
+ return _hash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mTimestamp != null) flg |= 0x4;
+ dest.writeByte(flg);
+ dest.writeTypedObject(mAudioFormat, flags);
+ dest.writeTypedObject(mAudioStreamParcelFileDescriptor, flags);
+ parcelTimestamp(dest, flags);
+ dest.writeTypedObject(mMetadata, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ /* package-private */
+ HotwordAudioStream(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ AudioFormat audioFormat = (AudioFormat) in.readTypedObject(AudioFormat.CREATOR);
+ ParcelFileDescriptor audioStreamParcelFileDescriptor =
+ (ParcelFileDescriptor) in.readTypedObject(ParcelFileDescriptor.CREATOR);
+ AudioTimestamp timestamp = unparcelTimestamp(in);
+ PersistableBundle metadata = (PersistableBundle) in.readTypedObject(
+ PersistableBundle.CREATOR);
+
+ this.mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ this.mAudioStreamParcelFileDescriptor = audioStreamParcelFileDescriptor;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioStreamParcelFileDescriptor);
+ this.mTimestamp = timestamp;
+ this.mMetadata = metadata;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMetadata);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<HotwordAudioStream> CREATOR =
+ new Parcelable.Creator<HotwordAudioStream>() {
+ @Override
+ public HotwordAudioStream[] newArray(int size) {
+ return new HotwordAudioStream[size];
+ }
+
+ @Override
+ public HotwordAudioStream createFromParcel(@NonNull Parcel in) {
+ return new HotwordAudioStream(in);
+ }
+ };
+
+ /**
+ * A builder for {@link HotwordAudioStream}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+
+ @NonNull
+ private AudioFormat mAudioFormat;
+ @NonNull
+ private ParcelFileDescriptor mAudioStreamParcelFileDescriptor;
+ @Nullable
+ private AudioTimestamp mTimestamp;
+ @NonNull
+ private PersistableBundle mMetadata;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param audioFormat The {@link AudioFormat} of the audio stream.
+ * @param audioStreamParcelFileDescriptor This stream starts with the audio bytes used for
+ * hotword detection, but continues streaming
+ * the audio until the stream is shutdown by the
+ * {@link HotwordDetectionService}.
+ */
+ @UnsupportedAppUsage
+ public Builder(
+ @NonNull AudioFormat audioFormat,
+ @NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor) {
+ mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ mAudioStreamParcelFileDescriptor = audioStreamParcelFileDescriptor;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioStreamParcelFileDescriptor);
+ }
+
+ /**
+ * The {@link AudioFormat} of the audio stream.
+ */
+ @UnsupportedAppUsage
+ @NonNull
+ public Builder setAudioFormat(@NonNull AudioFormat value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mAudioFormat = value;
+ return this;
+ }
+
+ /**
+ * This stream starts with the audio bytes used for hotword detection, but continues
+ * streaming
+ * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
+ */
+ @UnsupportedAppUsage
+ @NonNull
+ public Builder setAudioStreamParcelFileDescriptor(@NonNull ParcelFileDescriptor value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mAudioStreamParcelFileDescriptor = value;
+ return this;
+ }
+
+ /**
+ * The timestamp when the audio stream was captured by the Audio platform.
+ *
+ * <p>
+ * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying
+ * AudioRecord. The {@link HotwordDetectionService} is expected to optionally populate this
+ * field by {@link AudioRecord#getTimestamp}.
+ * </p>
+ *
+ * <p>
+ * This timestamp can be used in conjunction with the
+ * {@link HotwordDetectedResult#getHotwordOffsetMillis()} and
+ * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to
+ * timestamps.
+ * </p>
+ *
+ * @see #getAudioStreamParcelFileDescriptor()
+ */
+ @UnsupportedAppUsage
+ @NonNull
+ public Builder setTimestamp(@NonNull AudioTimestamp value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mTimestamp = value;
+ return this;
+ }
+
+ /**
+ * The metadata associated with the audio stream.
+ */
+ @UnsupportedAppUsage
+ @NonNull
+ public Builder setMetadata(@NonNull PersistableBundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mMetadata = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ @UnsupportedAppUsage
+ @NonNull
+ public HotwordAudioStream build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mTimestamp = defaultTimestamp();
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mMetadata = defaultMetadata();
+ }
+ HotwordAudioStream o = new HotwordAudioStream(
+ mAudioFormat,
+ mAudioStreamParcelFileDescriptor,
+ mTimestamp,
+ mMetadata);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index ab71459ed51e..990e136197d9 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.media.AudioRecord;
import android.media.MediaSyncEvent;
@@ -31,6 +32,9 @@ import com.android.internal.R;
import com.android.internal.util.DataClass;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
/**
@@ -196,6 +200,17 @@ public final class HotwordDetectedResult implements Parcelable {
}
/**
+ * The list of the audio streams containing audio bytes that were used for hotword detection.
+ *
+ * @hide
+ */
+ @NonNull
+ private final List<HotwordAudioStream> mAudioStreams;
+ private static List<HotwordAudioStream> defaultAudioStreams() {
+ return Collections.emptyList();
+ }
+
+ /**
* App-specific extras to support trigger.
*
* <p>The size of this bundle will be limited to {@link #getMaxBundleSize}. Results will larger
@@ -353,6 +368,54 @@ public final class HotwordDetectedResult implements Parcelable {
}
}
+ /**
+ * The list of the audio streams containing audio bytes that were used for hotword detection.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public @NonNull List<HotwordAudioStream> getAudioStreams() {
+ return List.copyOf(mAudioStreams);
+ }
+
+ @DataClass.Suppress("addAudioStreams")
+ abstract static class BaseBuilder {
+ /**
+ * The list of the audio streams containing audio bytes that were used for hotword
+ * detection.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public @NonNull Builder setAudioStreams(@NonNull List<HotwordAudioStream> value) {
+ Objects.requireNonNull(value, "value should not be null");
+ final Builder builder = (Builder) this;
+ // If the code gen flag in build() is changed, we must update the flag e.g. 0x200 here.
+ builder.mBuilderFieldsSet |= 0x200;
+ builder.mAudioStreams = List.copyOf(value);
+ return builder;
+ }
+ }
+
+ /**
+ * Provides an instance of {@link Builder} with state corresponding to this instance.
+ * @hide
+ */
+ public Builder buildUpon() {
+ return new Builder()
+ .setConfidenceLevel(mConfidenceLevel)
+ .setMediaSyncEvent(mMediaSyncEvent)
+ .setHotwordOffsetMillis(mHotwordOffsetMillis)
+ .setHotwordDurationMillis(mHotwordDurationMillis)
+ .setAudioChannel(mAudioChannel)
+ .setHotwordDetectionPersonalized(mHotwordDetectionPersonalized)
+ .setScore(mScore)
+ .setPersonalizedScore(mPersonalizedScore)
+ .setHotwordPhraseId(mHotwordPhraseId)
+ .setAudioStreams(mAudioStreams)
+ .setExtras(mExtras);
+ }
+
// Code below generated by codegen v1.0.23.
@@ -436,6 +499,7 @@ public final class HotwordDetectedResult implements Parcelable {
int score,
int personalizedScore,
int hotwordPhraseId,
+ @NonNull List<HotwordAudioStream> audioStreams,
@NonNull PersistableBundle extras) {
this.mConfidenceLevel = confidenceLevel;
com.android.internal.util.AnnotationValidations.validate(
@@ -448,6 +512,9 @@ public final class HotwordDetectedResult implements Parcelable {
this.mScore = score;
this.mPersonalizedScore = personalizedScore;
this.mHotwordPhraseId = hotwordPhraseId;
+ this.mAudioStreams = audioStreams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioStreams);
this.mExtras = extras;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mExtras);
@@ -578,6 +645,7 @@ public final class HotwordDetectedResult implements Parcelable {
"score = " + mScore + ", " +
"personalizedScore = " + mPersonalizedScore + ", " +
"hotwordPhraseId = " + mHotwordPhraseId + ", " +
+ "audioStreams = " + mAudioStreams + ", " +
"extras = " + mExtras +
" }";
}
@@ -604,6 +672,7 @@ public final class HotwordDetectedResult implements Parcelable {
&& mScore == that.mScore
&& mPersonalizedScore == that.mPersonalizedScore
&& mHotwordPhraseId == that.mHotwordPhraseId
+ && Objects.equals(mAudioStreams, that.mAudioStreams)
&& Objects.equals(mExtras, that.mExtras);
}
@@ -623,6 +692,7 @@ public final class HotwordDetectedResult implements Parcelable {
_hash = 31 * _hash + mScore;
_hash = 31 * _hash + mPersonalizedScore;
_hash = 31 * _hash + mHotwordPhraseId;
+ _hash = 31 * _hash + Objects.hashCode(mAudioStreams);
_hash = 31 * _hash + Objects.hashCode(mExtras);
return _hash;
}
@@ -645,6 +715,7 @@ public final class HotwordDetectedResult implements Parcelable {
dest.writeInt(mScore);
dest.writeInt(mPersonalizedScore);
dest.writeInt(mHotwordPhraseId);
+ dest.writeParcelableList(mAudioStreams, flags);
dest.writeTypedObject(mExtras, flags);
}
@@ -669,6 +740,8 @@ public final class HotwordDetectedResult implements Parcelable {
int score = in.readInt();
int personalizedScore = in.readInt();
int hotwordPhraseId = in.readInt();
+ List<HotwordAudioStream> audioStreams = new ArrayList<>();
+ in.readParcelableList(audioStreams, HotwordAudioStream.class.getClassLoader());
PersistableBundle extras = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
this.mConfidenceLevel = confidenceLevel;
@@ -682,6 +755,9 @@ public final class HotwordDetectedResult implements Parcelable {
this.mScore = score;
this.mPersonalizedScore = personalizedScore;
this.mHotwordPhraseId = hotwordPhraseId;
+ this.mAudioStreams = audioStreams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioStreams);
this.mExtras = extras;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mExtras);
@@ -708,7 +784,7 @@ public final class HotwordDetectedResult implements Parcelable {
*/
@SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member
- public static final class Builder {
+ public static final class Builder extends BaseBuilder {
private @HotwordConfidenceLevelValue int mConfidenceLevel;
private @Nullable MediaSyncEvent mMediaSyncEvent;
@@ -719,6 +795,7 @@ public final class HotwordDetectedResult implements Parcelable {
private int mScore;
private int mPersonalizedScore;
private int mHotwordPhraseId;
+ private @NonNull List<HotwordAudioStream> mAudioStreams;
private @NonNull PersistableBundle mExtras;
private long mBuilderFieldsSet = 0L;
@@ -868,7 +945,7 @@ public final class HotwordDetectedResult implements Parcelable {
@DataClass.Generated.Member
public @NonNull Builder setExtras(@NonNull PersistableBundle value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x200;
+ mBuilderFieldsSet |= 0x400;
mExtras = value;
return this;
}
@@ -876,7 +953,7 @@ public final class HotwordDetectedResult implements Parcelable {
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull HotwordDetectedResult build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x400; // Mark builder used
+ mBuilderFieldsSet |= 0x800; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mConfidenceLevel = defaultConfidenceLevel();
@@ -906,6 +983,9 @@ public final class HotwordDetectedResult implements Parcelable {
mHotwordPhraseId = defaultHotwordPhraseId();
}
if ((mBuilderFieldsSet & 0x200) == 0) {
+ mAudioStreams = defaultAudioStreams();
+ }
+ if ((mBuilderFieldsSet & 0x400) == 0) {
mExtras = defaultExtras();
}
HotwordDetectedResult o = new HotwordDetectedResult(
@@ -918,12 +998,13 @@ public final class HotwordDetectedResult implements Parcelable {
mScore,
mPersonalizedScore,
mHotwordPhraseId,
+ mAudioStreams,
mExtras);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x400) != 0) {
+ if ((mBuilderFieldsSet & 0x800) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -931,10 +1012,10 @@ public final class HotwordDetectedResult implements Parcelable {
}
@DataClass.Generated(
- time = 1658357814396L,
+ time = 1668466781144L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java",
- inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> mAudioStreams\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static java.util.List<android.service.voice.HotwordAudioStream> defaultAudioStreams()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> getAudioStreams()\npublic android.service.voice.HotwordDetectedResult.Builder buildUpon()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 5e86f2b27035..2175859d9de6 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -578,6 +578,7 @@ public abstract class WallpaperService extends Service {
*/
public void reportEngineShown(boolean waitForEngineShown) {
if (mIWallpaperEngine.mShownReported) return;
+ Trace.beginSection("WPMS.reportEngineShown-" + waitForEngineShown);
Log.d(TAG, "reportEngineShown: shouldWait=" + waitForEngineShown);
if (!waitForEngineShown) {
Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN);
@@ -590,6 +591,7 @@ public abstract class WallpaperService extends Service {
mCaller.sendMessageDelayed(message, TimeUnit.SECONDS.toMillis(5));
}
}
+ Trace.endSection();
}
/**
@@ -1259,7 +1261,9 @@ public abstract class WallpaperService extends Service {
didSurface = true;
if (DEBUG) Log.v(TAG, "onSurfaceCreated("
+ mSurfaceHolder + "): " + this);
+ Trace.beginSection("WPMS.Engine.onSurfaceCreated");
onSurfaceCreated(mSurfaceHolder);
+ Trace.endSection();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
@@ -1285,8 +1289,10 @@ public abstract class WallpaperService extends Service {
+ ", " + mCurWidth + ", " + mCurHeight
+ "): " + this);
didSurface = true;
+ Trace.beginSection("WPMS.Engine.onSurfaceChanged");
onSurfaceChanged(mSurfaceHolder, mFormat,
mCurWidth, mCurHeight);
+ Trace.endSection();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
@@ -1303,11 +1309,15 @@ public abstract class WallpaperService extends Service {
if (DEBUG) {
Log.v(TAG, "dispatching insets=" + windowInsets);
}
+ Trace.beginSection("WPMS.Engine.onApplyWindowInsets");
onApplyWindowInsets(windowInsets);
+ Trace.endSection();
}
if (redrawNeeded) {
+ Trace.beginSection("WPMS.Engine.onSurfaceRedrawNeeded");
onSurfaceRedrawNeeded(mSurfaceHolder);
+ Trace.endSection();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
@@ -1332,11 +1342,15 @@ public abstract class WallpaperService extends Service {
// the state to get them to notice.
if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
+ this);
+ Trace.beginSection("WPMS.Engine.onVisibilityChanged-true");
onVisibilityChanged(true);
+ Trace.endSection();
}
if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
+ this);
+ Trace.beginSection("WPMS.Engine.onVisibilityChanged-false");
onVisibilityChanged(false);
+ Trace.endSection();
}
} finally {
mIsCreating = false;
@@ -1422,12 +1436,16 @@ public abstract class WallpaperService extends Service {
mDisplayInstallOrientation = mDisplay.getInstallOrientation();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
+ Trace.beginSection("WPMS.Engine.onCreate");
onCreate(mSurfaceHolder);
+ Trace.endSection();
mInitializing = false;
mReportedVisible = false;
+ Trace.beginSection("WPMS.Engine.updateSurface");
updateSurface(false, false, false);
+ Trace.endSection();
}
/**
@@ -2237,14 +2255,15 @@ public abstract class WallpaperService extends Service {
public void reportShown() {
if (!mShownReported) {
mShownReported = true;
+ Trace.beginSection("WPMS.mConnection.engineShown");
try {
mConnection.engineShown(this);
Log.d(TAG, "Wallpaper has updated the surface:"
+ mWallpaperManager.getWallpaperInfo());
} catch (RemoteException e) {
Log.w(TAG, "Wallpaper host disappeared", e);
- return;
}
+ Trace.endSection();
}
}
@@ -2286,6 +2305,27 @@ public abstract class WallpaperService extends Service {
return mEngine == null ? null : SurfaceControl.mirrorSurface(mEngine.mSurfaceControl);
}
+ private void doAttachEngine() {
+ Trace.beginSection("WPMS.onCreateEngine");
+ Engine engine = onCreateEngine();
+ Trace.endSection();
+ mEngine = engine;
+ Trace.beginSection("WPMS.mConnection.attachEngine-" + mDisplayId);
+ try {
+ mConnection.attachEngine(this, mDisplayId);
+ } catch (RemoteException e) {
+ engine.detach();
+ Log.w(TAG, "Wallpaper host disappeared", e);
+ return;
+ } finally {
+ Trace.endSection();
+ }
+ mActiveEngines.add(engine);
+ Trace.beginSection("WPMS.engine.attach");
+ engine.attach(this);
+ Trace.endSection();
+ }
+
private void doDetachEngine() {
mActiveEngines.remove(mEngine);
mEngine.detach();
@@ -2311,21 +2351,15 @@ public abstract class WallpaperService extends Service {
}
switch (message.what) {
case DO_ATTACH: {
- Engine engine = onCreateEngine();
- mEngine = engine;
- try {
- mConnection.attachEngine(this, mDisplayId);
- } catch (RemoteException e) {
- engine.detach();
- Log.w(TAG, "Wallpaper host disappeared", e);
- return;
- }
- mActiveEngines.add(engine);
- engine.attach(this);
+ Trace.beginSection("WPMS.DO_ATTACH");
+ doAttachEngine();
+ Trace.endSection();
return;
}
case DO_DETACH: {
+ Trace.beginSection("WPMS.DO_DETACH");
doDetachEngine();
+ Trace.endSection();
return;
}
case DO_SET_DESIRED_SIZE: {
@@ -2406,7 +2440,9 @@ public abstract class WallpaperService extends Service {
}
} break;
case MSG_REPORT_SHOWN: {
+ Trace.beginSection("WPMS.MSG_REPORT_SHOWN");
reportShown();
+ Trace.endSection();
} break;
default :
Log.w(TAG, "Unknown message type " + message.what);
@@ -2430,8 +2466,10 @@ public abstract class WallpaperService extends Service {
public void attach(IWallpaperConnection conn, IBinder windowToken,
int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
int displayId, @SetWallpaperFlags int which) {
+ Trace.beginSection("WPMS.ServiceWrapper.attach");
mEngineWrapper = new IWallpaperEngineWrapper(mTarget, conn, windowToken,
windowType, isPreview, reqWidth, reqHeight, padding, displayId);
+ Trace.endSection();
}
@Override
@@ -2442,16 +2480,20 @@ public abstract class WallpaperService extends Service {
@Override
public void onCreate() {
+ Trace.beginSection("WPMS.onCreate");
super.onCreate();
+ Trace.endSection();
}
@Override
public void onDestroy() {
+ Trace.beginSection("WPMS.onDestroy");
super.onDestroy();
for (int i=0; i<mActiveEngines.size(); i++) {
mActiveEngines.get(i).detach();
}
mActiveEngines.clear();
+ Trace.endSection();
}
/**
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index cfad1afe1b5b..fbf8d8bf9fe4 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -467,6 +467,23 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Sets whether a container is being drag-resized.
+ * When {@code true}, the client will reuse a single (larger) surface size to avoid
+ * continuous allocations on every size change.
+ *
+ * @param container WindowContainerToken of the task that changed its drag resizing state
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setDragResizing(@NonNull WindowContainerToken container,
+ boolean dragResizing) {
+ final Change change = getOrCreateChange(container.asBinder());
+ change.mChangeMask |= Change.CHANGE_DRAG_RESIZING;
+ change.mDragResizing = dragResizing;
+ return this;
+ }
+
+ /**
* Sends a pending intent in sync.
* @param sender The PendingIntent sender.
* @param intent The fillIn intent to patch over the sender's base intent.
@@ -906,12 +923,14 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
public static final int CHANGE_FORCE_NO_PIP = 1 << 6;
public static final int CHANGE_FORCE_TRANSLUCENT = 1 << 7;
+ public static final int CHANGE_DRAG_RESIZING = 1 << 8;
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
private boolean mHidden = false;
private boolean mIgnoreOrientationRequest = false;
private boolean mForceTranslucent = false;
+ private boolean mDragResizing = false;
private int mChangeMask = 0;
private @ActivityInfo.Config int mConfigSetMask = 0;
@@ -932,6 +951,7 @@ public final class WindowContainerTransaction implements Parcelable {
mHidden = in.readBoolean();
mIgnoreOrientationRequest = in.readBoolean();
mForceTranslucent = in.readBoolean();
+ mDragResizing = in.readBoolean();
mChangeMask = in.readInt();
mConfigSetMask = in.readInt();
mWindowSetMask = in.readInt();
@@ -980,6 +1000,9 @@ public final class WindowContainerTransaction implements Parcelable {
if ((other.mChangeMask & CHANGE_FORCE_TRANSLUCENT) != 0) {
mForceTranslucent = other.mForceTranslucent;
}
+ if ((other.mChangeMask & CHANGE_DRAG_RESIZING) != 0) {
+ mDragResizing = other.mDragResizing;
+ }
mChangeMask |= other.mChangeMask;
if (other.mActivityWindowingMode >= 0) {
mActivityWindowingMode = other.mActivityWindowingMode;
@@ -1039,6 +1062,15 @@ public final class WindowContainerTransaction implements Parcelable {
return mForceTranslucent;
}
+ /** Gets the requested drag resizing state. */
+ public boolean getDragResizing() {
+ if ((mChangeMask & CHANGE_DRAG_RESIZING) == 0) {
+ throw new RuntimeException("Drag resizing not set. "
+ + "Check CHANGE_DRAG_RESIZING first");
+ }
+ return mDragResizing;
+ }
+
public int getChangeMask() {
return mChangeMask;
}
@@ -1100,6 +1132,9 @@ public final class WindowContainerTransaction implements Parcelable {
if ((mChangeMask & CHANGE_FOCUSABLE) != 0) {
sb.append("focusable:" + mFocusable + ",");
}
+ if ((mChangeMask & CHANGE_DRAG_RESIZING) != 0) {
+ sb.append("dragResizing:" + mDragResizing + ",");
+ }
if (mBoundsChangeTransaction != null) {
sb.append("hasBoundsTransaction,");
}
@@ -1117,6 +1152,7 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeBoolean(mHidden);
dest.writeBoolean(mIgnoreOrientationRequest);
dest.writeBoolean(mForceTranslucent);
+ dest.writeBoolean(mDragResizing);
dest.writeInt(mChangeMask);
dest.writeInt(mConfigSetMask);
dest.writeInt(mWindowSetMask);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f5ba275b604b..2676396cb997 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2096,7 +2096,8 @@ public class ChooserActivity extends ResolverActivity implements
return matchingShortcuts;
}
- private void sendShortcutManagerShareTargetResults(
+ @VisibleForTesting
+ protected void sendShortcutManagerShareTargetResults(
int shortcutType, ServiceResultInfo[] results) {
final Message msg = Message.obtain();
msg.what = ChooserHandler.SHORTCUT_MANAGER_ALL_SHARE_TARGET_RESULTS;
@@ -3873,7 +3874,11 @@ public class ChooserActivity extends ResolverActivity implements
}
}
- static class ServiceResultInfo {
+ /**
+ * Shortcuts grouped by application.
+ */
+ @VisibleForTesting
+ public static class ServiceResultInfo {
public final DisplayResolveInfo originalTarget;
public final List<ChooserTarget> resultTargets;
public final UserHandle userHandle;
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index a05062b43d60..39b9e217fce7 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -27,6 +27,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
@@ -225,6 +226,7 @@ public class InteractionJankMonitor {
public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = 63;
public static final int CUJ_LOCKSCREEN_OCCLUSION = 64;
public static final int CUJ_RECENTS_SCROLLING = 65;
+ public static final int CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS = 66;
private static final int NO_STATSD_LOGGING = -1;
@@ -299,6 +301,7 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS,
};
private static volatile InteractionJankMonitor sInstance;
@@ -384,7 +387,8 @@ public class InteractionJankMonitor {
CUJ_SHADE_CLEAR_ALL,
CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
CUJ_LOCKSCREEN_OCCLUSION,
- CUJ_RECENTS_SCROLLING
+ CUJ_RECENTS_SCROLLING,
+ CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -899,6 +903,8 @@ public class InteractionJankMonitor {
return "LOCKSCREEN_OCCLUSION";
case CUJ_RECENTS_SCROLLING:
return "RECENTS_SCROLLING";
+ case CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS:
+ return "LAUNCHER_APP_SWIPE_TO_RECENTS";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index a352063aa6ee..f998a690f5a0 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -2402,7 +2402,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
return;
}
final ThreadedRenderer renderer = getThreadedRenderer();
- if (renderer != null) {
+ if (renderer != null && !CAPTION_ON_SHELL) {
loadBackgroundDrawablesIfNeeded();
WindowInsets rootInsets = getRootWindowInsets();
mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 07e05c6a830e..2a9e60a46972 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1914,6 +1914,10 @@
-->
<string name="config_defaultCaptivePortalLoginPackageName" translatable="false">com.android.captiveportallogin</string>
+ <!-- The package name of the dock manager app. Must be granted the
+ POST_NOTIFICATIONS permission. -->
+ <string name="config_defaultDockManagerPackageName" translatable="false"></string>
+
<!-- Whether to enable geocoder overlay which allows geocoder to be replaced
by an app at run-time. When disabled, only the
config_geocoderProviderPackageName package will be searched for
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8e7da4a5eac3..79c7042adce1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3466,6 +3466,9 @@
<!-- Captive Portal Login -->
<java-symbol type="string" name="config_defaultCaptivePortalLoginPackageName" />
+ <!-- Dock Manager -->
+ <java-symbol type="string" name="config_defaultDockManagerPackageName" />
+
<!-- Optional IPsec algorithms -->
<java-symbol type="array" name="config_optionalIpSecAlgorithms" />
diff --git a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
index 98485c024a59..ee73f00d9d46 100644
--- a/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
+++ b/core/tests/coretests/src/android/content/pm/ConstrainDisplayApisConfigTest.java
@@ -29,10 +29,11 @@ import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
/**
- * Test class for {@link ConstrainDisplayApisConfig}.
+ * Test for {@link ConstrainDisplayApisConfig}.
*
* Build/Install/Run:
* atest FrameworksCoreTests:ConstrainDisplayApisConfigTest
@@ -72,6 +73,7 @@ public final class ConstrainDisplayApisConfigTest {
testNeverConstrainDisplayApis("com.android.test", /* version= */ 1, /* expected= */ false);
}
+ @Ignore("b/257375674")
@Test
public void neverConstrainDisplayApis_flagsHasSingleEntry_returnsTrueForPackageWithinRange() {
setNeverConstrainDisplayApisFlag("com.android.test:1:1");
@@ -107,6 +109,7 @@ public final class ConstrainDisplayApisConfigTest {
testNeverConstrainDisplayApis("com.android.test4", /* version= */ 9, /* expected= */ false);
}
+ @Ignore("b/257375674")
@Test
public void neverConstrainDisplayApis_flagHasInvalidEntries_ignoresInvalidEntries() {
// We add a valid entry before and after the invalid ones to make sure they are applied.
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
index 7ebebc965d8d..c59a3f518da8 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -246,10 +246,12 @@ public class VibratorTest {
@Test
public void getQFactorAndResonantFrequency_differentValues_returnsNaN() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setQFactor(1f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
.build();
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setQFactor(2f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 2, 2, null))
.build();
@@ -258,6 +260,7 @@ public class VibratorTest {
assertTrue(Float.isNaN(info.getQFactor()));
assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
+ assertEmptyFrequencyProfileAndControl(info);
// One vibrator with values undefined.
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3).build();
@@ -266,16 +269,19 @@ public class VibratorTest {
assertTrue(Float.isNaN(info.getQFactor()));
assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
+ assertEmptyFrequencyProfileAndControl(info);
}
@Test
public void getQFactorAndResonantFrequency_sameValues_returnsValue() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setQFactor(10f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 11, 10, 0.5f, null))
.build();
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setQFactor(10f)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 11, 5, 1, null))
@@ -285,113 +291,131 @@ public class VibratorTest {
assertEquals(10f, info.getQFactor(), TEST_TOLERANCE);
assertEquals(11f, info.getResonantFrequencyHz(), TEST_TOLERANCE);
+
+ // No frequency range defined.
+ assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
}
@Test
public void getFrequencyProfile_noVibrator_returnsEmpty() {
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
- assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEmptyFrequencyProfileAndControl(info);
}
@Test
public void getFrequencyProfile_differentResonantFrequencyOrResolutionValues_returnsEmpty() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo differentResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, differentResonantFrequency});
- assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEmptyFrequencyProfileAndControl(info);
VibratorInfo differentFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 2,
new float[] { 0, 1 }))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, differentFrequencyResolution});
- assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEmptyFrequencyProfileAndControl(info);
}
@Test
public void getFrequencyProfile_missingValues_returnsEmpty() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo missingResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(Float.NaN, 1, 1,
new float[] { 0, 1 }))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingResonantFrequency});
- assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEmptyFrequencyProfileAndControl(info);
VibratorInfo missingMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, Float.NaN, 1,
new float[] { 0, 1 }))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingMinFrequency});
- assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEmptyFrequencyProfileAndControl(info);
VibratorInfo missingFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, Float.NaN,
new float[] { 0, 1 }))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingFrequencyResolution});
- assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEmptyFrequencyProfileAndControl(info);
VibratorInfo missingMaxAmplitudes = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
.build();
info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, missingMaxAmplitudes});
- assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEmptyFrequencyProfileAndControl(info);
}
@Test
public void getFrequencyProfile_unalignedMaxAmplitudes_returnsEmpty() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
new float[] { 0, 1, 1, 0 }))
.build();
VibratorInfo unalignedMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.1f, 0.5f,
new float[] { 0, 1, 1, 0 }))
.build();
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
new float[] { 0, 1, 1, 0 }))
.build();
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, unalignedMinFrequency, thirdVibrator});
- assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEmptyFrequencyProfileAndControl(info);
}
@Test
public void getFrequencyProfile_alignedProfiles_returnsIntersection() {
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
new float[] { 0.5f, 1, 1, 0.5f }))
.build();
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
new float[] { 1, 1, 1 }))
.build();
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
new float[] { 0.8f, 1, 0.8f, 0.5f }))
.build();
@@ -401,6 +425,20 @@ public class VibratorTest {
assertEquals(
new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
info.getFrequencyProfile());
+ assertEquals(true, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
+
+ // Third vibrator without frequency control capability.
+ thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
+ new float[] { 0.8f, 1, 0.8f, 0.5f }))
+ .build();
+ info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, secondVibrator, thirdVibrator});
+
+ assertEquals(
+ new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
+ info.getFrequencyProfile());
+ assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
}
@Test
@@ -547,4 +585,12 @@ public class VibratorTest {
VibrationAttributes vibrationAttributes = captor.getValue();
assertEquals(new VibrationAttributes.Builder().build(), vibrationAttributes);
}
+
+ /**
+ * Asserts that the frequency profile is empty, and therefore frequency control isn't supported.
+ */
+ void assertEmptyFrequencyProfileAndControl(VibratorInfo info) {
+ assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
index 499f7a55996b..875cd0bb4e16 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityOverrideData.java
@@ -24,11 +24,13 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.UserHandle;
+import android.util.Pair;
import com.android.internal.app.chooser.TargetInfo;
import com.android.internal.logging.MetricsLogger;
import java.util.List;
+import java.util.function.BiFunction;
import java.util.function.Function;
/**
@@ -50,6 +52,9 @@ public class ChooserActivityOverrideData {
public Function<PackageManager, PackageManager> createPackageManager;
public Function<TargetInfo, Boolean> onSafelyStartCallback;
public Function<ChooserListAdapter, Void> onQueryDirectShareTargets;
+ public BiFunction<
+ IChooserWrapper, ChooserListAdapter, Pair<Integer, ChooserActivity.ServiceResultInfo[]>>
+ directShareTargets;
public ResolverListController resolverListController;
public ResolverListController workResolverListController;
public Boolean isVoiceInteraction;
@@ -72,6 +77,7 @@ public class ChooserActivityOverrideData {
public void reset() {
onSafelyStartCallback = null;
onQueryDirectShareTargets = null;
+ directShareTargets = null;
isVoiceInteraction = null;
createPackageManager = null;
previewThumbnail = null;
@@ -112,4 +118,3 @@ public class ChooserActivityOverrideData {
private ChooserActivityOverrideData() {}
}
-
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index e8c7ce0b312b..d656678c16ee 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -81,6 +81,7 @@ import android.net.Uri;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.service.chooser.ChooserTarget;
+import android.util.Pair;
import android.view.View;
import androidx.annotation.CallSuper;
@@ -89,6 +90,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import com.android.internal.R;
+import com.android.internal.app.ChooserActivity.ServiceResultInfo;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -2166,8 +2168,8 @@ public class ChooserActivityTest {
assertThat(logger.numCalls(), is(6));
}
- @Test @Ignore
- public void testDirectTargetLogging() throws InterruptedException {
+ @Test
+ public void testDirectTargetLogging() {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -2187,30 +2189,35 @@ public class ChooserActivityTest {
resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
+ ChooserActivityOverrideData
+ .getInstance()
+ .directShareTargets = (activity, adapter) -> {
+ DisplayResolveInfo displayInfo = activity.createTestDisplayResolveInfo(
+ sendIntent,
+ ri,
+ "testLabel",
+ "testInfo",
+ sendIntent,
+ /* resolveInfoPresentationGetter */ null);
+ ServiceResultInfo[] results = {
+ new ServiceResultInfo(
+ displayInfo,
+ serviceTargets,
+ adapter.getUserHandle())};
+ // TODO: consider covering the other type.
+ // Only 2 types are expected out of the shortcut loading logic:
+ // - TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER, if shortcuts were loaded from
+ // the ShortcutManager, and;
+ // - TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE, if shortcuts were loaded
+ // from AppPredictor.
+ // Ideally, our tests should cover all of them.
+ return new Pair<>(TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER, results);
+ };
+
// Start activity
final IChooserWrapper activity = (IChooserWrapper)
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
- // Insert the direct share target
- Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
- directShareToShortcutInfos.put(serviceTargets.get(0), null);
- InstrumentationRegistry.getInstrumentation().runOnMainSync(
- () -> activity.getAdapter().addServiceResults(
- activity.createTestDisplayResolveInfo(sendIntent,
- ri,
- "testLabel",
- "testInfo",
- sendIntent,
- /* resolveInfoPresentationGetter */ null),
- serviceTargets,
- TARGET_TYPE_CHOOSER_TARGET,
- directShareToShortcutInfos)
- );
- // Thread.sleep shouldn't be a thing in an integration test but it's
- // necessary here because of the way the code is structured
- // TODO: restructure the tests b/129870719
- Thread.sleep(((ChooserActivity) activity).mListViewUpdateDelayMs);
-
assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
activity.getAdapter().getCount(), is(3));
assertThat("Chooser should have exactly one selectable direct target",
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 7f8598217ec6..4c3235ca8ac0 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -31,6 +31,7 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.UserHandle;
+import android.util.Pair;
import android.util.Size;
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
@@ -239,6 +240,12 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
@Override
protected void queryDirectShareTargets(ChooserListAdapter adapter,
boolean skipAppPredictionService) {
+ if (sOverrides.directShareTargets != null) {
+ Pair<Integer, ServiceResultInfo[]> result =
+ sOverrides.directShareTargets.apply(this, adapter);
+ sendShortcutManagerShareTargetResults(result.first, result.second);
+ return;
+ }
if (sOverrides.onQueryDirectShareTargets != null) {
sOverrides.onQueryDirectShareTargets.apply(adapter);
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index f047905f18b6..a4a38c713a66 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1327,6 +1327,12 @@
"group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-787664727": {
+ "message": "Cannot launch dream activity due to invalid state. dream component: %s packageName: %s",
+ "level": "ERROR",
+ "group": "WM_DEBUG_DREAM",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+ },
"-784959154": {
"message": "Attempted to add private presentation window to a non-private display. Aborting.",
"level": "WARN",
@@ -2863,6 +2869,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "601283564": {
+ "message": "Dream packageName does not match active dream. Package %s does not match %s",
+ "level": "ERROR",
+ "group": "WM_DEBUG_DREAM",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+ },
"608694300": {
"message": " NEW SURFACE SESSION %s",
"level": "INFO",
@@ -3103,12 +3115,6 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/WindowStateAnimator.java"
},
- "829869827": {
- "message": "Cannot launch dream activity due to invalid state. dreaming: %b packageName: %s",
- "level": "ERROR",
- "group": "WM_DEBUG_DREAM",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"835814848": {
"message": "%s",
"level": "INFO",
@@ -4141,12 +4147,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
- "1918771553": {
- "message": "Dream packageName does not match active dream. Package %s does not match %s or %s",
- "level": "ERROR",
- "group": "WM_DEBUG_DREAM",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"1921821199": {
"message": "Preserving %s until the new one is added",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index abf7e9911086..42c892a240b6 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1667,6 +1667,9 @@ public class Canvas extends BaseCanvas {
* effectively treating them as zeros. In API level {@value Build.VERSION_CODES#P} and above
* these parameters will be respected.
*
+ * <p>Note: antialiasing is not supported, therefore {@link Paint#ANTI_ALIAS_FLAG} is
+ * ignored.</p>
+ *
* @param bitmap The bitmap to draw using the mesh
* @param meshWidth The number of columns in the mesh. Nothing is drawn if this is 0
* @param meshHeight The number of rows in the mesh. Nothing is drawn if this is 0
@@ -1678,7 +1681,7 @@ public class Canvas extends BaseCanvas {
* null, there must be at least (meshWidth+1) * (meshHeight+1) + colorOffset values
* in the array.
* @param colorOffset Number of color elements to skip before drawing
- * @param paint May be null. The paint used to draw the bitmap
+ * @param paint May be null. The paint used to draw the bitmap. Antialiasing is not supported.
*/
public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,
@NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,
@@ -1832,9 +1835,12 @@ public class Canvas extends BaseCanvas {
/**
* Draws the specified bitmap as an N-patch (most often, a 9-patch.)
*
+ * <p>Note: antialiasing is not supported, therefore {@link Paint#ANTI_ALIAS_FLAG} is
+ * ignored.</p>
+ *
* @param patch The ninepatch object to render
* @param dst The destination rectangle.
- * @param paint The paint to draw the bitmap with. may be null
+ * @param paint The paint to draw the bitmap with. May be null. Antialiasing is not supported.
*/
public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
super.drawPatch(patch, dst, paint);
@@ -1843,9 +1849,12 @@ public class Canvas extends BaseCanvas {
/**
* Draws the specified bitmap as an N-patch (most often, a 9-patch.)
*
+ * <p>Note: antialiasing is not supported, therefore {@link Paint#ANTI_ALIAS_FLAG} is
+ * ignored.</p>
+ *
* @param patch The ninepatch object to render
* @param dst The destination rectangle.
- * @param paint The paint to draw the bitmap with. may be null
+ * @param paint The paint to draw the bitmap with. May be null. Antialiasing is not supported.
*/
public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
super.drawPatch(patch, dst, paint);
@@ -2278,6 +2287,9 @@ public class Canvas extends BaseCanvas {
* array is optional, but if it is present, then it is used to specify the index of each
* triangle, rather than just walking through the arrays in order.
*
+ * <p>Note: antialiasing is not supported, therefore {@link Paint#ANTI_ALIAS_FLAG} is
+ * ignored.</p>
+ *
* @param mode How to interpret the array of vertices
* @param vertexCount The number of values in the vertices array (and corresponding texs and
* colors arrays if non-null). Each logical vertex is two values (x, y), vertexCount
@@ -2292,8 +2304,9 @@ public class Canvas extends BaseCanvas {
* @param colorOffset Number of values in colors to skip before drawing.
* @param indices If not null, array of indices to reference into the vertex (texs, colors)
* array.
- * @param indexCount number of entries in the indices array (if not null).
- * @param paint Specifies the shader to use if the texs array is non-null.
+ * @param indexCount Number of entries in the indices array (if not null).
+ * @param paint Specifies the shader to use if the texs array is non-null. Antialiasing is not
+ * supported.
*/
public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,
int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 451b99ea7550..f438a03b1434 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -137,6 +137,13 @@ public class Paint {
* <p>Enabling this flag will cause all draw operations that support
* antialiasing to use it.</p>
*
+ * <p>Notable draw operations that do <b>not</b> support antialiasing include:</p>
+ * <ul>
+ * <li>{@link android.graphics.Canvas#drawBitmapMesh}</li>
+ * <li>{@link android.graphics.Canvas#drawPatch}</li>
+ * <li>{@link android.graphics.Canvas#drawVertices}</li>
+ * </ul>
+ *
* @see #Paint(int)
* @see #setFlags(int)
*/
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index df5f921f3a62..c6197c8a730b 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -111,4 +111,8 @@
<!-- Whether to dim a split-screen task when the other is the IME target -->
<bool name="config_dimNonImeAttachedSide">true</bool>
+
+ <!-- Components support to launch multiple instances into split-screen -->
+ <string-array name="config_componentsSupportMultiInstancesSplit">
+ </string-array>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 43679364b443..e58e785850fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -16,14 +16,12 @@
package com.android.wm.shell;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -48,7 +46,6 @@ import android.window.StartingWindowInfo;
import android.window.StartingWindowRemovalInfo;
import android.window.TaskAppearedInfo;
import android.window.TaskOrganizer;
-import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -567,6 +564,22 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
}
+ /**
+ * Return list of {@link RunningTaskInfo}s for the given display.
+ *
+ * @return filtered list of tasks or empty list
+ */
+ public ArrayList<RunningTaskInfo> getRunningTasks(int displayId) {
+ ArrayList<RunningTaskInfo> result = new ArrayList<>();
+ for (int i = 0; i < mTasks.size(); i++) {
+ RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo();
+ if (taskInfo.displayId == displayId) {
+ result.add(taskInfo);
+ }
+ }
+ return result;
+ }
+
/** Gets running task by taskId. Returns {@code null} if no such task observed. */
@Nullable
public RunningTaskInfo getRunningTaskInfo(int taskId) {
@@ -693,57 +706,6 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
taskListener.reparentChildSurfaceToTask(taskId, sc, t);
}
- /**
- * Create a {@link WindowContainerTransaction} to clear task bounds.
- *
- * Only affects tasks that have {@link RunningTaskInfo#getActivityType()} set to
- * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
- *
- * @param displayId display id for tasks that will have bounds cleared
- * @return {@link WindowContainerTransaction} with pending operations to clear bounds
- */
- public WindowContainerTransaction prepareClearBoundsForStandardTasks(int displayId) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks: displayId=%d", displayId);
- WindowContainerTransaction wct = new WindowContainerTransaction();
- for (int i = 0; i < mTasks.size(); i++) {
- RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo();
- if ((taskInfo.displayId == displayId) && (taskInfo.getActivityType()
- == WindowConfiguration.ACTIVITY_TYPE_STANDARD)) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s",
- taskInfo.token, taskInfo);
- wct.setBounds(taskInfo.token, null);
- }
- }
- return wct;
- }
-
- /**
- * Create a {@link WindowContainerTransaction} to clear task level freeform setting.
- *
- * Only affects tasks that have {@link RunningTaskInfo#getActivityType()} set to
- * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
- *
- * @param displayId display id for tasks that will have windowing mode reset to {@link
- * WindowConfiguration#WINDOWING_MODE_UNDEFINED}
- * @return {@link WindowContainerTransaction} with pending operations to clear windowing mode
- */
- public WindowContainerTransaction prepareClearFreeformForStandardTasks(int displayId) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks: displayId=%d", displayId);
- WindowContainerTransaction wct = new WindowContainerTransaction();
- for (int i = 0; i < mTasks.size(); i++) {
- RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo();
- if (taskInfo.displayId == displayId
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
- && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE,
- "clearing windowing mode for token=%s taskInfo=%s", taskInfo.token,
- taskInfo);
- wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
- }
- }
- return wct;
- }
-
private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
int event) {
ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/back/OWNERS
new file mode 100644
index 000000000000..1e0f9bc6322f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/OWNERS
@@ -0,0 +1,5 @@
+# WM shell sub-module back navigation owners
+# Bug component: 1152663
+shanh@google.com
+arthurhung@google.com
+wilsonshih@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 8bc16bcc9d9d..1474754fc7f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -46,8 +46,10 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
/**
* Divider for multi window splits.
@@ -364,8 +366,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
mViewHost.relayout(lp);
}
- void setInteractive(boolean interactive) {
+ void setInteractive(boolean interactive, String from) {
if (interactive == mInteractive) return;
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "Set divider bar %s from %s", interactive ? "interactive" : "non-interactive",
+ from);
mInteractive = interactive;
releaseTouching();
mHandle.setVisibility(mInteractive ? View.VISIBLE : View.INVISIBLE);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 74f8bf9ac863..5b7ed278e843 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -48,6 +48,7 @@ import androidx.annotation.NonNull;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.SurfaceUtils;
import java.util.function.Consumer;
@@ -74,10 +75,14 @@ public class SplitDecorManager extends WindowlessWindowManager {
private boolean mShown;
private boolean mIsResizing;
- private Rect mBounds = new Rect();
+ private final Rect mBounds = new Rect();
+ private final Rect mResizingBounds = new Rect();
+ private final Rect mTempRect = new Rect();
private ValueAnimator mFadeAnimator;
private int mIconSize;
+ private int mOffsetX;
+ private int mOffsetY;
public SplitDecorManager(Configuration configuration, IconProvider iconProvider,
SurfaceSession surfaceSession) {
@@ -158,7 +163,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
/** Showing resizing hint. */
public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
- Rect sideBounds, SurfaceControl.Transaction t) {
+ Rect sideBounds, SurfaceControl.Transaction t, int offsetX, int offsetY) {
if (mResizingIconView == null) {
return;
}
@@ -167,6 +172,9 @@ public class SplitDecorManager extends WindowlessWindowManager {
mIsResizing = true;
mBounds.set(newBounds);
}
+ mResizingBounds.set(newBounds);
+ mOffsetX = offsetX;
+ mOffsetY = offsetY;
final boolean show =
newBounds.width() > mBounds.width() || newBounds.height() > mBounds.height();
@@ -221,11 +229,37 @@ public class SplitDecorManager extends WindowlessWindowManager {
/** Stops showing resizing hint. */
public void onResized(SurfaceControl.Transaction t) {
+ if (!mShown && mIsResizing) {
+ mTempRect.set(mResizingBounds);
+ mTempRect.offsetTo(-mOffsetX, -mOffsetY);
+ final SurfaceControl screenshot = ScreenshotUtils.takeScreenshot(t,
+ mHostLeash, mTempRect, Integer.MAX_VALUE - 1);
+
+ final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
+ final ValueAnimator va = ValueAnimator.ofFloat(1, 0);
+ va.addUpdateListener(valueAnimator -> {
+ final float progress = (float) valueAnimator.getAnimatedValue();
+ animT.setAlpha(screenshot, progress);
+ animT.apply();
+ });
+ va.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(@androidx.annotation.NonNull Animator animation) {
+ animT.remove(screenshot);
+ animT.apply();
+ animT.close();
+ }
+ });
+ va.start();
+ }
+
if (mResizingIconView == null) {
return;
}
mIsResizing = false;
+ mOffsetX = 0;
+ mOffsetY = 0;
if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
if (!mShown) {
// If fade-out animation is running, just add release callback to it.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 295a2e3c4244..3de1045bfbda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -448,7 +448,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
*/
void updateDivideBounds(int position) {
updateBounds(position);
- mSplitLayoutHandler.onLayoutSizeChanging(this);
+ mSplitLayoutHandler.onLayoutSizeChanging(this, mSurfaceEffectPolicy.mParallaxOffset.x,
+ mSurfaceEffectPolicy.mParallaxOffset.y);
}
void setDividePosition(int position, boolean applyLayoutChange) {
@@ -600,7 +601,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
animator.start();
}
- /** Swich both surface position with animation. */
+ /** Switch both surface position with animation. */
public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1,
SurfaceControl leash2, Consumer<Rect> finishCallback) {
final boolean isLandscape = isLandscape();
@@ -811,7 +812,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
* @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
* SurfaceControl, SurfaceControl, boolean)
*/
- void onLayoutSizeChanging(SplitLayout layout);
+ void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY);
/**
* Calls when finish resizing the split bounds.
@@ -1092,7 +1093,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
// ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
// because DividerView won't receive onImeVisibilityChanged callback after it being
// re-inflated.
- mSplitWindowManager.setInteractive(!mImeShown || !mHasImeFocus);
+ mSplitWindowManager.setInteractive(!mImeShown || !mHasImeFocus,
+ "onImeStartPositioning");
return needOffset ? IME_ANIMATION_NO_ALPHA : 0;
}
@@ -1118,7 +1120,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
// Restore the split layout when wm-shell is not controlling IME insets anymore.
if (!controlling && mImeShown) {
reset();
- mSplitWindowManager.setInteractive(true);
+ mSplitWindowManager.setInteractive(true, "onImeControlTargetChanged");
mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this);
mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 864b9a7528b0..060ac56cae96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -166,9 +166,9 @@ public final class SplitWindowManager extends WindowlessWindowManager {
}
}
- void setInteractive(boolean interactive) {
+ void setInteractive(boolean interactive, String from) {
if (mDividerView == null) return;
- mDividerView.setInteractive(interactive);
+ mDividerView.setInteractive(interactive, from);
}
View getDividerView() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index 34ff6d814c8d..abc4024bc290 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -16,8 +16,11 @@
package com.android.wm.shell.desktopmode;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -151,21 +154,18 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
int displayId = mContext.getDisplayId();
+ ArrayList<RunningTaskInfo> runningTasks = mShellTaskOrganizer.getRunningTasks(displayId);
+
WindowContainerTransaction wct = new WindowContainerTransaction();
- // Reset freeform windowing mode that is set per task level (tasks should inherit
- // container value)
- wct.merge(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(displayId),
- true /* transfer */);
- int targetWindowingMode;
+ // Reset freeform windowing mode that is set per task level so tasks inherit it
+ clearFreeformForStandardTasks(runningTasks, wct);
if (active) {
- targetWindowingMode = WINDOWING_MODE_FREEFORM;
+ moveHomeBehindVisibleTasks(runningTasks, wct);
+ setDisplayAreaWindowingMode(displayId, WINDOWING_MODE_FREEFORM, wct);
} else {
- targetWindowingMode = WINDOWING_MODE_FULLSCREEN;
- // Clear any resized bounds
- wct.merge(mShellTaskOrganizer.prepareClearBoundsForStandardTasks(displayId),
- true /* transfer */);
+ clearBoundsForStandardTasks(runningTasks, wct);
+ setDisplayAreaWindowingMode(displayId, WINDOWING_MODE_FULLSCREEN, wct);
}
- prepareWindowingModeChange(wct, displayId, targetWindowingMode);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mTransitions.startTransition(TRANSIT_CHANGE, wct, null);
} else {
@@ -173,17 +173,69 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
}
}
- private void prepareWindowingModeChange(WindowContainerTransaction wct,
- int displayId, @WindowConfiguration.WindowingMode int windowingMode) {
- DisplayAreaInfo displayAreaInfo = mRootTaskDisplayAreaOrganizer
- .getDisplayAreaInfo(displayId);
+ private WindowContainerTransaction clearBoundsForStandardTasks(
+ ArrayList<RunningTaskInfo> runningTasks, WindowContainerTransaction wct) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks");
+ for (RunningTaskInfo taskInfo : runningTasks) {
+ if (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s",
+ taskInfo.token, taskInfo);
+ wct.setBounds(taskInfo.token, null);
+ }
+ }
+ return wct;
+ }
+
+ private void clearFreeformForStandardTasks(ArrayList<RunningTaskInfo> runningTasks,
+ WindowContainerTransaction wct) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks");
+ for (RunningTaskInfo taskInfo : runningTasks) {
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE,
+ "clearing windowing mode for token=%s taskInfo=%s", taskInfo.token,
+ taskInfo);
+ wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
+ }
+ }
+ }
+
+ private void moveHomeBehindVisibleTasks(ArrayList<RunningTaskInfo> runningTasks,
+ WindowContainerTransaction wct) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks");
+ RunningTaskInfo homeTask = null;
+ ArrayList<RunningTaskInfo> visibleTasks = new ArrayList<>();
+ for (RunningTaskInfo taskInfo : runningTasks) {
+ if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
+ homeTask = taskInfo;
+ } else if (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
+ && taskInfo.isVisible()) {
+ visibleTasks.add(taskInfo);
+ }
+ }
+ if (homeTask == null) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks: home task not found");
+ } else {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks: visible tasks %d",
+ visibleTasks.size());
+ wct.reorder(homeTask.getToken(), true /* onTop */);
+ for (RunningTaskInfo task : visibleTasks) {
+ wct.reorder(task.getToken(), true /* onTop */);
+ }
+ }
+ }
+
+ private void setDisplayAreaWindowingMode(int displayId,
+ @WindowConfiguration.WindowingMode int windowingMode, WindowContainerTransaction wct) {
+ DisplayAreaInfo displayAreaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(
+ displayId);
if (displayAreaInfo == null) {
ProtoLog.e(WM_SHELL_DESKTOP_MODE,
"unable to update windowing mode for display %d display not found", displayId);
return;
}
- ProtoLog.d(WM_SHELL_DESKTOP_MODE,
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE,
"setWindowingMode: displayId=%d current wmMode=%d new wmMode=%d", displayId,
displayAreaInfo.configuration.windowConfiguration.getWindowingMode(),
windowingMode);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index f4888fbb2bb9..168f6d79a390 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -98,9 +98,11 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
switch (change.getMode()) {
case WindowManager.TRANSIT_OPEN:
- case WindowManager.TRANSIT_TO_FRONT:
onOpenTransitionReady(change, startT, finishT);
break;
+ case WindowManager.TRANSIT_TO_FRONT:
+ onToFrontTransitionReady(change, startT, finishT);
+ break;
case WindowManager.TRANSIT_CLOSE: {
taskInfoList.add(change.getTaskInfo());
onCloseTransitionReady(change, startT, finishT);
@@ -138,6 +140,21 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
change.getTaskInfo(), startT, finishT);
}
+ private void onToFrontTransitionReady(
+ TransitionInfo.Change change,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ boolean exists = mWindowDecorViewModel.setupWindowDecorationForTransition(
+ change.getTaskInfo(),
+ startT,
+ finishT);
+ if (!exists) {
+ // Window caption does not exist, create it
+ mWindowDecorViewModel.createWindowDecoration(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
+ }
+ }
+
@Override
public void onTransitionStarting(@NonNull IBinder transition) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index c52ed249c2ca..75f9a4c33af9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -42,8 +42,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM_SHELL),
WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
- WM_SHELL_SPLIT_SCREEN(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
- Consts.TAG_WM_SHELL),
+ WM_SHELL_SPLIT_SCREEN(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ Consts.TAG_WM_SPLIT_SCREEN),
WM_SHELL_SYSUI_EVENTS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
@@ -110,6 +110,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
private static class Consts {
private static final String TAG_WM_SHELL = "WindowManagerShell";
private static final String TAG_WM_STARTING_WINDOW = "ShellStartingWindow";
+ private static final String TAG_WM_SPLIT_SCREEN = "ShellSplitScreen";
private static final boolean ENABLE_DEBUG = true;
private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index eb08d0ecbd06..5533ad56d17c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -86,8 +86,8 @@ interface ISplitScreen {
/**
* Starts a pair of intent and task in one transition.
*/
- oneway void startIntentAndTask(in PendingIntent pendingIntent, in Intent fillInIntent,
- in Bundle options1, int taskId, in Bundle options2, int sidePosition, float splitRatio,
+ oneway void startIntentAndTask(in PendingIntent pendingIntent, in Bundle options1, int taskId,
+ in Bundle options2, int sidePosition, float splitRatio,
in RemoteTransition remoteTransition, in InstanceId instanceId) = 16;
/**
@@ -108,9 +108,8 @@ interface ISplitScreen {
* Starts a pair of intent and task using legacy transition system.
*/
oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
- in Intent fillInIntent, in Bundle options1, int taskId, in Bundle options2,
- int splitPosition, float splitRatio, in RemoteAnimationAdapter adapter,
- in InstanceId instanceId) = 12;
+ in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio,
+ in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 12;
/**
* Starts a pair of shortcut and task using legacy transition system.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index c6a2b8312ebd..cdc8cdd2c28d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -32,6 +33,8 @@ import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -60,13 +63,12 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -166,8 +168,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
+ private final String[] mMultiInstancesComponents;
+
+ @VisibleForTesting
+ StageCoordinator mStageCoordinator;
- private StageCoordinator mStageCoordinator;
// Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
// outside the bounds of the roots by being reparented into a higher level fullscreen container
private SurfaceControl mGoingToRecentsTasksLayer;
@@ -210,6 +215,51 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
shellInit.addInitCallback(this::onInit, this);
}
+
+ // TODO(255224696): Remove the config once having a way for client apps to opt-in
+ // multi-instances split.
+ mMultiInstancesComponents = mContext.getResources()
+ .getStringArray(R.array.config_componentsSupportMultiInstancesSplit);
+ }
+
+ @VisibleForTesting
+ SplitScreenController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+ DisplayController displayController,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController,
+ DragAndDropController dragAndDropController,
+ Transitions transitions,
+ TransactionPool transactionPool,
+ IconProvider iconProvider,
+ RecentTasksController recentTasks,
+ ShellExecutor mainExecutor,
+ StageCoordinator stageCoordinator) {
+ mShellCommandHandler = shellCommandHandler;
+ mShellController = shellController;
+ mTaskOrganizer = shellTaskOrganizer;
+ mSyncQueue = syncQueue;
+ mContext = context;
+ mRootTDAOrganizer = rootTDAOrganizer;
+ mMainExecutor = mainExecutor;
+ mDisplayController = displayController;
+ mDisplayImeController = displayImeController;
+ mDisplayInsetsController = displayInsetsController;
+ mDragAndDropController = dragAndDropController;
+ mTransitions = transitions;
+ mTransactionPool = transactionPool;
+ mIconProvider = iconProvider;
+ mRecentTasksOptional = Optional.of(recentTasks);
+ mStageCoordinator = stageCoordinator;
+ mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
+ shellInit.addInitCallback(this::onInit, this);
+ mMultiInstancesComponents = mContext.getResources()
+ .getStringArray(R.array.config_componentsSupportMultiInstancesSplit);
}
public SplitScreen asSplitScreen() {
@@ -471,72 +521,116 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
startIntent(intent, fillInIntent, position, options);
}
+ private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
+ @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
+ @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
+ Intent fillInIntent = null;
+ if (launchSameComponentAdjacently(pendingIntent, splitPosition, taskId)
+ && supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) {
+ fillInIntent = new Intent();
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ }
+ mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
+ options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
+ }
+
+ private void startIntentAndTask(PendingIntent pendingIntent, @Nullable Bundle options1,
+ int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
+ float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ Intent fillInIntent = null;
+ if (launchSameComponentAdjacently(pendingIntent, splitPosition, taskId)
+ && supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) {
+ fillInIntent = new Intent();
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ }
+ mStageCoordinator.startIntentAndTask(pendingIntent, fillInIntent, options1, taskId,
+ options2, splitPosition, splitRatio, remoteTransition, instanceId);
+ }
+
@Override
public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
- if (fillInIntent == null) {
- fillInIntent = new Intent();
- }
- // Flag this as a no-user-action launch to prevent sending user leaving event to the
- // current top activity since it's going to be put into another side of the split. This
- // prevents the current top activity from going into pip mode due to user leaving event.
+ // Flag this as a no-user-action launch to prevent sending user leaving event to the current
+ // top activity since it's going to be put into another side of the split. This prevents the
+ // current top activity from going into pip mode due to user leaving event.
+ if (fillInIntent == null) fillInIntent = new Intent();
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
- // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
- // split and there is no reusable background task.
- if (shouldAddMultipleTaskFlag(intent.getIntent(), position)) {
- final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional.isPresent()
- ? mRecentTasksOptional.get().findTaskInBackground(
- intent.getIntent().getComponent())
- : null;
- if (taskInfo != null) {
- startTask(taskInfo.taskId, position, options);
+ if (launchSameComponentAdjacently(intent, position, INVALID_TASK_ID)) {
+ final ComponentName launching = intent.getIntent().getComponent();
+ if (supportMultiInstancesSplit(launching)) {
+ // To prevent accumulating large number of instances in the background, reuse task
+ // in the background with priority.
+ final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
+ .map(recentTasks -> recentTasks.findTaskInBackground(launching))
+ .orElse(null);
+ if (taskInfo != null) {
+ startTask(taskInfo.taskId, position, options);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "Start task in background");
+ return;
+ }
+
+ // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
+ // the split and there is no reusable background task.
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+ } else if (isSplitScreenVisible()) {
+ mStageCoordinator.switchSplitPosition("startIntent");
return;
}
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
}
- if (!ENABLE_SHELL_TRANSITIONS) {
- mStageCoordinator.startIntentLegacy(intent, fillInIntent, position, options);
- return;
- }
mStageCoordinator.startIntent(intent, fillInIntent, position, options);
}
/** Returns {@code true} if it's launching the same component on both sides of the split. */
- @VisibleForTesting
- boolean shouldAddMultipleTaskFlag(@Nullable Intent startIntent, @SplitPosition int position) {
- if (startIntent == null) {
- return false;
- }
+ private boolean launchSameComponentAdjacently(@Nullable PendingIntent pendingIntent,
+ @SplitPosition int position, int taskId) {
+ if (pendingIntent == null || pendingIntent.getIntent() == null) return false;
+
+ final ComponentName launchingActivity = pendingIntent.getIntent().getComponent();
+ if (launchingActivity == null) return false;
- final ComponentName launchingActivity = startIntent.getComponent();
- if (launchingActivity == null) {
+ if (taskId != INVALID_TASK_ID) {
+ final ActivityManager.RunningTaskInfo taskInfo =
+ mTaskOrganizer.getRunningTaskInfo(taskId);
+ if (taskInfo != null) {
+ return Objects.equals(taskInfo.baseIntent.getComponent(), launchingActivity);
+ }
return false;
}
- if (isSplitScreenVisible()) {
- // To prevent users from constantly dropping the same app to the same side resulting in
- // a large number of instances in the background.
- final ActivityManager.RunningTaskInfo targetTaskInfo = getTaskInfo(position);
- final ComponentName targetActivity = targetTaskInfo != null
- ? targetTaskInfo.baseIntent.getComponent() : null;
- if (Objects.equals(launchingActivity, targetActivity)) {
- return false;
+ if (!isSplitScreenVisible()) {
+ // Split screen is not yet activated, check if the current top running task is valid to
+ // split together.
+ final ActivityManager.RunningTaskInfo taskInfo = getFocusingTaskInfo();
+ if (taskInfo != null && isValidToEnterSplitScreen(taskInfo)) {
+ return Objects.equals(taskInfo.baseIntent.getComponent(), launchingActivity);
}
-
- // Allow users to start a new instance the same to adjacent side.
- final ActivityManager.RunningTaskInfo pairedTaskInfo =
- getTaskInfo(SplitLayout.reversePosition(position));
- final ComponentName pairedActivity = pairedTaskInfo != null
- ? pairedTaskInfo.baseIntent.getComponent() : null;
- return Objects.equals(launchingActivity, pairedActivity);
+ return false;
}
- final ActivityManager.RunningTaskInfo taskInfo = getFocusingTaskInfo();
- if (taskInfo != null && isValidToEnterSplitScreen(taskInfo)) {
- return Objects.equals(taskInfo.baseIntent.getComponent(), launchingActivity);
+ // Compare to the adjacent side of the split to determine if this is launching the same
+ // component adjacently.
+ final ActivityManager.RunningTaskInfo pairedTaskInfo =
+ getTaskInfo(SplitLayout.reversePosition(position));
+ final ComponentName pairedActivity = pairedTaskInfo != null
+ ? pairedTaskInfo.baseIntent.getComponent() : null;
+ return Objects.equals(launchingActivity, pairedActivity);
+ }
+
+ @VisibleForTesting
+ /** Returns {@code true} if the component supports multi-instances split. */
+ boolean supportMultiInstancesSplit(@Nullable ComponentName launching) {
+ if (launching == null) return false;
+
+ final String componentName = launching.flattenToString();
+ for (int i = 0; i < mMultiInstancesComponents.length; i++) {
+ if (mMultiInstancesComponents[i].equals(componentName)) {
+ return true;
+ }
}
return false;
@@ -839,14 +933,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Override
public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
- Intent fillInIntent, Bundle options1, int taskId, Bundle options2,
- int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
- InstanceId instanceId) {
+ Bundle options1, int taskId, Bundle options2, int splitPosition, float splitRatio,
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController,
"startIntentAndTaskWithLegacyTransition", (controller) ->
- controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition(
- pendingIntent, fillInIntent, options1, taskId, options2,
- splitPosition, splitRatio, adapter, instanceId));
+ controller.startIntentAndTaskWithLegacyTransition(pendingIntent,
+ options1, taskId, options2, splitPosition, splitRatio, adapter,
+ instanceId));
}
@Override
@@ -872,14 +965,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
- public void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent,
- @Nullable Bundle options1, int taskId, @Nullable Bundle options2,
- @SplitPosition int splitPosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ public void startIntentAndTask(PendingIntent pendingIntent, @Nullable Bundle options1,
+ int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
+ float splitRatio, @Nullable RemoteTransition remoteTransition,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startIntentAndTask",
- (controller) -> controller.mStageCoordinator.startIntentAndTask(pendingIntent,
- fillInIntent, options1, taskId, options2, splitPosition, splitRatio,
- remoteTransition, instanceId));
+ (controller) -> controller.startIntentAndTask(pendingIntent, options1, taskId,
+ options2, splitPosition, splitRatio, remoteTransition, instanceId));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 15a11334307b..c2ab7ef7e7bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -24,7 +24,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
@@ -428,6 +427,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Launches an activity into split. */
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ startIntentLegacy(intent, fillInIntent, position, options);
+ return;
+ }
+
final WindowContainerTransaction wct = new WindowContainerTransaction();
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
prepareEvictChildTasks(position, evictWct);
@@ -441,13 +445,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
prepareEnterSplitScreen(wct, null /* taskInfo */, position);
mSplitTransitions.startEnterTransition(transitType, wct, null, this,
- aborted -> {
- // Switch the split position if launching as MULTIPLE_TASK failed.
- if (aborted && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
- setSideStagePositionAnimated(
- SplitLayout.reversePosition(mSideStagePosition));
- }
- } /* consumedCallback */,
+ null /* consumedCallback */,
(finishWct, finishT) -> {
if (!evictWct.isEmpty()) {
finishWct.merge(evictWct, true);
@@ -457,7 +455,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Launches an activity into split by legacy transition. */
void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
- @SplitPosition int position, @androidx.annotation.Nullable Bundle options) {
+ @SplitPosition int position, @Nullable Bundle options) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
prepareEvictChildTasks(position, evictWct);
@@ -473,12 +471,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
exitSplitScreen(mMainStage.getChildCount() == 0
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
mSplitUnsupportedToast.show();
- } else {
- // Switch the split position if launching as MULTIPLE_TASK failed.
- if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
- setSideStagePosition(SplitLayout.reversePosition(
- getSideStagePosition()), null);
- }
}
// Do nothing when the animation was cancelled.
@@ -771,9 +763,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStage.evictInvisibleChildren(wct);
}
- Bundle resolveStartStage(@StageType int stage,
- @SplitPosition int position, @androidx.annotation.Nullable Bundle options,
- @androidx.annotation.Nullable WindowContainerTransaction wct) {
+ Bundle resolveStartStage(@StageType int stage, @SplitPosition int position,
+ @Nullable Bundle options, @Nullable WindowContainerTransaction wct) {
switch (stage) {
case STAGE_TYPE_UNDEFINED: {
if (position != SPLIT_POSITION_UNDEFINED) {
@@ -844,9 +835,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
: mMainStage.getTopVisibleChildTaskId();
}
- void setSideStagePositionAnimated(@SplitPosition int sideStagePosition) {
- if (mSideStagePosition == sideStagePosition) return;
- SurfaceControl.Transaction t = mTransactionPool.acquire();
+ void switchSplitPosition(String reason) {
+ final SurfaceControl.Transaction t = mTransactionPool.acquire();
mTempRect1.setEmpty();
final StageTaskListener topLeftStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
@@ -886,6 +876,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
va.start();
});
});
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
+ mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
}
void setSideStagePosition(@SplitPosition int sideStagePosition,
@@ -1097,7 +1092,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus));
} catch (RemoteException | NullPointerException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Unable to update focus on the chosen stage, %s", TAG, e);
+ "Unable to update focus on the chosen stage: %s", e.getMessage());
}
}
@@ -1434,14 +1429,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Request to %s divider bar from %s.", TAG,
+ "Request to %s divider bar from %s.",
(visible ? "show" : "hide"), Debug.getCaller());
// Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard
// dismissing animation.
if (visible && mKeyguardShowing) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Defer showing divider bar due to keyguard showing.", TAG);
+ " Defer showing divider bar due to keyguard showing.");
return;
}
@@ -1450,7 +1445,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mIsDividerRemoteAnimating) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Skip animating divider bar due to it's remote animating.", TAG);
+ " Skip animating divider bar due to it's remote animating.");
return;
}
@@ -1465,12 +1460,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
if (dividerLeash == null) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Skip animating divider bar due to divider leash not ready.", TAG);
+ " Skip animating divider bar due to divider leash not ready.");
return;
}
if (mIsDividerRemoteAnimating) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "%s: Skip animating divider bar due to it's remote animating.", TAG);
+ " Skip animating divider bar due to it's remote animating.");
return;
}
@@ -1617,10 +1612,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onDoubleTappedDivider() {
- setSideStagePositionAnimated(SplitLayout.reversePosition(mSideStagePosition));
- mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
+ switchSplitPosition("double tap");
}
@Override
@@ -1633,14 +1625,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@Override
- public void onLayoutSizeChanging(SplitLayout layout) {
+ public void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY) {
final SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
updateSurfaceBounds(layout, t, true /* applyResizingOffset */);
getMainStageBounds(mTempRect1);
getSideStageBounds(mTempRect2);
- mMainStage.onResizing(mTempRect1, mTempRect2, t);
- mSideStage.onResizing(mTempRect2, mTempRect1, t);
+ mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY);
+ mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY);
t.apply();
mTransactionPool.release(t);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 6b90eabe3bd2..acad5d93eab4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -288,9 +288,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
}
- void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t) {
+ void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t, int offsetX,
+ int offsetY) {
if (mSplitDecorManager != null && mRootTaskInfo != null) {
- mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t);
+ mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t, offsetX,
+ offsetY);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 2830fa967011..857decf65567 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -24,7 +24,6 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
-import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
@@ -333,9 +332,12 @@ public class Transitions implements RemoteCallable<Transitions> {
boolean isOpening = isOpeningType(info.getType());
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if ((change.getFlags() & TransitionInfo.FLAG_IS_SYSTEM_WINDOW) != 0) {
+ if (change.hasFlags(TransitionInfo.FLAGS_IS_NON_APP_WINDOW)) {
// Currently system windows are controlled by WindowState, so don't change their
- // surfaces. Otherwise their window tokens could be hidden unexpectedly.
+ // surfaces. Otherwise their surfaces could be hidden or cropped unexpectedly.
+ // This includes Wallpaper (always z-ordered at bottom) and IME (associated with
+ // app), because there may not be a transition associated with their visibility
+ // changes, and currently they don't need transition animation.
continue;
}
final SurfaceControl leash = change.getLeash();
@@ -372,16 +374,7 @@ public class Transitions implements RemoteCallable<Transitions> {
finishT.setAlpha(leash, 1.f);
}
} else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
- // Wallpaper/IME are anomalies: their visibility is tied to other WindowStates.
- // As a result, we actually can't hide their WindowTokens because there may not be a
- // transition associated with them becoming visible again. Fortunately, since
- // wallpapers are always z-ordered to the back, we don't have to worry about it
- // flickering to the front during reparenting. Similarly, the IME is reparented to
- // the associated app, so its visibility is coupled. So, an explicit hide is not
- // needed visually anyways.
- if ((change.getFlags() & (FLAG_IS_WALLPAPER | FLAG_IS_INPUT_METHOD)) == 0) {
- finishT.hide(leash);
- }
+ finishT.hide(leash);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 36dd8edaa8b7..ca15f0002fac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -105,6 +105,11 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
if (!shouldShowWindowDecor(taskInfo)) return false;
+ CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ if (oldDecoration != null) {
+ // close the old decoration if it exists to avoid two window decorations being added
+ oldDecoration.close();
+ }
final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
mContext,
mDisplayController,
@@ -141,23 +146,25 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
@Override
- public void setupWindowDecorationForTransition(
+ public boolean setupWindowDecorationForTransition(
RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
- if (decoration == null) return;
+ if (decoration == null) return false;
decoration.relayout(taskInfo, startT, finishT);
+ return true;
}
@Override
- public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
+ public boolean destroyWindowDecoration(RunningTaskInfo taskInfo) {
final CaptionWindowDecoration decoration =
mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
- if (decoration == null) return;
+ if (decoration == null) return false;
decoration.close();
+ return true;
}
private class CaptionTouchEventListener implements
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index f0f2db7ded80..a49a300995e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -40,6 +40,9 @@ class TaskPositioner implements DragResizeCallback {
private final Rect mTaskBoundsAtDragStart = new Rect();
private final PointF mResizeStartPoint = new PointF();
private final Rect mResizeTaskBounds = new Rect();
+ // Whether the |dragResizing| hint should be sent with the next bounds change WCT.
+ // Used to optimized fluid resizing of freeform tasks.
+ private boolean mPendingDragResizeHint = false;
private int mCtrlType;
private DragStartListener mDragStartListener;
@@ -53,6 +56,12 @@ class TaskPositioner implements DragResizeCallback {
@Override
public void onDragResizeStart(int ctrlType, float x, float y) {
+ if (ctrlType != CTRL_TYPE_UNDEFINED) {
+ // The task is being resized, send the |dragResizing| hint to core with the first
+ // bounds-change wct.
+ mPendingDragResizeHint = true;
+ }
+
mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
mCtrlType = ctrlType;
@@ -63,19 +72,31 @@ class TaskPositioner implements DragResizeCallback {
@Override
public void onDragResizeMove(float x, float y) {
- changeBounds(x, y);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (changeBounds(wct, x, y)) {
+ if (mPendingDragResizeHint) {
+ // This is the first bounds change since drag resize operation started.
+ wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
+ mPendingDragResizeHint = false;
+ }
+ mTaskOrganizer.applyTransaction(wct);
+ }
}
@Override
public void onDragResizeEnd(float x, float y) {
- changeBounds(x, y);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
+ changeBounds(wct, x, y);
+ mTaskOrganizer.applyTransaction(wct);
mCtrlType = 0;
mTaskBoundsAtDragStart.setEmpty();
mResizeStartPoint.set(0, 0);
+ mPendingDragResizeHint = false;
}
- private void changeBounds(float x, float y) {
+ private boolean changeBounds(WindowContainerTransaction wct, float x, float y) {
float deltaX = x - mResizeStartPoint.x;
mResizeTaskBounds.set(mTaskBoundsAtDragStart);
if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
@@ -96,10 +117,10 @@ class TaskPositioner implements DragResizeCallback {
}
if (!mResizeTaskBounds.isEmpty()) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
- mTaskOrganizer.applyTransaction(wct);
+ return true;
}
+ return false;
}
interface DragStartListener {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index d7f71c8235f1..2ce4d04377a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -44,7 +44,7 @@ public interface WindowDecorViewModel {
* @param taskSurface the surface of the task
* @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition
- * @return the window decoration object
+ * @return {@code true} if window decoration was created, {@code false} otherwise
*/
boolean createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo,
@@ -66,8 +66,9 @@ public interface WindowDecorViewModel {
*
* @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition
+ * @return {@code true} if window decoration exists, {@code false} otherwise
*/
- void setupWindowDecorationForTransition(
+ boolean setupWindowDecorationForTransition(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT);
@@ -76,6 +77,7 @@ public interface WindowDecorViewModel {
* Destroys the window decoration of the give task.
*
* @param taskInfo the info of the task
+ * @return {@code true} if window decoration was destroyed, {@code false} otherwise
*/
- void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
+ boolean destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 7cbace5af48f..081c8ae91bdb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -16,13 +16,9 @@
package com.android.wm.shell;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -34,8 +30,6 @@ import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIO
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.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
@@ -44,11 +38,9 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
-import android.app.WindowConfiguration;
import android.content.LocusId;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
@@ -61,8 +53,6 @@ import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
import android.window.TaskAppearedInfo;
import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-import android.window.WindowContainerTransaction.Change;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -638,130 +628,10 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
verify(mTaskOrganizerController).restartTaskTopActivityProcessIfVisible(task1.token);
}
- @Test
- public void testPrepareClearBoundsForStandardTasks() {
- MockToken token1 = new MockToken();
- RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_UNDEFINED, token1);
- mOrganizer.onTaskAppeared(task1, null);
-
- MockToken token2 = new MockToken();
- RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_UNDEFINED, token2);
- mOrganizer.onTaskAppeared(task2, null);
-
- MockToken otherDisplayToken = new MockToken();
- RunningTaskInfo otherDisplayTask = createTaskInfo(3, WINDOWING_MODE_UNDEFINED,
- otherDisplayToken);
- otherDisplayTask.displayId = 2;
- mOrganizer.onTaskAppeared(otherDisplayTask, null);
-
- WindowContainerTransaction wct = mOrganizer.prepareClearBoundsForStandardTasks(1);
-
- assertEquals(wct.getChanges().size(), 2);
- Change boundsChange1 = wct.getChanges().get(token1.binder());
- assertNotNull(boundsChange1);
- assertNotEquals(
- (boundsChange1.getWindowSetMask() & WindowConfiguration.WINDOW_CONFIG_BOUNDS), 0);
- assertTrue(boundsChange1.getConfiguration().windowConfiguration.getBounds().isEmpty());
-
- Change boundsChange2 = wct.getChanges().get(token2.binder());
- assertNotNull(boundsChange2);
- assertNotEquals(
- (boundsChange2.getWindowSetMask() & WindowConfiguration.WINDOW_CONFIG_BOUNDS), 0);
- assertTrue(boundsChange2.getConfiguration().windowConfiguration.getBounds().isEmpty());
- }
-
- @Test
- public void testPrepareClearBoundsForStandardTasks_onlyClearActivityTypeStandard() {
- MockToken token1 = new MockToken();
- RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_UNDEFINED, token1);
- mOrganizer.onTaskAppeared(task1, null);
-
- MockToken token2 = new MockToken();
- RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_UNDEFINED, token2);
- task2.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_HOME);
- mOrganizer.onTaskAppeared(task2, null);
-
- WindowContainerTransaction wct = mOrganizer.prepareClearBoundsForStandardTasks(1);
-
- // Only clear bounds for task1
- assertEquals(1, wct.getChanges().size());
- assertNotNull(wct.getChanges().get(token1.binder()));
- }
-
- @Test
- public void testPrepareClearFreeformForStandardTasks() {
- MockToken token1 = new MockToken();
- RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_FREEFORM, token1);
- mOrganizer.onTaskAppeared(task1, null);
-
- MockToken token2 = new MockToken();
- RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW, token2);
- mOrganizer.onTaskAppeared(task2, null);
-
- MockToken otherDisplayToken = new MockToken();
- RunningTaskInfo otherDisplayTask = createTaskInfo(3, WINDOWING_MODE_FREEFORM,
- otherDisplayToken);
- otherDisplayTask.displayId = 2;
- mOrganizer.onTaskAppeared(otherDisplayTask, null);
-
- WindowContainerTransaction wct = mOrganizer.prepareClearFreeformForStandardTasks(1);
-
- // Only task with freeform windowing mode and the right display should be updated
- assertEquals(wct.getChanges().size(), 1);
- Change wmModeChange1 = wct.getChanges().get(token1.binder());
- assertNotNull(wmModeChange1);
- assertEquals(wmModeChange1.getWindowingMode(), WINDOWING_MODE_UNDEFINED);
- }
-
- @Test
- public void testPrepareClearFreeformForStandardTasks_onlyClearActivityTypeStandard() {
- MockToken token1 = new MockToken();
- RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_FREEFORM, token1);
- mOrganizer.onTaskAppeared(task1, null);
-
- MockToken token2 = new MockToken();
- RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_FREEFORM, token2);
- task2.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_HOME);
- mOrganizer.onTaskAppeared(task2, null);
-
- WindowContainerTransaction wct = mOrganizer.prepareClearFreeformForStandardTasks(1);
-
- // Only clear freeform for task1
- assertEquals(1, wct.getChanges().size());
- assertNotNull(wct.getChanges().get(token1.binder()));
- }
-
private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
taskInfo.taskId = taskId;
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
return taskInfo;
}
-
- private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode, MockToken token) {
- RunningTaskInfo taskInfo = createTaskInfo(taskId, windowingMode);
- taskInfo.displayId = 1;
- taskInfo.token = token.token();
- taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
- return taskInfo;
- }
-
- private static class MockToken {
- private final WindowContainerToken mToken;
- private final IBinder mBinder;
-
- MockToken() {
- mToken = mock(WindowContainerToken.class);
- mBinder = mock(IBinder.class);
- when(mToken.asBinder()).thenReturn(mBinder);
- }
-
- WindowContainerToken token() {
- return mToken;
- }
-
- IBinder binder() {
- return mBinder;
- }
- }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS
new file mode 100644
index 000000000000..1e0f9bc6322f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS
@@ -0,0 +1,5 @@
+# WM shell sub-module back navigation owners
+# Bug component: 1152663
+shanh@google.com
+arthurhung@google.com
+wilsonshih@google.com
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 5332476d5130..3d779481d361 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -105,7 +105,8 @@ public class SplitLayoutTests extends ShellTestCase {
@Test
public void testUpdateDivideBounds() {
mSplitLayout.updateDivideBounds(anyInt());
- verify(mSplitLayoutHandler).onLayoutSizeChanging(any(SplitLayout.class));
+ verify(mSplitLayoutHandler).onLayoutSizeChanging(any(SplitLayout.class), anyInt(),
+ anyInt());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
index 79b520c734c8..89bafcb6b2f4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
@@ -16,10 +16,13 @@
package com.android.wm.shell.desktopmode;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
@@ -30,13 +33,14 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -68,6 +72,9 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
+import java.util.ArrayList;
+import java.util.Arrays;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class DesktopModeControllerTest extends ShellTestCase {
@@ -83,9 +90,7 @@ public class DesktopModeControllerTest extends ShellTestCase {
@Mock
private Handler mMockHandler;
@Mock
- private Transitions mMockTransitions;
- private TestShellExecutor mExecutor;
-
+ private Transitions mTransitions;
private DesktopModeController mController;
private DesktopModeTaskRepository mDesktopModeTaskRepository;
private ShellInit mShellInit;
@@ -97,20 +102,19 @@ public class DesktopModeControllerTest extends ShellTestCase {
when(DesktopModeStatus.isActive(any())).thenReturn(true);
mShellInit = Mockito.spy(new ShellInit(mTestExecutor));
- mExecutor = new TestShellExecutor();
mDesktopModeTaskRepository = new DesktopModeTaskRepository();
mController = new DesktopModeController(mContext, mShellInit, mShellController,
- mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mMockTransitions,
- mDesktopModeTaskRepository, mMockHandler, mExecutor);
+ mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mTransitions,
+ mDesktopModeTaskRepository, mMockHandler, new TestShellExecutor());
- when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(anyInt())).thenReturn(
- new WindowContainerTransaction());
+ when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>());
mShellInit.init();
clearInvocations(mShellTaskOrganizer);
clearInvocations(mRootTaskDisplayAreaOrganizer);
+ clearInvocations(mTransitions);
}
@After
@@ -124,113 +128,133 @@ public class DesktopModeControllerTest extends ShellTestCase {
}
@Test
- public void testDesktopModeEnabled_taskWmClearedDisplaySetToFreeform() {
- // Create a fake WCT to simulate setting task windowing mode to undefined
- WindowContainerTransaction taskWct = new WindowContainerTransaction();
- MockToken taskMockToken = new MockToken();
- taskWct.setWindowingMode(taskMockToken.token(), WINDOWING_MODE_UNDEFINED);
- when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(
- mContext.getDisplayId())).thenReturn(taskWct);
-
- // Create a fake DisplayAreaInfo to check if windowing mode change is set correctly
- MockToken displayMockToken = new MockToken();
- DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(displayMockToken.mToken,
- mContext.getDisplayId(), 0);
- when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId()))
- .thenReturn(displayAreaInfo);
+ public void testDesktopModeEnabled_rootTdaSetToFreeform() {
+ DisplayAreaInfo displayAreaInfo = createMockDisplayArea();
- // The test
mController.updateDesktopModeActive(true);
+ WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
+
+ // 1 change: Root TDA windowing mode
+ assertThat(wct.getChanges().size()).isEqualTo(1);
+ // Verify WCT has a change for setting windowing mode to freeform
+ Change change = wct.getChanges().get(displayAreaInfo.token.asBinder());
+ assertThat(change).isNotNull();
+ assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM);
+ }
- ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
- WindowContainerTransaction.class);
- verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture());
+ @Test
+ public void testDesktopModeDisabled_rootTdaSetToFullscreen() {
+ DisplayAreaInfo displayAreaInfo = createMockDisplayArea();
- // WCT should have 2 changes - clear task wm mode and set display wm mode
- WindowContainerTransaction wct = arg.getValue();
- assertThat(wct.getChanges()).hasSize(2);
+ mController.updateDesktopModeActive(false);
+ WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
+
+ // 1 change: Root TDA windowing mode
+ assertThat(wct.getChanges().size()).isEqualTo(1);
+ // Verify WCT has a change for setting windowing mode to fullscreen
+ Change change = wct.getChanges().get(displayAreaInfo.token.asBinder());
+ assertThat(change).isNotNull();
+ assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN);
+ }
- // Verify executed WCT has a change for setting task windowing mode to undefined
- Change taskWmModeChange = wct.getChanges().get(taskMockToken.binder());
- assertThat(taskWmModeChange).isNotNull();
- assertThat(taskWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
+ @Test
+ public void testDesktopModeEnabled_windowingModeCleared() {
+ createMockDisplayArea();
+ RunningTaskInfo freeformTask = createFreeformTask();
+ RunningTaskInfo fullscreenTask = createFullscreenTask();
+ RunningTaskInfo homeTask = createHomeTask();
+ when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>(
+ Arrays.asList(freeformTask, fullscreenTask, homeTask)));
- // Verify executed WCT has a change for setting display windowing mode to freeform
- Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder());
- assertThat(displayWmModeChange).isNotNull();
- assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM);
+ mController.updateDesktopModeActive(true);
+ WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
+
+ // 2 changes: Root TDA windowing mode and 1 task
+ assertThat(wct.getChanges().size()).isEqualTo(2);
+ // No changes for tasks that are not standard or freeform
+ assertThat(wct.getChanges().get(fullscreenTask.token.asBinder())).isNull();
+ assertThat(wct.getChanges().get(homeTask.token.asBinder())).isNull();
+ // Standard freeform task has windowing mode cleared
+ Change change = wct.getChanges().get(freeformTask.token.asBinder());
+ assertThat(change).isNotNull();
+ assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
}
@Test
- public void testDesktopModeDisabled_taskWmAndBoundsClearedDisplaySetToFullscreen() {
- // Create a fake WCT to simulate setting task windowing mode to undefined
- WindowContainerTransaction taskWmWct = new WindowContainerTransaction();
- MockToken taskWmMockToken = new MockToken();
- taskWmWct.setWindowingMode(taskWmMockToken.token(), WINDOWING_MODE_UNDEFINED);
- when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(
- mContext.getDisplayId())).thenReturn(taskWmWct);
-
- // Create a fake WCT to simulate clearing task bounds
- WindowContainerTransaction taskBoundsWct = new WindowContainerTransaction();
- MockToken taskBoundsMockToken = new MockToken();
- taskBoundsWct.setBounds(taskBoundsMockToken.token(), null);
- when(mShellTaskOrganizer.prepareClearBoundsForStandardTasks(
- mContext.getDisplayId())).thenReturn(taskBoundsWct);
-
- // Create a fake DisplayAreaInfo to check if windowing mode change is set correctly
- MockToken displayMockToken = new MockToken();
- DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(displayMockToken.mToken,
- mContext.getDisplayId(), 0);
- when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId()))
- .thenReturn(displayAreaInfo);
+ public void testDesktopModeDisabled_windowingModeAndBoundsCleared() {
+ createMockDisplayArea();
+ RunningTaskInfo freeformTask = createFreeformTask();
+ RunningTaskInfo fullscreenTask = createFullscreenTask();
+ RunningTaskInfo homeTask = createHomeTask();
+ when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>(
+ Arrays.asList(freeformTask, fullscreenTask, homeTask)));
- // The test
mController.updateDesktopModeActive(false);
+ WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
+
+ // 3 changes: Root TDA windowing mode and 2 tasks
+ assertThat(wct.getChanges().size()).isEqualTo(3);
+ // No changes to home task
+ assertThat(wct.getChanges().get(homeTask.token.asBinder())).isNull();
+ // Standard tasks have bounds cleared
+ assertThatBoundsCleared(wct.getChanges().get(freeformTask.token.asBinder()));
+ assertThatBoundsCleared(wct.getChanges().get(fullscreenTask.token.asBinder()));
+ // Freeform standard tasks have windowing mode cleared
+ assertThat(wct.getChanges().get(
+ freeformTask.token.asBinder()).getWindowingMode()).isEqualTo(
+ WINDOWING_MODE_UNDEFINED);
+ }
- ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
- WindowContainerTransaction.class);
- verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture());
-
- // WCT should have 3 changes - clear task wm mode and bounds and set display wm mode
- WindowContainerTransaction wct = arg.getValue();
- assertThat(wct.getChanges()).hasSize(3);
-
- // Verify executed WCT has a change for setting task windowing mode to undefined
- Change taskWmMode = wct.getChanges().get(taskWmMockToken.binder());
- assertThat(taskWmMode).isNotNull();
- assertThat(taskWmMode.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
-
- // Verify executed WCT has a change for clearing task bounds
- Change bounds = wct.getChanges().get(taskBoundsMockToken.binder());
- assertThat(bounds).isNotNull();
- assertThat(bounds.getWindowSetMask() & WINDOW_CONFIG_BOUNDS).isNotEqualTo(0);
- assertThat(bounds.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue();
-
- // Verify executed WCT has a change for setting display windowing mode to fullscreen
- Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder());
- assertThat(displayWmModeChange).isNotNull();
- assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN);
+ @Test
+ public void testDesktopModeEnabled_homeTaskBehindVisibleTask() {
+ createMockDisplayArea();
+ RunningTaskInfo fullscreenTask1 = createFullscreenTask();
+ fullscreenTask1.isVisible = true;
+ RunningTaskInfo fullscreenTask2 = createFullscreenTask();
+ fullscreenTask2.isVisible = false;
+ RunningTaskInfo homeTask = createHomeTask();
+ when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>(
+ Arrays.asList(fullscreenTask1, fullscreenTask2, homeTask)));
+
+ mController.updateDesktopModeActive(true);
+ WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
+
+ // Check that there are hierarchy changes for home task and visible task
+ assertThat(wct.getHierarchyOps()).hasSize(2);
+ // First show home task
+ WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0);
+ assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
+ assertThat(op1.getContainer()).isEqualTo(homeTask.token.asBinder());
+
+ // Then visible task on top of it
+ WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1);
+ assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
+ assertThat(op2.getContainer()).isEqualTo(fullscreenTask1.token.asBinder());
}
@Test
public void testShowDesktopApps() {
// Set up two active tasks on desktop
- mDesktopModeTaskRepository.addActiveTask(1);
- mDesktopModeTaskRepository.addActiveTask(2);
- MockToken token1 = new MockToken();
- MockToken token2 = new MockToken();
- ActivityManager.RunningTaskInfo taskInfo1 = new TestRunningTaskInfoBuilder().setToken(
- token1.token()).setLastActiveTime(100).build();
- ActivityManager.RunningTaskInfo taskInfo2 = new TestRunningTaskInfoBuilder().setToken(
- token2.token()).setLastActiveTime(200).build();
- when(mShellTaskOrganizer.getRunningTaskInfo(1)).thenReturn(taskInfo1);
- when(mShellTaskOrganizer.getRunningTaskInfo(2)).thenReturn(taskInfo2);
+ RunningTaskInfo freeformTask1 = createFreeformTask();
+ freeformTask1.lastActiveTime = 100;
+ RunningTaskInfo freeformTask2 = createFreeformTask();
+ freeformTask2.lastActiveTime = 200;
+ mDesktopModeTaskRepository.addActiveTask(freeformTask1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(freeformTask2.taskId);
+ when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn(
+ freeformTask1);
+ when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn(
+ freeformTask2);
// Run show desktop apps logic
mController.showDesktopApps();
ArgumentCaptor<WindowContainerTransaction> wctCaptor = ArgumentCaptor.forClass(
WindowContainerTransaction.class);
- verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture());
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ verify(mTransitions).startTransition(eq(TRANSIT_TO_FRONT), wctCaptor.capture(), any());
+ } else {
+ verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture());
+ }
WindowContainerTransaction wct = wctCaptor.getValue();
// Check wct has reorder calls
@@ -239,12 +263,12 @@ public class DesktopModeControllerTest extends ShellTestCase {
// Task 2 has activity later, must be first
WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0);
assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op1.getContainer()).isEqualTo(token2.binder());
+ assertThat(op1.getContainer()).isEqualTo(freeformTask2.token.asBinder());
// Task 1 should be second
- WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(0);
+ WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1);
assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op2.getContainer()).isEqualTo(token2.binder());
+ assertThat(op2.getContainer()).isEqualTo(freeformTask1.token.asBinder());
}
@Test
@@ -266,7 +290,7 @@ public class DesktopModeControllerTest extends ShellTestCase {
@Test
public void testHandleTransitionRequest_notFreeform_returnsNull() {
- ActivityManager.RunningTaskInfo trigger = new ActivityManager.RunningTaskInfo();
+ RunningTaskInfo trigger = new RunningTaskInfo();
trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
WindowContainerTransaction wct = mController.handleRequest(
new Binder(),
@@ -276,7 +300,7 @@ public class DesktopModeControllerTest extends ShellTestCase {
@Test
public void testHandleTransitionRequest_returnsWct() {
- ActivityManager.RunningTaskInfo trigger = new ActivityManager.RunningTaskInfo();
+ RunningTaskInfo trigger = new RunningTaskInfo();
trigger.token = new MockToken().mToken;
trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
WindowContainerTransaction wct = mController.handleRequest(
@@ -285,6 +309,57 @@ public class DesktopModeControllerTest extends ShellTestCase {
assertThat(wct).isNotNull();
}
+ private DisplayAreaInfo createMockDisplayArea() {
+ DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().mToken,
+ mContext.getDisplayId(), 0);
+ when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId()))
+ .thenReturn(displayAreaInfo);
+ return displayAreaInfo;
+ }
+
+ private RunningTaskInfo createFreeformTask() {
+ return new TestRunningTaskInfoBuilder()
+ .setToken(new MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM)
+ .setLastActiveTime(100)
+ .build();
+ }
+
+ private RunningTaskInfo createFullscreenTask() {
+ return new TestRunningTaskInfoBuilder()
+ .setToken(new MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setLastActiveTime(100)
+ .build();
+ }
+
+ private RunningTaskInfo createHomeTask() {
+ return new TestRunningTaskInfoBuilder()
+ .setToken(new MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_HOME)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setLastActiveTime(100)
+ .build();
+ }
+
+ private WindowContainerTransaction getDesktopModeSwitchTransaction() {
+ ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
+ WindowContainerTransaction.class);
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ verify(mTransitions).startTransition(eq(TRANSIT_CHANGE), arg.capture(), any());
+ } else {
+ verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture());
+ }
+ return arg.getValue();
+ }
+
+ private void assertThatBoundsCleared(Change change) {
+ assertThat((change.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) != 0).isTrue();
+ assertThat(change.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue();
+ }
+
private static class MockToken {
private final WindowContainerToken mToken;
private final IBinder mBinder;
@@ -298,9 +373,5 @@ public class DesktopModeControllerTest extends ShellTestCase {
WindowContainerToken token() {
return mToken;
}
-
- IBinder binder() {
- return mBinder;
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index d01f3d310fc3..38b75f81171f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -16,18 +16,24 @@
package com.android.wm.shell.splitscreen;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -35,6 +41,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -65,11 +73,11 @@ import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
-
/**
* Tests for {@link SplitScreenController}
*/
@@ -91,18 +99,21 @@ public class SplitScreenControllerTests extends ShellTestCase {
@Mock Transitions mTransitions;
@Mock TransactionPool mTransactionPool;
@Mock IconProvider mIconProvider;
- @Mock Optional<RecentTasksController> mRecentTasks;
+ @Mock StageCoordinator mStageCoordinator;
+ @Mock RecentTasksController mRecentTasks;
+ @Captor ArgumentCaptor<Intent> mIntentCaptor;
private SplitScreenController mSplitScreenController;
@Before
public void setup() {
+ assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
MockitoAnnotations.initMocks(this);
mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
mRootTDAOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mMainExecutor));
+ mIconProvider, mRecentTasks, mMainExecutor, mStageCoordinator));
}
@Test
@@ -148,58 +159,100 @@ public class SplitScreenControllerTests extends ShellTestCase {
}
@Test
- public void testShouldAddMultipleTaskFlag_notInSplitScreen() {
- doReturn(false).when(mSplitScreenController).isSplitScreenVisible();
- doReturn(true).when(mSplitScreenController).isValidToEnterSplitScreen(any());
+ public void testStartIntent_appendsNoUserActionFlag() {
+ Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
- // Verify launching the same activity returns true.
+ mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+
+ verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
+ eq(SPLIT_POSITION_TOP_OR_LEFT), isNull());
+ assertEquals(FLAG_ACTIVITY_NO_USER_ACTION,
+ mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_NO_USER_ACTION);
+ }
+
+ @Test
+ public void startIntent_multiInstancesSupported_appendsMultipleTaskFag() {
+ doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+ // Put the same component into focus task
ActivityManager.RunningTaskInfo focusTaskInfo =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
- doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
- assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
-
- // Verify launching different activity returns false.
- Intent diffIntent = createStartIntent("diffActivity");
- focusTaskInfo =
- createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent);
- doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
- assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+ doReturn(focusTaskInfo).when(mStageCoordinator).getFocusingTaskInfo();
+ doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
+
+ mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+
+ verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
+ eq(SPLIT_POSITION_TOP_OR_LEFT), isNull());
+ assertEquals(FLAG_ACTIVITY_MULTIPLE_TASK,
+ mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK);
}
@Test
- public void testShouldAddMultipleTaskFlag_inSplitScreen() {
+ public void startIntent_multiInstancesSupported_startTaskInBackgroundBeforeSplitActivated() {
+ doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
+ doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
+ Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+ // Put the same component into focus task
+ ActivityManager.RunningTaskInfo focusTaskInfo =
+ createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
+ doReturn(focusTaskInfo).when(mStageCoordinator).getFocusingTaskInfo();
+ doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
+ // Put the same component into a task in the background
+ ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
+ doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any());
+
+ mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+
+ verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
+ isNull());
+ }
+
+ @Test
+ public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() {
+ doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
+ doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
+ Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+ // Put the same component into another side of the split
doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
+ ActivityManager.RunningTaskInfo sameTaskInfo =
+ createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
+ doReturn(sameTaskInfo).when(mSplitScreenController).getTaskInfo(
+ SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ // Put the same component into a task in the background
+ doReturn(new ActivityManager.RecentTaskInfo()).when(mRecentTasks)
+ .findTaskInBackground(any());
+
+ mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+
+ verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
+ isNull());
+ }
+
+ @Test
+ public void startIntent_multiInstancesNotSupported_switchesPositionAfterSplitActivated() {
+ doReturn(false).when(mSplitScreenController).supportMultiInstancesSplit(any());
Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+ // Put the same component into another side of the split
+ doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
ActivityManager.RunningTaskInfo sameTaskInfo =
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
- Intent diffIntent = createStartIntent("diffActivity");
- ActivityManager.RunningTaskInfo differentTaskInfo =
- createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
-
- // Verify launching the same activity return false.
- doReturn(sameTaskInfo).when(mSplitScreenController)
- .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
- assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
-
- // Verify launching the same activity as adjacent returns true.
- doReturn(differentTaskInfo).when(mSplitScreenController)
- .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
- doReturn(sameTaskInfo).when(mSplitScreenController)
- .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
- assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
-
- // Verify launching different activity from adjacent returns false.
- doReturn(differentTaskInfo).when(mSplitScreenController)
- .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
- doReturn(differentTaskInfo).when(mSplitScreenController)
- .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
- assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+ doReturn(sameTaskInfo).when(mSplitScreenController).getTaskInfo(
+ SPLIT_POSITION_BOTTOM_OR_RIGHT);
+
+ mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+
+ verify(mStageCoordinator).switchSplitPosition(anyString());
}
private Intent createStartIntent(String activityName) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
new file mode 100644
index 000000000000..ac10ddb0116a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -0,0 +1,130 @@
+package com.android.wm.shell.windowdecor
+
+import android.app.ActivityManager
+import android.graphics.Rect
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+/**
+ * Tests for [TaskPositioner].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:TaskPositionerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TaskPositionerTest : ShellTestCase() {
+
+ @Mock
+ private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer
+ @Mock
+ private lateinit var mockWindowDecoration: WindowDecoration<*>
+ @Mock
+ private lateinit var mockDragStartListener: TaskPositioner.DragStartListener
+
+ @Mock
+ private lateinit var taskToken: WindowContainerToken
+ @Mock
+ private lateinit var taskBinder: IBinder
+
+ private lateinit var taskPositioner: TaskPositioner
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ taskPositioner = TaskPositioner(
+ mockShellTaskOrganizer,
+ mockWindowDecoration,
+ mockDragStartListener
+ )
+ `when`(taskToken.asBinder()).thenReturn(taskBinder)
+ mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
+ taskId = TASK_ID
+ token = taskToken
+ configuration.windowConfiguration.bounds = STARTING_BOUNDS
+ }
+ }
+
+ @Test
+ fun testDragResize_move_skipsDragResizingFlag() {
+ taskPositioner.onDragResizeStart(
+ CTRL_TYPE_UNDEFINED, // Move
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ // Move the task 10px to the right.
+ val newX = STARTING_BOUNDS.left.toFloat() + 10
+ val newY = STARTING_BOUNDS.top.toFloat()
+ taskPositioner.onDragResizeMove(
+ newX,
+ newY
+ )
+
+ taskPositioner.onDragResizeEnd(newX, newY)
+
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
+ change.dragResizing
+ }
+ })
+ }
+
+ @Test
+ fun testDragResize_resize_setsDragResizingFlag() {
+ taskPositioner.onDragResizeStart(
+ CTRL_TYPE_RIGHT, // Resize right
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ // Resize the task by 10px to the right.
+ val newX = STARTING_BOUNDS.right.toFloat() + 10
+ val newY = STARTING_BOUNDS.top.toFloat()
+ taskPositioner.onDragResizeMove(
+ newX,
+ newY
+ )
+
+ taskPositioner.onDragResizeEnd(newX, newY)
+
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
+ change.dragResizing
+ }
+ })
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
+ !change.dragResizing
+ }
+ })
+ }
+
+ companion object {
+ private const val TASK_ID = 5
+ private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
+ }
+}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 95599bd41e8d..1183ca3b4977 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -174,6 +174,12 @@ static struct {
jfieldID typeId;
} gDescriptorInfo;
+static struct {
+ jclass clazz;
+ jmethodID ctorId;
+ jmethodID setId;
+} gBufferInfo;
+
struct fields_t {
jmethodID postEventFromNativeID;
jmethodID lockAndGetContextID;
@@ -460,11 +466,7 @@ status_t JMediaCodec::dequeueOutputBuffer(
return err;
}
- ScopedLocalRef<jclass> clazz(
- env, env->FindClass("android/media/MediaCodec$BufferInfo"));
-
- jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
- env->CallVoidMethod(bufferInfo, method, (jint)offset, (jint)size, timeUs, flags);
+ env->CallVoidMethod(bufferInfo, gBufferInfo.setId, (jint)offset, (jint)size, timeUs, flags);
return OK;
}
@@ -1091,13 +1093,7 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
CHECK(msg->findInt64("timeUs", &timeUs));
CHECK(msg->findInt32("flags", (int32_t *)&flags));
- ScopedLocalRef<jclass> clazz(
- env, env->FindClass("android/media/MediaCodec$BufferInfo"));
- jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "()V");
- jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
-
- obj = env->NewObject(clazz.get(), ctor);
-
+ obj = env->NewObject(gBufferInfo.clazz, gBufferInfo.ctorId);
if (obj == NULL) {
if (env->ExceptionCheck()) {
ALOGE("Could not create MediaCodec.BufferInfo.");
@@ -1107,7 +1103,7 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
return;
}
- env->CallVoidMethod(obj, method, (jint)offset, (jint)size, timeUs, flags);
+ env->CallVoidMethod(obj, gBufferInfo.setId, (jint)offset, (jint)size, timeUs, flags);
break;
}
@@ -3235,6 +3231,16 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) {
gDescriptorInfo.typeId = env->GetFieldID(clazz.get(), "mType", "I");
CHECK(gDescriptorInfo.typeId != NULL);
+
+ clazz.reset(env->FindClass("android/media/MediaCodec$BufferInfo"));
+ CHECK(clazz.get() != NULL);
+ gBufferInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+ gBufferInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V");
+ CHECK(gBufferInfo.ctorId != NULL);
+
+ gBufferInfo.setId = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
+ CHECK(gBufferInfo.setId != NULL);
}
static void android_media_MediaCodec_native_setup(
diff --git a/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default.xml b/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default.xml
new file mode 100644
index 000000000000..46abff81d38f
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_5g_plus_mobiledata_default.xml
@@ -0,0 +1,33 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="22"
+ android:viewportHeight="17"
+ android:width="22dp"
+ android:height="17dp">
+ <group>
+ <group>
+ <path android:fillColor="#FF000000"
+ android:pathData="M1.03 8.47l0.43-4.96h4.33v1.17H2.48L2.25 7.39C2.66 7.1 3.1 6.96 3.57 6.96c0.77 0 1.38 0.3 1.83 0.9 s0.66 1.41 0.66 2.43c0 1.03-0.24 1.84-0.72 2.43S4.2 13.6 3.36 13.6c-0.75 0-1.36-0.24-1.83-0.73s-0.74-1.16-0.81-2.02h1.13 c0.07 0.57 0.23 1 0.49 1.29s0.59 0.43 1.01 0.43c0.47 0 0.84-0.2 1.1-0.61c0.26-0.41 0.4-0.96 0.4-1.65 c0-0.65-0.14-1.18-0.43-1.59S3.76 8.09 3.28 8.09c-0.4 0-0.72 0.1-0.96 0.31L1.99 8.73L1.03 8.47z"/>
+ </group>
+ <group>
+ <path android:fillColor="#FF000000"
+ android:pathData="M 18.93,5.74 L 18.93,3.39 L 17.63,3.39 L 17.63,5.74 L 15.28,5.74 L 15.28,7.04 L 17.63,7.04 L 17.63,9.39 L 18.93,9.39 L 18.93,7.04 L 21.28,7.04 L 21.28,5.74 z"/>
+ </group>
+ <path android:fillColor="#FF000000"
+ android:pathData="M13.78 12.24l-0.22 0.27c-0.63 0.73-1.55 1.1-2.76 1.1c-1.08 0-1.92-0.36-2.53-1.07s-0.93-1.72-0.94-3.02V7.56 c0-1.39 0.28-2.44 0.84-3.13s1.39-1.04 2.51-1.04c0.95 0 1.69 0.26 2.23 0.79s0.83 1.28 0.89 2.26h-1.25 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72 0-1.24 0.23-1.57 0.7S8.6 6.37 8.59 7.4v2.03c0 1 0.19 1.77 0.57 2.31 c0.38 0.54 0.93 0.8 1.65 0.8c0.67 0 1.19-0.16 1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+ </group>
+</vector>
diff --git a/packages/SettingsLib/res/values/carrierid_icon_overrides.xml b/packages/SettingsLib/res/values/carrierid_icon_overrides.xml
new file mode 100644
index 000000000000..d2ae52d8347a
--- /dev/null
+++ b/packages/SettingsLib/res/values/carrierid_icon_overrides.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!--
+ ~ This resource file exists to enumerate all network type icon overrides on a
+ ~ per-carrierId basis
+-->
+<resources>
+ <!--
+ Network type (RAT) icon overrides can be configured here on a per-carrierId basis.
+ 1. Add a new TypedArray here, using the naming scheme below
+ 2. The entries are (NetworkType, drawable ID) pairs
+ 3. Add this array's ID to the MAPPING field of MobileIconCarrierIdOverrides.kt
+ -->
+ <array name="carrierId_2032_iconOverrides">
+ <item>5G_PLUS</item>
+ <item>@drawable/ic_5g_plus_mobiledata_default</item>
+ </array>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
index 5fa04f93e993..faea5b2c3e71 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt
@@ -412,14 +412,13 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int)
}
companion object {
- private const val TAG = "ThemedBatteryDrawable"
- private const val WIDTH = 12f
- private const val HEIGHT = 20f
+ const val WIDTH = 12f
+ const val HEIGHT = 20f
private const val CRITICAL_LEVEL = 15
// On a 12x20 grid, how wide to make the fill protection stroke.
// Scales when our size changes
private const val PROTECTION_STROKE_WIDTH = 3f
// Arbitrarily chosen for visibility at small sizes
- private const val PROTECTION_MIN_STROKE_WIDTH = 6f
+ const val PROTECTION_MIN_STROKE_WIDTH = 6f
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileIconCarrierIdOverrides.kt b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileIconCarrierIdOverrides.kt
new file mode 100644
index 000000000000..a0395b559291
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileIconCarrierIdOverrides.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.mobile
+
+import android.annotation.DrawableRes
+import android.content.res.Resources
+import android.content.res.TypedArray
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import com.android.settingslib.R
+import com.android.settingslib.SignalIcon.MobileIconGroup
+
+/**
+ * This class defines a network type (3G, 4G, etc.) override mechanism on a per-carrierId basis.
+ *
+ * Traditionally, carrier-customized network type iconography was achieved using the `MCC/MNC`
+ * resource qualifiers, and swapping out the drawable resource by name. It would look like this:
+ *
+ * res/
+ * drawable/
+ * 3g_mobiledata_icon.xml
+ * drawable-MCC-MNC/
+ * 3g_mobiledata_icon.xml
+ *
+ * This would mean that, provided a context created with this MCC/MNC configuration set, loading
+ * the network type icon through [MobileIconGroup] would provide a carrier-defined network type
+ * icon rather than the AOSP-defined default.
+ *
+ * The MCC/MNC mechanism no longer can fully define carrier-specific network type icons, because
+ * there is no longer a 1:1 mapping between MCC/MNC and carrier. With the advent of MVNOs, multiple
+ * carriers can have the same MCC/MNC value, but wish to differentiate based on their carrier ID.
+ * CarrierId is a newer concept than MCC/MNC, and provides more granularity when it comes to
+ * determining the carrier (e.g. MVNOs can share MCC/MNC values with the network owner), therefore
+ * it can fit all of the same use cases currently handled by `MCC/MNC`, without the need to apply a
+ * configuration context in order to get the proper UI for a given SIM icon.
+ *
+ * NOTE: CarrierId icon overrides will always take precedence over those defined using `MCC/MNC`
+ * resource qualifiers.
+ *
+ * [MAPPING] encodes the relationship between CarrierId and the corresponding override array
+ * that exists in the config.xml. An alternative approach could be to generate the resource name
+ * by string concatenation at run-time:
+ *
+ * val resName = "carrierId_$carrierId_iconOverrides"
+ * val override = resources.getResourceIdentifier(resName)
+ *
+ * However, that's going to be far less efficient until MAPPING grows to a sufficient size. For now,
+ * given a relatively small number of entries, we should just maintain the mapping here.
+ */
+interface MobileIconCarrierIdOverrides {
+ @DrawableRes
+ fun getOverrideFor(carrierId: Int, networkType: String, resources: Resources): Int
+ fun carrierIdEntryExists(carrierId: Int): Boolean
+}
+
+class MobileIconCarrierIdOverridesImpl : MobileIconCarrierIdOverrides {
+ @DrawableRes
+ override fun getOverrideFor(carrierId: Int, networkType: String, resources: Resources): Int {
+ val resId = MAPPING[carrierId] ?: return 0
+ val ta = resources.obtainTypedArray(resId)
+ val map = parseNetworkIconOverrideTypedArray(ta)
+ ta.recycle()
+ return map[networkType] ?: 0
+ }
+
+ override fun carrierIdEntryExists(carrierId: Int) =
+ overrideExists(carrierId, MAPPING)
+
+ companion object {
+ private const val TAG = "MobileIconOverrides"
+ /**
+ * This map maintains the lookup from the canonical carrier ID (see below link) to the
+ * corresponding overlay resource. New overrides should add an entry below in order to
+ * change the network type icon resources based on carrier ID
+ *
+ * Refer to the link below for the canonical mapping maintained in AOSP:
+ * https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb
+ */
+ private val MAPPING = mapOf(
+ // 2032 == Xfinity Mobile
+ 2032 to R.array.carrierId_2032_iconOverrides,
+ )
+
+ /**
+ * Parse `carrierId_XXXX_iconOverrides` for a particular network type. The resource file
+ * "carrierid_icon_overrides.xml" defines a TypedArray format for overriding specific
+ * network type icons (a.k.a. RAT icons) for a particular carrier ID. The format is defined
+ * as an array of (network type name, drawable) pairs:
+ * <array name="carrierId_XXXX_iconOverrides>
+ * <item>NET_TYPE_1</item>
+ * <item>@drawable/net_type_1_override</item>
+ * <item>NET_TYPE_2</item>
+ * <item>@drawable/net_type_2_override</item>
+ * </array>
+ *
+ * @param ta the [TypedArray] defined in carrierid_icon_overrides.xml
+ * @return the overridden drawable resource ID if it exists, or 0 if it does not
+ */
+ @VisibleForTesting
+ @JvmStatic
+ fun parseNetworkIconOverrideTypedArray(ta: TypedArray): Map<String, Int> {
+ if (ta.length() % 2 != 0) {
+ Log.w(TAG,
+ "override must contain an even number of (key, value) entries. skipping")
+
+ return mapOf()
+ }
+
+ val result = mutableMapOf<String, Int>()
+ // The array is defined as Pair(String, resourceId), so walk by 2
+ for (i in 0 until ta.length() step 2) {
+ val key = ta.getString(i)
+ val override = ta.getResourceId(i + 1, 0)
+ if (key == null || override == 0) {
+ Log.w(TAG, "Invalid override found. Skipping")
+ continue
+ }
+ result[key] = override
+ }
+
+ return result
+ }
+
+ @JvmStatic
+ private fun overrideExists(carrierId: Int, mapping: Map<Int, Int>): Boolean =
+ mapping.containsKey(carrierId)
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/mobile/MobileIconCarrierIdOverridesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/mobile/MobileIconCarrierIdOverridesTest.java
new file mode 100644
index 000000000000..740261d3bac2
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/mobile/MobileIconCarrierIdOverridesTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.mobile;
+
+import static com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl.parseNetworkIconOverrideTypedArray;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.res.TypedArray;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Map;
+
+@RunWith(RobolectricTestRunner.class)
+public final class MobileIconCarrierIdOverridesTest {
+ private static final String OVERRIDE_ICON_1_NAME = "name_1";
+ private static final int OVERRIDE_ICON_1_RES = 1;
+
+ private static final String OVERRIDE_ICON_2_NAME = "name_2";
+ private static final int OVERRIDE_ICON_2_RES = 2;
+
+ NetworkOverrideTypedArrayMock mResourceMock;
+
+ @Before
+ public void setUp() {
+ mResourceMock = new NetworkOverrideTypedArrayMock(
+ new String[] { OVERRIDE_ICON_1_NAME, OVERRIDE_ICON_2_NAME },
+ new int[] { OVERRIDE_ICON_1_RES, OVERRIDE_ICON_2_RES }
+ );
+ }
+
+ @Test
+ public void testParse_singleOverride() {
+ mResourceMock.setOverrides(
+ new String[] { OVERRIDE_ICON_1_NAME },
+ new int[] { OVERRIDE_ICON_1_RES }
+ );
+
+ Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
+
+ assertThat(parsed.get(OVERRIDE_ICON_1_NAME)).isEqualTo(OVERRIDE_ICON_1_RES);
+ }
+
+ @Test
+ public void testParse_multipleOverrides() {
+ mResourceMock.setOverrides(
+ new String[] { OVERRIDE_ICON_1_NAME, OVERRIDE_ICON_2_NAME },
+ new int[] { OVERRIDE_ICON_1_RES, OVERRIDE_ICON_2_RES }
+ );
+
+ Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
+
+ assertThat(parsed.get(OVERRIDE_ICON_2_NAME)).isEqualTo(OVERRIDE_ICON_2_RES);
+ assertThat(parsed.get(OVERRIDE_ICON_1_NAME)).isEqualTo(OVERRIDE_ICON_1_RES);
+ }
+
+ @Test
+ public void testParse_nonexistentKey_isNull() {
+ mResourceMock.setOverrides(
+ new String[] { OVERRIDE_ICON_1_NAME },
+ new int[] { OVERRIDE_ICON_1_RES }
+ );
+
+ Map<String, Integer> parsed = parseNetworkIconOverrideTypedArray(mResourceMock.getMock());
+
+ assertThat(parsed.get(OVERRIDE_ICON_2_NAME)).isNull();
+ }
+
+ static class NetworkOverrideTypedArrayMock {
+ private Object[] mInterleaved;
+
+ private final TypedArray mMockTypedArray = mock(TypedArray.class);
+
+ NetworkOverrideTypedArrayMock(
+ String[] networkTypes,
+ int[] iconOverrides) {
+
+ mInterleaved = interleaveTypes(networkTypes, iconOverrides);
+
+ doAnswer(invocation -> {
+ return mInterleaved[(int) invocation.getArgument(0)];
+ }).when(mMockTypedArray).getString(/* index */ anyInt());
+
+ doAnswer(invocation -> {
+ return mInterleaved[(int) invocation.getArgument(0)];
+ }).when(mMockTypedArray).getResourceId(/* index */ anyInt(), /* default */ anyInt());
+
+ when(mMockTypedArray.length()).thenAnswer(invocation -> {
+ return mInterleaved.length;
+ });
+ }
+
+ TypedArray getMock() {
+ return mMockTypedArray;
+ }
+
+ void setOverrides(String[] types, int[] resIds) {
+ mInterleaved = interleaveTypes(types, resIds);
+ }
+
+ private Object[] interleaveTypes(String[] strs, int[] ints) {
+ assertThat(strs.length).isEqualTo(ints.length);
+
+ Object[] ret = new Object[strs.length * 2];
+
+ // Keep track of where we are in the interleaved array, but iterate the overrides
+ int interleavedIndex = 0;
+ for (int i = 0; i < strs.length; i++) {
+ ret[interleavedIndex] = strs[i];
+ interleavedIndex += 1;
+ ret[interleavedIndex] = ints[i];
+ interleavedIndex += 1;
+ }
+ return ret;
+ }
+ }
+}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 70a464e2a190..9d7a9e7aac8a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -114,8 +114,8 @@ android_library {
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
+ "androidx.test.ext.junit",
"com.google.android.material_material",
- "kotlin-reflect",
"kotlinx_coroutines_android",
"kotlinx_coroutines",
"iconloader_base",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4267ba2ff0b7..68ff116be4b0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -195,6 +195,9 @@
<permission android:name="com.android.systemui.permission.FLAGS"
android:protectionLevel="signature" />
+ <permission android:name="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
+ android:protectionLevel="signature|privileged" />
+
<!-- Adding Quick Settings tiles -->
<uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />
@@ -976,5 +979,12 @@
<action android:name="com.android.systemui.action.DISMISS_VOLUME_PANEL_DIALOG" />
</intent-filter>
</receiver>
+
+ <provider
+ android:authorities="com.android.systemui.keyguard.quickaffordance"
+ android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+ android:exported="true"
+ android:permission="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
+ />
</application>
</manifest>
diff --git a/packages/SystemUI/compose/features/AndroidManifest.xml b/packages/SystemUI/compose/features/AndroidManifest.xml
index eada40e6a40d..278a89f7dba3 100644
--- a/packages/SystemUI/compose/features/AndroidManifest.xml
+++ b/packages/SystemUI/compose/features/AndroidManifest.xml
@@ -34,6 +34,11 @@
android:enabled="false"
tools:replace="android:authorities"
tools:node="remove" />
+ <provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+ android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
+ android:enabled="false"
+ tools:replace="android:authorities"
+ tools:node="remove" />
<provider android:name="com.android.keyguard.clock.ClockOptionsProvider"
android:authorities="com.android.systemui.test.keyguard.clock.disabled"
android:enabled="false"
diff --git a/packages/SystemUI/res-keyguard/values-land/dimens.xml b/packages/SystemUI/res-keyguard/values-land/dimens.xml
index a4e7a5f12db4..f1aa54412b3b 100644
--- a/packages/SystemUI/res-keyguard/values-land/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-land/dimens.xml
@@ -27,4 +27,6 @@
<integer name="scaled_password_text_size">26</integer>
<dimen name="bouncer_user_switcher_y_trans">@dimen/status_bar_height</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_user_switcher_bottom_margin">0dp</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_view_flipper_bottom_margin">0dp</dimen>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 0a55cf779683..3861d983b309 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -124,6 +124,8 @@
<dimen name="bouncer_user_switcher_item_padding_horizontal">12dp</dimen>
<dimen name="bouncer_user_switcher_header_padding_end">44dp</dimen>
<dimen name="bouncer_user_switcher_y_trans">0dp</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_user_switcher_bottom_margin">0dp</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_view_flipper_bottom_margin">0dp</dimen>
<!-- 2 * the margin + size should equal the plus_margin -->
<dimen name="user_switcher_icon_large_margin">16dp</dimen>
diff --git a/packages/SystemUI/res/drawable/internet_dialog_selected_effect.xml b/packages/SystemUI/res/drawable/internet_dialog_selected_effect.xml
new file mode 100644
index 000000000000..8f6b4c246ba4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_selected_effect.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 5b961595fbd3..f14be410bf75 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -190,6 +190,11 @@
</LinearLayout>
+ <ViewStub android:id="@+id/secondary_mobile_network_stub"
+ android:inflatedId="@+id/secondary_mobile_network_layout"
+ android:layout="@layout/qs_dialog_secondary_mobile_network"
+ style="@style/InternetDialog.Network"/>
+
<LinearLayout
android:id="@+id/turn_on_wifi_layout"
style="@style/InternetDialog.Network"
@@ -307,22 +312,15 @@
<LinearLayout
android:id="@+id/see_all_layout"
- android:layout_width="match_parent"
+ style="@style/InternetDialog.Network"
android:layout_height="64dp"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="center_vertical|center_horizontal"
- android:orientation="horizontal"
- android:paddingStart="22dp"
- android:paddingEnd="22dp">
+ android:paddingStart="20dp">
<FrameLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:clickable="false"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+ android:layout_gravity="center_vertical|start">
<ImageView
android:id="@+id/arrow_forward"
android:src="@drawable/ic_arrow_forward"
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 9b8b611558fe..530db0d0304a 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -44,7 +44,7 @@
android:background="@drawable/qs_media_outline_album_bg"
/>
- <com.android.systemui.ripple.MultiRippleView
+ <com.android.systemui.surfaceeffects.ripple.MultiRippleView
android:id="@+id/touch_ripple_view"
android:layout_width="match_parent"
android:layout_height="@dimen/qs_media_session_height_expanded"
@@ -53,6 +53,15 @@
app:layout_constraintTop_toTopOf="@id/album_art"
app:layout_constraintBottom_toBottomOf="@id/album_art" />
+ <com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
+ android:id="@+id/turbulence_noise_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_expanded"
+ app:layout_constraintStart_toStartOf="@id/album_art"
+ app:layout_constraintEnd_toEndOf="@id/album_art"
+ app:layout_constraintTop_toTopOf="@id/album_art"
+ app:layout_constraintBottom_toBottomOf="@id/album_art" />
+
<!-- Guideline for output switcher -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/center_vertical_guideline"
diff --git a/packages/SystemUI/res/layout/qs_dialog_secondary_mobile_network.xml b/packages/SystemUI/res/layout/qs_dialog_secondary_mobile_network.xml
new file mode 100644
index 000000000000..4592c5e6332c
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_dialog_secondary_mobile_network.xml
@@ -0,0 +1,63 @@
+<?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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/InternetDialog.Network">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:clickable="false"
+ android:layout_gravity="center_vertical|start">
+ <ImageView
+ android:id="@+id/secondary_signal_icon"
+ android:autoMirrored="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="start|center_vertical">
+ <TextView
+ android:id="@+id/secondary_mobile_title"
+ android:maxLines="1"
+ style="@style/InternetDialog.NetworkTitle"/>
+ <TextView
+ android:id="@+id/secondary_mobile_summary"
+ style="@style/InternetDialog.NetworkSummary"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="match_parent"
+ android:clickable="false"
+ android:layout_gravity="end|center_vertical"
+ android:gravity="center">
+ <ImageView
+ android:id="@+id/secondary_settings_icon"
+ android:src="@drawable/ic_settings_24dp"
+ android:layout_width="24dp"
+ android:layout_gravity="end|center_vertical"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/screen_record_options.xml b/packages/SystemUI/res/layout/screen_record_options.xml
new file mode 100644
index 000000000000..d6c9e98d8b4d
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_record_options.xml
@@ -0,0 +1,87 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <ImageView
+ android:layout_width="@dimen/screenrecord_option_icon_size"
+ android:layout_height="@dimen/screenrecord_option_icon_size"
+ android:src="@drawable/ic_mic_26dp"
+ android:tint="?android:attr/textColorSecondary"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="0"
+ android:layout_marginRight="@dimen/screenrecord_option_padding"
+ android:importantForAccessibility="no"/>
+ <Spinner
+ android:id="@+id/screen_recording_options"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:layout_weight="1"
+ android:popupBackground="@drawable/screenrecord_spinner_background"
+ android:dropDownWidth="274dp"
+ android:importantForAccessibility="yes"/>
+ <Switch
+ android:layout_width="wrap_content"
+ android:minWidth="48dp"
+ android:layout_height="48dp"
+ android:layout_weight="0"
+ android:layout_gravity="end"
+ android:id="@+id/screenrecord_audio_switch"
+ style="@style/ScreenRecord.Switch"
+ android:importantForAccessibility="yes"/>
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/show_taps"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="@dimen/screenrecord_option_padding">
+ <ImageView
+ android:layout_width="@dimen/screenrecord_option_icon_size"
+ android:layout_height="@dimen/screenrecord_option_icon_size"
+ android:layout_weight="0"
+ android:src="@drawable/ic_touch"
+ android:tint="?android:attr/textColorSecondary"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="@dimen/screenrecord_option_padding"
+ android:importantForAccessibility="no"/>
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:minHeight="48dp"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:text="@string/screenrecord_taps_label"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:textColor="?android:attr/textColorPrimary"
+ android:contentDescription="@string/screenrecord_taps_label"/>
+ <Switch
+ android:layout_width="wrap_content"
+ android:minWidth="48dp"
+ android:layout_height="48dp"
+ android:layout_weight="0"
+ android:id="@+id/screenrecord_taps_switch"
+ style="@style/ScreenRecord.Switch"
+ android:importantForAccessibility="yes"/>
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
new file mode 100644
index 000000000000..ac46cdb1be24
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -0,0 +1,94 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!-- Scrollview is necessary to fit everything in landscape layout -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/screen_share_permission_dialog"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ android:paddingTop="@dimen/dialog_top_padding"
+ android:paddingBottom="@dimen/dialog_bottom_padding"
+ android:orientation="vertical"
+ android:gravity="center_horizontal">
+
+ <ImageView
+ android:layout_width="@dimen/screenrecord_logo_size"
+ android:layout_height="@dimen/screenrecord_logo_size"
+ android:src="@drawable/ic_screenrecord"
+ android:tint="@color/screenrecord_icon_color"
+ android:importantForAccessibility="no"/>
+ <TextView
+ android:id="@+id/screen_share_dialog_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:layout_marginTop="22dp"
+ android:layout_marginBottom="15dp"/>
+ <Spinner
+ android:id="@+id/screen_share_mode_spinner"
+ android:layout_width="320dp"
+ android:layout_height="72dp"
+ android:layout_marginTop="24dp"
+ android:layout_marginBottom="24dp" />
+ <ViewStub
+ android:id="@+id/options_stub"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/text_warning"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/screenrecord_description"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:textColorSecondary"
+ android:gravity="start"
+ android:layout_marginBottom="20dp"/>
+
+ <!-- Buttons -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="36dp">
+ <TextView
+ android:id="@+id/button_cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/cancel"
+ style="@style/Widget.Dialog.Button.BorderButton" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+ <TextView
+ android:id="@+id/button_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/screenrecord_start"
+ style="@style/Widget.Dialog.Button" />
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index 1ac78d491d78..88429925eed0 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -44,7 +44,7 @@
app:layout_constraintHorizontal_bias="0"
app:layout_constraintWidth_percent="1.0"
app:layout_constraintWidth_max="wrap"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/screenshot_message_container"
app:layout_constraintStart_toEndOf="@+id/screenshot_preview_border"
app:layout_constraintEnd_toEndOf="parent">
<LinearLayout
@@ -70,7 +70,7 @@
android:alpha="0"
android:background="@drawable/overlay_border"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/screenshot_message_container"
app:layout_constraintEnd_toEndOf="@id/screenshot_preview_end"
app:layout_constraintTop_toTopOf="@id/screenshot_preview_top"/>
<androidx.constraintlayout.widget.Barrier
@@ -142,4 +142,41 @@
app:layout_constraintStart_toStartOf="@id/screenshot_preview"
app:layout_constraintTop_toTopOf="@id/screenshot_preview"
android:elevation="7dp"/>
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/screenshot_message_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
+ android:layout_marginVertical="4dp"
+ android:paddingHorizontal="@dimen/overlay_action_container_padding_right"
+ android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
+ android:elevation="4dp"
+ android:background="@drawable/action_chip_container_background"
+ android:visibility="gone"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent">
+
+ <ImageView
+ android:id="@+id/screenshot_message_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:paddingEnd="4dp"
+ android:src="@drawable/ic_work_app_badge"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
+
+ <TextView
+ android:id="@+id/screenshot_message_content"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/screenshot_message_icon"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
</com.android.systemui.screenshot.DraggableConstraintLayout>
diff --git a/packages/SystemUI/res/layout/wireless_charging_layout.xml b/packages/SystemUI/res/layout/wireless_charging_layout.xml
index 887e3e715369..f1bc88370071 100644
--- a/packages/SystemUI/res/layout/wireless_charging_layout.xml
+++ b/packages/SystemUI/res/layout/wireless_charging_layout.xml
@@ -22,7 +22,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.systemui.ripple.RippleView
+ <com.android.systemui.surfaceeffects.ripple.RippleView
android:id="@+id/wireless_charging_ripple"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
index d9df3373bef1..707bc9e535ff 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -17,7 +17,6 @@
<resources>
<dimen name="notification_panel_margin_horizontal">48dp</dimen>
<dimen name="status_view_margin_horizontal">62dp</dimen>
- <dimen name="bouncer_user_switcher_y_trans">20dp</dimen>
<!-- qs_tiles_page_horizontal_margin should be margin / 2, otherwise full space between two
pages is margin * 2, and that makes tiles page not appear immediately after user swipes to
diff --git a/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml b/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
index 97ead01669a9..b98165fb08f0 100644
--- a/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
@@ -21,4 +21,6 @@
<!-- Space between status view and notification shelf -->
<dimen name="keyguard_status_view_bottom_margin">70dp</dimen>
<dimen name="keyguard_clock_top_margin">80dp</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_user_switcher_bottom_margin">186dp</dimen>
+ <dimen name="bouncer_user_switcher_view_mode_view_flipper_bottom_margin">110dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
index 17f82b50d7be..8b41a44b9ba3 100644
--- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -21,7 +21,6 @@
for different hardware and product builds. -->
<resources>
<dimen name="status_view_margin_horizontal">124dp</dimen>
- <dimen name="bouncer_user_switcher_y_trans">200dp</dimen>
<dimen name="large_screen_shade_header_left_padding">24dp</dimen>
<dimen name="qqs_layout_padding_bottom">40dp</dimen>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ce9829b318cd..55d637916d0c 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -485,6 +485,12 @@
<!-- Whether to show a severe low battery dialog. -->
<bool name="config_severe_battery_dialog">false</bool>
+ <!-- A path representing a shield. Will sometimes be displayed with the battery icon when
+ needed. This path is a 10px wide and 13px tall. -->
+ <string name="config_batterymeterShieldPath" translatable="false">
+ M5 0L0 1.88V6.19C0 9.35 2.13 12.29 5 13.01C7.87 12.29 10 9.35 10 6.19V1.88L5 0Z
+ </string>
+
<!-- A path similar to frameworks/base/core/res/res/values/config.xml
config_mainBuiltInDisplayCutout that describes a path larger than the exact path of a display
cutout. If present as well as config_enableDisplayCutoutProtection is set to true, then
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 771d0d19708a..c78d36dd3685 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -105,6 +105,12 @@
so the width of the icon should be 13.0dp * (12.0 / 20.0) -->
<dimen name="status_bar_battery_icon_width">7.8dp</dimen>
+ <!-- The battery icon is 13dp tall, but the other system icons are 15dp tall (see
+ @*android:dimen/status_bar_system_icon_size) with some top and bottom padding embedded in
+ the drawables themselves. So, the battery icon may need an extra 1dp of spacing so that its
+ bottom still aligns with the bottom of all the other system icons. See b/258672854. -->
+ <dimen name="status_bar_battery_extra_vertical_spacing">1dp</dimen>
+
<!-- The font size for the clock in the status bar. -->
<dimen name="status_bar_clock_size">14sp</dimen>
@@ -762,7 +768,7 @@
<dimen name="keyguard_lock_padding">20dp</dimen>
<dimen name="keyguard_indication_margin_bottom">32dp</dimen>
- <dimen name="lock_icon_margin_bottom">110dp</dimen>
+ <dimen name="lock_icon_margin_bottom">74dp</dimen>
<dimen name="ambient_indication_margin_bottom">71dp</dimen>
@@ -1478,10 +1484,12 @@
<!-- Dream overlay complications related dimensions -->
<dimen name="dream_overlay_complication_clock_time_text_size">86sp</dimen>
+ <dimen name="dream_overlay_complication_clock_time_padding">20dp</dimen>
<dimen name="dream_overlay_complication_clock_subtitle_text_size">24sp</dimen>
<dimen name="dream_overlay_complication_preview_text_size">36sp</dimen>
<dimen name="dream_overlay_complication_preview_icon_padding">28dp</dimen>
<dimen name="dream_overlay_complication_shadow_padding">2dp</dimen>
+ <dimen name="dream_overlay_complication_smartspace_padding">24dp</dimen>
<!-- The position of the end guide, which dream overlay complications can align their start with
if their end is aligned with the parent end. Represented as the percentage over from the
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c88da1895e8d..4146e20cb345 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -235,6 +235,8 @@
<string name="screenshot_left_boundary_pct">Left boundary <xliff:g id="percent" example="50">%1$d</xliff:g> percent</string>
<!-- Content description for the right boundary of the screenshot being cropped, with the current position as a percentage. [CHAR LIMIT=NONE] -->
<string name="screenshot_right_boundary_pct">Right boundary <xliff:g id="percent" example="50">%1$d</xliff:g> percent</string>
+ <!-- Notification displayed when a screenshot is saved in a work profile. [CHAR LIMIT=NONE] -->
+ <string name="screenshot_work_profile_notification" translatable="false">Work screenshots are saved in the work <xliff:g id="app" example="Files">%1$s</xliff:g> app</string>
<!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
<string name="screenrecord_name">Screen Recorder</string>
@@ -314,7 +316,7 @@
<!-- Content description of the QR Code scanner for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_qr_code_scanner_button">QR Code Scanner</string>
<!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_unlock_button">Unlock</string>
+ <string name="accessibility_unlock_button">Unlocked</string>
<!-- Content description of the lock icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_lock_icon">Device locked</string>
<!-- Content description hint of the unlock button when fingerprint is on (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -403,7 +405,7 @@
<string name="keyguard_face_failed">Can\u2019t recognize face</string>
<!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] -->
<string name="keyguard_suggest_fingerprint">Use fingerprint instead</string>
- <!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=25] -->
+ <!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=65] -->
<string name="keyguard_face_unlock_unavailable">Face unlock unavailable.</string>
<!-- Content description of the bluetooth icon when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -437,11 +439,17 @@
<string name="accessibility_battery_level">Battery <xliff:g id="number">%d</xliff:g> percent.</string>
<!-- Content description of the battery level icon for accessibility, including the estimated time remaining before the phone runs out of battery (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_battery_level_with_estimate">Battery <xliff:g id="percentage" example="95%">%1$s</xliff:g> percent, about <xliff:g id="time" example="Until 3:15pm">%2$s</xliff:g> left based on your usage</string>
+ <string name="accessibility_battery_level_with_estimate">Battery <xliff:g id="percentage" example="95%">%1$d</xliff:g> percent, about <xliff:g id="time" example="Until 3:15pm">%2$s</xliff:g> left based on your usage</string>
<!-- Content description of the battery level icon for accessibility while the device is charging (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_battery_level_charging">Battery charging, <xliff:g id="battery_percentage">%d</xliff:g> percent.</string>
+ <!-- Content description of the battery level icon for accessibility, with information that the device charging is paused in order to protect the lifetime of the battery (not shown on screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_battery_level_charging_paused">Battery <xliff:g id="percentage" example="90%">%d</xliff:g> percent. Charging paused for battery protection.</string>
+
+ <!-- Content description of the battery level icon for accessibility, including the estimated time remaining before the phone runs out of battery *and* information that the device charging is paused in order to protect the lifetime of the battery (not shown on screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_battery_level_charging_paused_with_estimate">Battery <xliff:g id="percentage" example="90%">%1$d</xliff:g> percent, about <xliff:g id="time" example="Until 3:15pm">%2$s</xliff:g> left based on your usage. Charging paused for battery protection.</string>
+
<!-- Content description of overflow icon container of the notifications for accessibility (not shown on the screen)[CHAR LIMIT=NONE] -->
<string name="accessibility_overflow_action">See all notifications</string>
@@ -1311,7 +1319,7 @@
<string name="wallet_lockscreen_settings_label">Lock screen settings</string>
<!-- QR Code Scanner label, title [CHAR LIMIT=32] -->
- <string name="qr_code_scanner_title">Scan QR code</string>
+ <string name="qr_code_scanner_title">QR code scanner</string>
<!-- Name of the work status bar icon. -->
<string name="status_bar_work">Work profile</string>
@@ -2039,6 +2047,15 @@
<!-- Text used to refer to the user's current carrier in mobile_data_disable_message if the users's mobile network carrier name is not available [CHAR LIMIT=NONE] -->
<string name="mobile_data_disable_message_default_carrier">your carrier</string>
+ <!-- Title of the dialog to turn off data usage [CHAR LIMIT=NONE] -->
+ <string name="auto_data_switch_disable_title">Switch back to <xliff:g id="carrier" example="T-Mobile">%s</xliff:g>?</string>
+ <!-- Message body of the dialog to turn off data usage [CHAR LIMIT=NONE] -->
+ <string name="auto_data_switch_disable_message">Mobile data won\’t automatically switch based on availability</string>
+ <!-- Negative button title of the quick settings switch back to DDS dialog [CHAR LIMIT=NONE] -->
+ <string name="auto_data_switch_dialog_negative_button">No thanks</string>
+ <!-- Positive button title of the quick settings switch back to DDS dialog [CHAR LIMIT=NONE] -->
+ <string name="auto_data_switch_dialog_positive_button">Yes, switch</string>
+
<!-- Warning shown when user input has been blocked due to another app overlaying screen
content. Since we don't know what the app is showing on top of the input target, we
can't verify user consent. [CHAR LIMIT=NONE] -->
@@ -2509,6 +2526,12 @@
Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
<string name="mobile_data_connection_active">Connected</string>
<!-- Provider Model:
+ Summary indicating that a SIM is temporarily connected to mobile data [CHAR LIMIT=50] -->
+ <string name="mobile_data_temp_connection_active">Temporarily connected</string>
+ <!-- Provider Model:
+ Summary indicating that a SIM is temporarily connected to mobile data [CHAR LIMIT=50] -->
+ <string name="mobile_data_poor_connection">Poor connection</string>
+ <!-- Provider Model:
Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] -->
<string name="mobile_data_off_summary">Mobile data won\u0027t auto\u2011connect</string>
<!-- Provider Model:
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ff29039a962f..dea06b7a00e6 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1095,7 +1095,7 @@
<item name="android:orientation">horizontal</item>
<item name="android:focusable">true</item>
<item name="android:clickable">true</item>
- <item name="android:background">?android:attr/selectableItemBackground</item>
+ <item name="android:background">@drawable/internet_dialog_selected_effect</item>
</style>
<style name="InternetDialog.NetworkTitle">
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index 148e5ec1606f..1eb621e0368b 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -44,6 +44,16 @@
app:layout_constraintTop_toTopOf="@+id/album_art"
app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+ <!-- Turbulence noise must have the same constraint as the album art. -->
+ <Constraint
+ android:id="@+id/turbulence_noise_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_collapsed"
+ app:layout_constraintStart_toStartOf="@+id/album_art"
+ app:layout_constraintEnd_toEndOf="@+id/album_art"
+ app:layout_constraintTop_toTopOf="@+id/album_art"
+ app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+
<Constraint
android:id="@+id/header_title"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml
index ac484d7dde8e..64c2ef1fc915 100644
--- a/packages/SystemUI/res/xml/media_session_expanded.xml
+++ b/packages/SystemUI/res/xml/media_session_expanded.xml
@@ -37,6 +37,16 @@
app:layout_constraintTop_toTopOf="@+id/album_art"
app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+ <!-- Turbulence noise must have the same constraint as the album art. -->
+ <Constraint
+ android:id="@+id/turbulence_noise_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_expanded"
+ app:layout_constraintStart_toStartOf="@+id/album_art"
+ app:layout_constraintEnd_toEndOf="@+id/album_art"
+ app:layout_constraintTop_toTopOf="@+id/album_art"
+ app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+
<Constraint
android:id="@+id/header_title"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml
index af4be1ade656..5d3650ccc8e6 100644
--- a/packages/SystemUI/res/xml/qqs_header.xml
+++ b/packages/SystemUI/res/xml/qqs_header.xml
@@ -25,7 +25,7 @@
android:id="@+id/clock">
<Layout
android:layout_width="wrap_content"
- android:layout_height="0dp"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintStart_toStartOf="@id/begin_guide"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
@@ -42,7 +42,7 @@
<Constraint
android:id="@+id/date">
<Layout
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
android:layout_marginStart="8dp"
app:layout_constrainedWidth="true"
@@ -57,14 +57,16 @@
<Constraint
android:id="@+id/statusIcons">
<Layout
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
+ app:layout_constrainedWidth="true"
app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height"
app:layout_constraintStart_toEndOf="@id/date"
app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintHorizontal_chainStyle="packed"
/>
</Constraint>
@@ -80,12 +82,16 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintHorizontal_chainStyle="packed"
/>
</Constraint>
<Constraint
android:id="@+id/carrier_group">
<Layout
+ app:layout_constraintWidth_min="48dp"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
diff --git a/packages/SystemUI/res/xml/qs_header_new.xml b/packages/SystemUI/res/xml/qs_header_new.xml
index d8a4e7752960..982c422f1fda 100644
--- a/packages/SystemUI/res/xml/qs_header_new.xml
+++ b/packages/SystemUI/res/xml/qs_header_new.xml
@@ -43,6 +43,7 @@
app:layout_constraintBottom_toBottomOf="@id/carrier_group"
app:layout_constraintEnd_toStartOf="@id/carrier_group"
app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
<Transform
android:scaleX="2.57"
@@ -53,7 +54,7 @@
<Constraint
android:id="@+id/date">
<Layout
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/space"
@@ -67,16 +68,15 @@
<Constraint
android:id="@+id/carrier_group">
<Layout
- app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
- android:minHeight="@dimen/large_screen_shade_header_min_height"
app:layout_constraintWidth_min="48dp"
- android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintStart_toEndOf="@id/clock"
app:layout_constraintTop_toBottomOf="@id/privacy_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintBottom_toTopOf="@id/batteryRemainingIcon"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
<PropertySet
android:alpha="1"
@@ -86,7 +86,7 @@
<Constraint
android:id="@+id/statusIcons">
<Layout
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
app:layout_constrainedWidth="true"
app:layout_constraintStart_toEndOf="@id/space"
@@ -108,6 +108,7 @@
app:layout_constraintTop_toTopOf="@id/date"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
</Constraint>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 485a0d320bb9..b679cfa145d4 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -52,23 +52,14 @@ android_library {
"SystemUIUnfoldLib",
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
- "androidx.lifecycle_lifecycle-runtime-ktx",
- "androidx.lifecycle_lifecycle-viewmodel-ktx",
- "androidx.recyclerview_recyclerview",
- "kotlinx_coroutines_android",
- "kotlinx_coroutines",
"dagger2",
"jsr330",
],
resource_dirs: [
"res",
],
- optimize: {
- proguard_flags_files: ["proguard.flags"],
- },
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
- kotlincflags: ["-Xjvm-default=enable"],
}
java_library {
diff --git a/packages/SystemUI/shared/proguard.flags b/packages/SystemUI/shared/proguard.flags
deleted file mode 100644
index 5eda04500190..000000000000
--- a/packages/SystemUI/shared/proguard.flags
+++ /dev/null
@@ -1,4 +0,0 @@
-# Retain signatures of TypeToken and its subclasses for gson usage in ClockRegistry
--keepattributes Signature
--keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
--keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index 933e58639ccf..196f7f05d20d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -22,9 +22,19 @@ import android.annotation.StringRes
import android.os.Parcel
import android.os.Parcelable
+/**
+ * Base interface for flags that can change value on a running device.
+ * @property id unique id to help identify this flag. Must be unique. This will be removed soon.
+ * @property teamfood Set to true to include this flag as part of the teamfood flag. This will
+ * be removed soon.
+ * @property name Used for server-side flagging where appropriate. Also used for display. No spaces.
+ * @property namespace The server-side namespace that this flag lives under.
+ */
interface Flag<T> {
val id: Int
val teamfood: Boolean
+ val name: String
+ val namespace: String
}
interface ParcelableFlag<T> : Flag<T>, Parcelable {
@@ -38,13 +48,10 @@ interface ResourceFlag<T> : Flag<T> {
}
interface DeviceConfigFlag<T> : Flag<T> {
- val name: String
- val namespace: String
val default: T
}
interface SysPropFlag<T> : Flag<T> {
- val name: String
val default: T
}
@@ -56,6 +63,8 @@ interface SysPropFlag<T> : Flag<T> {
// Consider using the "parcelize" kotlin library.
abstract class BooleanFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val default: Boolean = false,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -71,6 +80,8 @@ abstract class BooleanFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readBoolean(),
teamfood = parcel.readBoolean(),
overridden = parcel.readBoolean()
@@ -78,6 +89,8 @@ abstract class BooleanFlag constructor(
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeBoolean(default)
parcel.writeBoolean(teamfood)
parcel.writeBoolean(overridden)
@@ -91,20 +104,24 @@ abstract class BooleanFlag constructor(
*/
data class UnreleasedFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
-) : BooleanFlag(id, false, teamfood, overridden)
+) : BooleanFlag(id, name, namespace, false, teamfood, overridden)
/**
- * A Flag that is is true by default.
+ * A Flag that is true by default.
*
* It can be changed or overridden in any build, meaning it can be turned off if needed.
*/
data class ReleasedFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
-) : BooleanFlag(id, true, teamfood, overridden)
+) : BooleanFlag(id, name, namespace, true, teamfood, overridden)
/**
* A Flag that reads its default values from a resource overlay instead of code.
@@ -113,6 +130,8 @@ data class ReleasedFlag constructor(
*/
data class ResourceBooleanFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
@BoolRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Boolean>
@@ -142,7 +161,8 @@ data class DeviceConfigBooleanFlag constructor(
data class SysPropBooleanFlag constructor(
override val id: Int,
override val name: String,
- override val default: Boolean = false
+ override val namespace: String,
+ override val default: Boolean = false,
) : SysPropFlag<Boolean> {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet.
override val teamfood: Boolean = false
@@ -150,6 +170,8 @@ data class SysPropBooleanFlag constructor(
data class StringFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val default: String = "",
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -164,23 +186,31 @@ data class StringFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readString() ?: ""
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeString(default)
}
}
data class ResourceStringFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
@StringRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<String>
data class IntFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val default: Int = 0,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -196,17 +226,23 @@ data class IntFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readInt()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeInt(default)
}
}
data class ResourceIntFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
@IntegerRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Int>
@@ -215,6 +251,8 @@ data class LongFlag constructor(
override val id: Int,
override val default: Long = 0,
override val teamfood: Boolean = false,
+ override val name: String,
+ override val namespace: String,
override val overridden: Boolean = false
) : ParcelableFlag<Long> {
@@ -228,17 +266,23 @@ data class LongFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readLong()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeLong(default)
}
}
data class FloatFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val default: Float = 0f,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -254,23 +298,31 @@ data class FloatFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readFloat()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeFloat(default)
}
}
data class ResourceFloatFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val resourceId: Int,
- override val teamfood: Boolean = false
+ override val teamfood: Boolean = false,
) : ResourceFlag<Int>
data class DoubleFlag constructor(
override val id: Int,
+ override val name: String,
+ override val namespace: String,
override val default: Double = 0.0,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -286,11 +338,15 @@ data class DoubleFlag constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
+ name = parcel.readString(),
+ namespace = parcel.readString(),
default = parcel.readDouble()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
+ parcel.writeString(name)
+ parcel.writeString(namespace)
parcel.writeDouble(default)
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt
new file mode 100644
index 000000000000..c2658a9e61b1
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.shared.keyguard.data.content
+
+import android.content.ContentResolver
+import android.net.Uri
+
+/** Contract definitions for querying content about keyguard quick affordances. */
+object KeyguardQuickAffordanceProviderContract {
+
+ const val AUTHORITY = "com.android.systemui.keyguard.quickaffordance"
+ const val PERMISSION = "android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
+
+ private val BASE_URI: Uri =
+ Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build()
+
+ /**
+ * Table for slots.
+ *
+ * Slots are positions where affordances can be placed on the lock screen. Affordances that are
+ * placed on slots are said to be "selected". The system supports the idea of multiple
+ * affordances per slot, though the implementation may limit the number of affordances on each
+ * slot.
+ *
+ * Supported operations:
+ * - Query - to know which slots are available, query the [SlotTable.URI] [Uri]. The result set
+ * will contain rows with the [SlotTable.Columns] columns.
+ */
+ object SlotTable {
+ const val TABLE_NAME = "slots"
+ val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
+
+ object Columns {
+ /** String. Unique ID for this slot. */
+ const val ID = "id"
+ /** Integer. The maximum number of affordances that can be placed in the slot. */
+ const val CAPACITY = "capacity"
+ }
+ }
+
+ /**
+ * Table for affordances.
+ *
+ * Affordances are actions/buttons that the user can execute. They are placed on slots on the
+ * lock screen.
+ *
+ * Supported operations:
+ * - Query - to know about all the affordances that are available on the device, regardless of
+ * which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result set will
+ * contain rows, each with the columns specified in [AffordanceTable.Columns].
+ */
+ object AffordanceTable {
+ const val TABLE_NAME = "affordances"
+ val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
+
+ object Columns {
+ /** String. Unique ID for this affordance. */
+ const val ID = "id"
+ /** String. User-visible name for this affordance. */
+ const val NAME = "name"
+ /**
+ * Integer. Resource ID for the drawable to load for this affordance. This is a resource
+ * ID from the system UI package.
+ */
+ const val ICON = "icon"
+ }
+ }
+
+ /**
+ * Table for selections.
+ *
+ * Selections are pairs of slot and affordance IDs.
+ *
+ * Supported operations:
+ * - Insert - to insert an affordance and place it in a slot, insert values for the columns into
+ * the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the system.
+ * Selecting a new affordance for a slot that is already full will automatically remove the
+ * oldest affordance from the slot.
+ * - Query - to know which affordances are set on which slots, query the [SelectionTable.URI]
+ * [Uri]. The result set will contain rows, each of which with the columns from
+ * [SelectionTable.Columns].
+ * - Delete - to unselect an affordance, removing it from a slot, delete from the
+ * [SelectionTable.URI] [Uri], passing in values for each column.
+ */
+ object SelectionTable {
+ const val TABLE_NAME = "selections"
+ val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
+
+ object Columns {
+ /** String. Unique ID for the slot. */
+ const val SLOT_ID = "slot_id"
+ /** String. Unique ID for the selected affordance. */
+ const val AFFORDANCE_ID = "affordance_id"
+ }
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 647dd47159e0..08904658a27d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -20,7 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
@@ -255,7 +255,8 @@ public class Task {
// Also consider undefined activity type to include tasks in overview right after rebooting
// the device.
final boolean isDockable = taskInfo.supportsMultiWindow
- && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode())
+ && ArrayUtils.contains(
+ CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, taskInfo.getWindowingMode())
&& (taskInfo.getActivityType() == ACTIVITY_TYPE_UNDEFINED
|| ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType()));
return new Task(taskKey,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index 40c8774d4f34..a790d89ac1ae 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -61,26 +61,42 @@ public class PreviewPositionHelper {
* Updates the matrix based on the provided parameters
*/
public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
- int canvasWidth, int canvasHeight, int screenWidthPx, int taskbarSize, boolean isTablet,
+ int canvasWidth, int canvasHeight, int screenWidthPx, int screenHeightPx,
+ int taskbarSize, boolean isTablet,
int currentRotation, boolean isRtl) {
boolean isRotated = false;
boolean isOrientationDifferent;
- float fullscreenTaskWidth = screenWidthPx;
- if (mSplitBounds != null && !mSplitBounds.appsStackedVertically) {
- // For landscape, scale the width
- float taskPercent = mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? mSplitBounds.leftTaskPercent
- : (1 - (mSplitBounds.leftTaskPercent + mSplitBounds.dividerWidthPercent));
- // Scale landscape width to that of actual screen
- fullscreenTaskWidth = screenWidthPx * taskPercent;
- }
int thumbnailRotation = thumbnailData.rotation;
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
RectF thumbnailClipHint = new RectF();
- float canvasScreenRatio = canvasWidth / fullscreenTaskWidth;
- float scaledTaskbarSize = taskbarSize * canvasScreenRatio;
- thumbnailClipHint.bottom = isTablet ? scaledTaskbarSize : 0;
+
+ float scaledTaskbarSize = 0;
+ if (mSplitBounds != null) {
+ float fullscreenTaskWidth;
+ float fullscreenTaskHeight;
+ float canvasScreenRatio;
+
+ float taskPercent;
+ if (!mSplitBounds.appsStackedVertically) {
+ // For landscape, scale the width
+ taskPercent = mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? mSplitBounds.leftTaskPercent
+ : (1 - (mSplitBounds.leftTaskPercent + mSplitBounds.dividerWidthPercent));
+ // Scale landscape width to that of actual screen
+ fullscreenTaskWidth = screenWidthPx * taskPercent;
+ canvasScreenRatio = canvasWidth / fullscreenTaskWidth;
+ } else {
+ taskPercent = mDesiredStagePosition != STAGE_POSITION_TOP_OR_LEFT
+ ? mSplitBounds.leftTaskPercent
+ : (1 - (mSplitBounds.leftTaskPercent + mSplitBounds.dividerWidthPercent));
+ // Scale landscape width to that of actual screen
+ fullscreenTaskHeight = screenHeightPx * taskPercent;
+ canvasScreenRatio = canvasHeight / fullscreenTaskHeight;
+ }
+ scaledTaskbarSize = taskbarSize * canvasScreenRatio;
+ thumbnailClipHint.bottom = isTablet ? scaledTaskbarSize : 0;
+ }
float scale = thumbnailData.scale;
final float thumbnailScale;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index 8a2509610310..82d70116bbff 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -53,6 +53,8 @@ public final class InteractionJankMonitorWrapper {
InteractionJankMonitor.CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
public static final int CUJ_RECENTS_SCROLLING =
InteractionJankMonitor.CUJ_RECENTS_SCROLLING;
+ public static final int CUJ_APP_SWIPE_TO_RECENTS =
+ InteractionJankMonitor.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS;
@IntDef({
CUJ_APP_LAUNCH_FROM_RECENTS,
@@ -62,7 +64,8 @@ public final class InteractionJankMonitorWrapper {
CUJ_QUICK_SWITCH,
CUJ_APP_LAUNCH_FROM_WIDGET,
CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
- CUJ_RECENTS_SCROLLING
+ CUJ_RECENTS_SCROLLING,
+ CUJ_APP_SWIPE_TO_RECENTS
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
new file mode 100644
index 000000000000..74519c21820b
--- /dev/null
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.annotation.BoolRes
+
+object FlagsFactory {
+ private val flagMap = mutableMapOf<String, Flag<*>>()
+
+ val knownFlags: Map<String, Flag<*>>
+ get() = flagMap
+
+ fun unreleasedFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): UnreleasedFlag {
+ val flag = UnreleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+ FlagsFactory.checkForDupesAndAdd(flag)
+ return flag
+ }
+
+ fun releasedFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): ReleasedFlag {
+ val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+ FlagsFactory.checkForDupesAndAdd(flag)
+ return flag
+ }
+
+ fun resourceBooleanFlag(
+ id: Int,
+ @BoolRes resourceId: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): ResourceBooleanFlag {
+ val flag =
+ ResourceBooleanFlag(
+ id = id,
+ name = name,
+ namespace = namespace,
+ resourceId = resourceId,
+ teamfood = teamfood
+ )
+ FlagsFactory.checkForDupesAndAdd(flag)
+ return flag
+ }
+
+ fun sysPropBooleanFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ default: Boolean = false
+ ): SysPropBooleanFlag {
+ val flag =
+ SysPropBooleanFlag(id = id, name = name, namespace = "systemui", default = default)
+ FlagsFactory.checkForDupesAndAdd(flag)
+ return flag
+ }
+
+ private fun checkForDupesAndAdd(flag: Flag<*>) {
+ if (flagMap.containsKey(flag.name)) {
+ throw IllegalArgumentException("Name {flag.name} is already registered")
+ }
+ flagMap.forEach {
+ if (it.value.id == flag.id) {
+ throw IllegalArgumentException("Name {flag.id} is already registered")
+ }
+ }
+ flagMap[flag.name] = flag
+ }
+}
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
new file mode 100644
index 000000000000..89c0786af6e3
--- /dev/null
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.annotation.BoolRes
+
+object FlagsFactory {
+ private val flagMap = mutableMapOf<String, Flag<*>>()
+
+ val knownFlags: Map<String, Flag<*>>
+ get() = flagMap
+
+ fun unreleasedFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): UnreleasedFlag {
+ // Unreleased flags are always false in this build.
+ val flag = UnreleasedFlag(id = id, name = "", namespace = "", teamfood = false)
+ return flag
+ }
+
+ fun releasedFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): ReleasedFlag {
+ val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+ flagMap[name] = flag
+ return flag
+ }
+
+ fun resourceBooleanFlag(
+ id: Int,
+ @BoolRes resourceId: Int,
+ name: String,
+ namespace: String = "systemui",
+ teamfood: Boolean = false
+ ): ResourceBooleanFlag {
+ val flag =
+ ResourceBooleanFlag(
+ id = id,
+ name = name,
+ namespace = namespace,
+ resourceId = resourceId,
+ teamfood = teamfood
+ )
+ flagMap[name] = flag
+ return flag
+ }
+
+ fun sysPropBooleanFlag(
+ id: Int,
+ name: String,
+ namespace: String = "systemui",
+ default: Boolean = false
+ ): SysPropBooleanFlag {
+ val flag =
+ SysPropBooleanFlag(id = id, name = name, namespace = namespace, default = default)
+ flagMap[name] = flag
+ return flag
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index db64f05ccbea..8fa7b11e2664 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -68,7 +68,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
private final KeyguardUpdateMonitorCallback mUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
- public void onTrustGrantedWithFlags(int flags, int userId) {
+ public void onTrustGrantedWithFlags(int flags, int userId, String message) {
if (userId != KeyguardUpdateMonitor.getCurrentUser()) return;
boolean bouncerVisible = mView.isVisibleToUser();
boolean temporaryAndRenewable =
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 73229c321079..faaba63938bf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
@@ -152,7 +153,9 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
}
public void startAppearAnimation() {
- mMessageAreaController.setMessage(getInitialMessageResId());
+ if (TextUtils.isEmpty(mMessageAreaController.getMessage())) {
+ mMessageAreaController.setMessage(getInitialMessageResId());
+ }
mView.startAppearAnimation();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 2bd3ca59b740..db986e0a631a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -103,6 +103,11 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
mView.setNextMessageColor(colorState);
}
+ /** Returns the message of the underlying TextView. */
+ public CharSequence getMessage() {
+ return mView.getText();
+ }
+
/**
* Reload colors from resources.
**/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 2bb3a5f437f5..5c4126eeb93a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -727,6 +727,11 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
mViewMode.reloadColors();
}
+ /** Handles density or font scale changes. */
+ void onDensityOrFontScaleChanged() {
+ mViewMode.onDensityOrFontScaleChanged();
+ }
+
/**
* Enscapsulates the differences between bouncer modes for the container.
*/
@@ -752,6 +757,9 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
/** Refresh colors */
default void reloadColors() {};
+ /** Handles density or font scale changes. */
+ default void onDensityOrFontScaleChanged() {}
+
/** On a successful auth, optionally handle how the view disappears */
default void startDisappearAnimation(SecurityMode securityMode) {};
@@ -899,14 +907,9 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
mFalsingA11yDelegate = falsingA11yDelegate;
if (mUserSwitcherViewGroup == null) {
- LayoutInflater.from(v.getContext()).inflate(
- R.layout.keyguard_bouncer_user_switcher,
- mView,
- true);
- mUserSwitcherViewGroup = mView.findViewById(R.id.keyguard_bouncer_user_switcher);
+ inflateUserSwitcher();
}
updateSecurityViewLocation();
- mUserSwitcher = mView.findViewById(R.id.user_switcher_header);
setupUserSwitcher();
mUserSwitcherController.addUserSwitchCallback(mUserSwitchCallback);
}
@@ -937,6 +940,12 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
}
@Override
+ public void onDensityOrFontScaleChanged() {
+ mView.removeView(mUserSwitcherViewGroup);
+ inflateUserSwitcher();
+ }
+
+ @Override
public void onDestroy() {
mUserSwitcherController.removeUserSwitchCallback(mUserSwitchCallback);
}
@@ -1097,11 +1106,19 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
new KeyguardSecurityViewTransition());
}
int yTrans = mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_y_trans);
+ int viewFlipperBottomMargin = mResources.getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_view_mode_view_flipper_bottom_margin);
+ int userSwitcherBottomMargin = mResources.getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_view_mode_user_switcher_bottom_margin);
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.connect(mUserSwitcherViewGroup.getId(), TOP, PARENT_ID, TOP, yTrans);
- constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
- constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.connect(mUserSwitcherViewGroup.getId(), BOTTOM, mViewFlipper.getId(),
+ TOP, userSwitcherBottomMargin);
+ constraintSet.connect(mViewFlipper.getId(), TOP, mUserSwitcherViewGroup.getId(),
+ BOTTOM);
+ constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM,
+ viewFlipperBottomMargin);
constraintSet.centerHorizontally(mViewFlipper.getId(), PARENT_ID);
constraintSet.centerHorizontally(mUserSwitcherViewGroup.getId(), PARENT_ID);
constraintSet.setVerticalChainStyle(mViewFlipper.getId(), CHAIN_SPREAD);
@@ -1137,6 +1154,15 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
}
}
+ private void inflateUserSwitcher() {
+ LayoutInflater.from(mView.getContext()).inflate(
+ R.layout.keyguard_bouncer_user_switcher,
+ mView,
+ true);
+ mUserSwitcherViewGroup = mView.findViewById(R.id.keyguard_bouncer_user_switcher);
+ mUserSwitcher = mView.findViewById(R.id.user_switcher_header);
+ }
+
interface UserSwitcherCallback {
void showUnlockToContinueMessage();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 7a49926f8ef1..01be33e1e156 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -251,6 +251,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
public void onUiModeChanged() {
reloadColors();
}
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ KeyguardSecurityContainerController.this.onDensityOrFontScaleChanged();
+ }
};
private boolean mBouncerVisible = false;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
@@ -727,6 +732,14 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mView.reloadColors();
}
+ /** Handles density or font scale changes. */
+ private void onDensityOrFontScaleChanged() {
+ mSecurityViewFlipperController.onDensityOrFontScaleChanged();
+ mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
+ mKeyguardSecurityCallback);
+ mView.onDensityOrFontScaleChanged();
+ }
+
static class Factory {
private final KeyguardSecurityContainer mView;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index bddf4b09ebb3..25afe11ac536 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -83,6 +83,13 @@ public class KeyguardSecurityViewFlipperController
}
}
+ /** Handles density or font scale changes. */
+ public void onDensityOrFontScaleChanged() {
+ mView.removeAllViews();
+ mChildren.clear();
+ }
+
+
@VisibleForTesting
KeyguardInputViewController<KeyguardInputView> getSecurityView(SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 83e23bd52f19..8b9823be65fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import android.content.Context;
+import android.os.Trace;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -112,4 +113,11 @@ public class KeyguardStatusView extends GridLayout {
mKeyguardSlice.dump(pw, args);
}
}
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("KeyguardStatusView#onMeasure");
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ Trace.endSection();
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 1bb83f44ce6c..c8bcbbdb6883 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -314,8 +314,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean mCredentialAttempted;
private boolean mKeyguardGoingAway;
private boolean mGoingToSleep;
- private boolean mBouncerFullyShown;
- private boolean mBouncerIsOrWillBeShowing;
+ private boolean mPrimaryBouncerFullyShown;
+ private boolean mPrimaryBouncerIsOrWillBeShowing;
private boolean mUdfpsBouncerShowing;
private boolean mAuthInterruptActive;
private boolean mNeedsSlowUnlockTransition;
@@ -486,19 +486,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
FACE_AUTH_TRIGGERED_TRUST_DISABLED);
}
- mLogger.logTrustChanged(wasTrusted, enabled, userId);
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onTrustChanged(userId);
- if (enabled && flags != 0) {
- cb.onTrustGrantedWithFlags(flags, userId);
- }
- }
- }
-
+ String message = null;
if (KeyguardUpdateMonitor.getCurrentUser() == userId) {
- CharSequence message = null;
final boolean userHasTrust = getUserHasTrust(userId);
if (userHasTrust && trustGrantedMessages != null) {
for (String msg : trustGrantedMessages) {
@@ -508,14 +497,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
}
-
- if (message != null) {
- mLogger.logShowTrustGrantedMessage(message.toString());
- }
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.showTrustGrantedMessage(message);
+ }
+ mLogger.logTrustChanged(wasTrusted, enabled, userId);
+ if (message != null) {
+ mLogger.logShowTrustGrantedMessage(message.toString());
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onTrustChanged(userId);
+ if (enabled) {
+ cb.onTrustGrantedWithFlags(flags, userId, message);
}
}
}
@@ -1664,8 +1656,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
public void onAuthenticationFailed() {
String reason =
mKeyguardBypassController.canBypass() ? "bypass"
- : mUdfpsBouncerShowing ? "udfpsBouncer" :
- mBouncerFullyShown ? "bouncer" : "udfpsFpDown";
+ : mUdfpsBouncerShowing ? "udfpsBouncer"
+ : mPrimaryBouncerFullyShown ? "bouncer"
+ : "udfpsFpDown";
requestActiveUnlock(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
"faceFailure-" + reason);
@@ -2057,7 +2050,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
handleKeyguardReset();
break;
case MSG_KEYGUARD_BOUNCER_CHANGED:
- handleKeyguardBouncerChanged(msg.arg1, msg.arg2);
+ handlePrimaryBouncerChanged(msg.arg1, msg.arg2);
break;
case MSG_REPORT_EMERGENCY_CALL_ACTION:
handleReportEmergencyCallAction();
@@ -2526,7 +2519,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
requestOrigin,
extraReason, canFaceBypass
|| mUdfpsBouncerShowing
- || mBouncerFullyShown
+ || mPrimaryBouncerFullyShown
|| mAuthController.isUdfpsFingerDown());
}
@@ -2547,7 +2540,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean shouldTriggerActiveUnlock() {
// Triggers:
final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant();
- final boolean awakeKeyguard = mBouncerFullyShown || mUdfpsBouncerShowing
+ final boolean awakeKeyguard = mPrimaryBouncerFullyShown || mUdfpsBouncerShowing
|| (isKeyguardVisible() && !mGoingToSleep
&& mStatusBarState != StatusBarState.SHADE_LOCKED);
@@ -2626,7 +2619,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean shouldListenKeyguardState =
isKeyguardVisible()
|| !mDeviceInteractive
- || (mBouncerIsOrWillBeShowing && !mKeyguardGoingAway)
+ || (mPrimaryBouncerIsOrWillBeShowing && !mKeyguardGoingAway)
|| mGoingToSleep
|| shouldListenForFingerprintAssistant
|| (mKeyguardOccluded && mIsDreaming)
@@ -2645,8 +2638,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& mIsPrimaryUser
&& biometricEnabledForUser;
- final boolean shouldListenBouncerState =
- !(mFingerprintLockedOut && mBouncerIsOrWillBeShowing && mCredentialAttempted);
+ final boolean shouldListenBouncerState = !(mFingerprintLockedOut
+ && mPrimaryBouncerIsOrWillBeShowing && mCredentialAttempted);
final boolean isEncryptedOrLockdownForUser = isEncryptedOrLockdown(user);
@@ -2671,7 +2664,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
user,
shouldListen,
biometricEnabledForUser,
- mBouncerIsOrWillBeShowing,
+ mPrimaryBouncerIsOrWillBeShowing,
userCanSkipBouncer,
mCredentialAttempted,
mDeviceInteractive,
@@ -2728,7 +2721,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// Scan even when encrypted or timeout to show a preemptive bouncer when bypassing.
// Lock-down mode shouldn't scan, since it is more explicit.
boolean strongAuthAllowsScanning = (!isEncryptedOrTimedOut || canBypass
- && !mBouncerFullyShown);
+ && !mPrimaryBouncerFullyShown);
// If the device supports face detection (without authentication) and bypass is enabled,
// allow face scanning to happen if the device is in lockdown mode.
@@ -2749,7 +2742,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
final boolean shouldListen =
- (mBouncerFullyShown
+ (mPrimaryBouncerFullyShown
|| mAuthInterruptActive
|| mOccludingAppRequestingFace
|| awakeKeyguard
@@ -2776,7 +2769,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mAuthInterruptActive,
becauseCannotSkipBouncer,
biometricEnabledForUser,
- mBouncerFullyShown,
+ mPrimaryBouncerFullyShown,
faceAuthenticated,
faceDisabledForUser,
isFaceLockedOut(),
@@ -3306,17 +3299,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
/**
* Handle {@link #MSG_KEYGUARD_BOUNCER_CHANGED}
*
- * @see #sendKeyguardBouncerChanged(boolean, boolean)
+ * @see #sendPrimaryBouncerChanged(boolean, boolean)
*/
- private void handleKeyguardBouncerChanged(int bouncerIsOrWillBeShowing, int bouncerFullyShown) {
+ private void handlePrimaryBouncerChanged(int primaryBouncerIsOrWillBeShowing,
+ int primaryBouncerFullyShown) {
Assert.isMainThread();
- final boolean wasBouncerIsOrWillBeShowing = mBouncerIsOrWillBeShowing;
- final boolean wasBouncerFullyShown = mBouncerFullyShown;
- mBouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing == 1;
- mBouncerFullyShown = bouncerFullyShown == 1;
- mLogger.logKeyguardBouncerChanged(mBouncerIsOrWillBeShowing, mBouncerFullyShown);
-
- if (mBouncerFullyShown) {
+ final boolean wasPrimaryBouncerIsOrWillBeShowing = mPrimaryBouncerIsOrWillBeShowing;
+ final boolean wasPrimaryBouncerFullyShown = mPrimaryBouncerFullyShown;
+ mPrimaryBouncerIsOrWillBeShowing = primaryBouncerIsOrWillBeShowing == 1;
+ mPrimaryBouncerFullyShown = primaryBouncerFullyShown == 1;
+ mLogger.logPrimaryKeyguardBouncerChanged(mPrimaryBouncerIsOrWillBeShowing,
+ mPrimaryBouncerFullyShown);
+
+ if (mPrimaryBouncerFullyShown) {
// If the bouncer is shown, always clear this flag. This can happen in the following
// situations: 1) Default camera with SHOW_WHEN_LOCKED is not chosen yet. 2) Secure
// camera requests dismiss keyguard (tapping on photos for example). When these happen,
@@ -3326,18 +3321,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mCredentialAttempted = false;
}
- if (wasBouncerIsOrWillBeShowing != mBouncerIsOrWillBeShowing) {
+ if (wasPrimaryBouncerIsOrWillBeShowing != mPrimaryBouncerIsOrWillBeShowing) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onKeyguardBouncerStateChanged(mBouncerIsOrWillBeShowing);
+ cb.onKeyguardBouncerStateChanged(mPrimaryBouncerIsOrWillBeShowing);
}
}
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
- if (wasBouncerFullyShown != mBouncerFullyShown) {
- if (mBouncerFullyShown) {
+ if (wasPrimaryBouncerFullyShown != mPrimaryBouncerFullyShown) {
+ if (mPrimaryBouncerFullyShown) {
requestActiveUnlock(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT,
"bouncerFullyShown");
@@ -3345,7 +3340,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onKeyguardBouncerFullyShowingChanged(mBouncerFullyShown);
+ cb.onKeyguardBouncerFullyShowingChanged(mPrimaryBouncerFullyShown);
}
}
updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
@@ -3496,14 +3491,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
/**
- * @see #handleKeyguardBouncerChanged(int, int)
+ * @see #handlePrimaryBouncerChanged(int, int)
*/
- public void sendKeyguardBouncerChanged(boolean bouncerIsOrWillBeShowing,
- boolean bouncerFullyShown) {
- mLogger.logSendKeyguardBouncerChanged(bouncerIsOrWillBeShowing, bouncerFullyShown);
+ public void sendPrimaryBouncerChanged(boolean primaryBouncerIsOrWillBeShowing,
+ boolean primaryBouncerFullyShown) {
+ mLogger.logSendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
+ primaryBouncerFullyShown);
Message message = mHandler.obtainMessage(MSG_KEYGUARD_BOUNCER_CHANGED);
- message.arg1 = bouncerIsOrWillBeShowing ? 1 : 0;
- message.arg2 = bouncerFullyShown ? 1 : 0;
+ message.arg1 = primaryBouncerIsOrWillBeShowing ? 1 : 0;
+ message.arg2 = primaryBouncerFullyShown ? 1 : 0;
message.sendToTarget();
}
@@ -3863,7 +3859,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (isUdfpsSupported()) {
pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true));
- pw.println(" mBouncerIsOrWillBeShowing=" + mBouncerIsOrWillBeShowing);
+ pw.println(" mPrimaryBouncerIsOrWillBeShowing="
+ + mPrimaryBouncerIsOrWillBeShowing);
pw.println(" mStatusBarState=" + StatusBarState.toString(mStatusBarState));
pw.println(" mUdfpsBouncerShowing=" + mUdfpsBouncerShowing);
} else if (isSfpsSupported()) {
@@ -3893,7 +3890,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" mFaceLockedOutPermanent=" + mFaceLockedOutPermanent);
pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched);
- pw.println(" mBouncerFullyShown=" + mBouncerFullyShown);
+ pw.println(" mPrimaryBouncerFullyShown=" + mPrimaryBouncerFullyShown);
pw.println(" mNeedsSlowUnlockTransition=" + mNeedsSlowUnlockTransition);
}
mListenModels.print(pw);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index c06e1dcf08c2..c5142f309a46 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -174,14 +174,12 @@ public class KeyguardUpdateMonitorCallback {
public void onTrustManagedChanged(int userId) { }
/**
- * Called after trust was granted with non-zero flags.
+ * Called after trust was granted.
+ * @param userId of the user that has been granted trust
+ * @param message optional message the trust agent has provided to show that should indicate
+ * why trust was granted.
*/
- public void onTrustGrantedWithFlags(int flags, int userId) { }
-
- /**
- * Called when setting the trust granted message.
- */
- public void showTrustGrantedMessage(@Nullable CharSequence message) { }
+ public void onTrustGrantedWithFlags(int flags, int userId, @Nullable String message) { }
/**
* Called when a biometric has been acquired.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 90f0446ee34d..6c3c246e7fb9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -50,16 +50,11 @@ public interface KeyguardViewController {
/**
* Resets the state of Keyguard View.
- * @param hideBouncerWhenShowing
+ * @param hideBouncerWhenShowing when true, hides the primary and alternate bouncers if showing.
*/
void reset(boolean hideBouncerWhenShowing);
/**
- * Stop showing any alternate auth methods.
- */
- void resetAlternateAuth(boolean forceUpdateScrim);
-
- /**
* Called when the device started going to sleep.
*/
default void onStartedGoingToSleep() {};
@@ -156,20 +151,24 @@ public interface KeyguardViewController {
void notifyKeyguardAuthenticated(boolean strongAuth);
/**
- * Shows the Bouncer.
- *
+ * Shows the primary bouncer.
+ */
+ void showPrimaryBouncer(boolean scrimmed);
+
+ /**
+ * When the primary bouncer is fully visible or is showing but animation didn't finish yet.
*/
- void showBouncer(boolean scrimmed);
+ boolean primaryBouncerIsOrWillBeShowing();
/**
- * Returns {@code true} when the bouncer is currently showing
+ * Returns {@code true} when the primary bouncer or alternate bouncer is currently showing
*/
boolean isBouncerShowing();
/**
- * When bouncer is fully visible or it is showing but animation didn't finish yet.
+ * Stop showing the alternate bouncer, if showing.
*/
- boolean bouncerIsOrWillBeShowing();
+ void hideAlternateBouncer(boolean forceUpdateScrim);
// TODO: Deprecate registerStatusBar in KeyguardViewController interface. It is currently
// only used for testing purposes in StatusBarKeyguardViewManager, and it prevents us from
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 0a82968ae4cb..34a5ef75f176 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -158,6 +158,10 @@ public class LockIconView extends FrameLayout implements Dumpable {
return mLockIconCenter.y - mRadius;
}
+ float getLocationBottom() {
+ return mLockIconCenter.y + mRadius;
+ }
+
/**
* Updates the icon its default state where no visual is shown.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index fe7c70ae4c7e..dd6a1bd457b8 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -280,6 +280,10 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
return mView.getLocationTop();
}
+ public float getBottom() {
+ return mView.getLocationBottom();
+ }
+
private void updateVisibility() {
if (mCancelDelayedUpdateVisibilityRunnable != null) {
mCancelDelayedUpdateVisibilityRunnable.run();
@@ -695,7 +699,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
"lock-screen-lock-icon-longpress",
TOUCH_VIBRATION_ATTRIBUTES);
- mKeyguardViewController.showBouncer(/* scrim */ true);
+ mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index 32ce537ea25a..9e58500c7206 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -18,14 +18,11 @@ package com.android.keyguard.logging
import com.android.systemui.log.dagger.KeyguardLog
import com.android.systemui.plugins.log.LogBuffer
-import com.android.systemui.plugins.log.LogLevel
import com.android.systemui.plugins.log.LogLevel.DEBUG
import com.android.systemui.plugins.log.LogLevel.ERROR
import com.android.systemui.plugins.log.LogLevel.INFO
import com.android.systemui.plugins.log.LogLevel.VERBOSE
import com.android.systemui.plugins.log.LogLevel.WARNING
-import com.android.systemui.plugins.log.MessageInitializer
-import com.android.systemui.plugins.log.MessagePrinter
import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
@@ -37,18 +34,16 @@ private const val TAG = "KeyguardLog"
* an overkill.
*/
class KeyguardLogger @Inject constructor(@KeyguardLog private val buffer: LogBuffer) {
- fun d(@CompileTimeConstant msg: String) = log(msg, DEBUG)
+ fun d(@CompileTimeConstant msg: String) = buffer.log(TAG, DEBUG, msg)
- fun e(@CompileTimeConstant msg: String) = log(msg, ERROR)
+ fun e(@CompileTimeConstant msg: String) = buffer.log(TAG, ERROR, msg)
- fun v(@CompileTimeConstant msg: String) = log(msg, VERBOSE)
+ fun v(@CompileTimeConstant msg: String) = buffer.log(TAG, VERBOSE, msg)
- fun w(@CompileTimeConstant msg: String) = log(msg, WARNING)
+ fun w(@CompileTimeConstant msg: String) = buffer.log(TAG, WARNING, msg)
- fun log(msg: String, level: LogLevel) = buffer.log(TAG, level, msg)
-
- private fun debugLog(messageInitializer: MessageInitializer, messagePrinter: MessagePrinter) {
- buffer.log(TAG, DEBUG, messageInitializer, messagePrinter)
+ fun logException(ex: Exception, @CompileTimeConstant logMsg: String) {
+ buffer.log(TAG, ERROR, {}, { logMsg }, exception = ex)
}
fun v(msg: String, arg: Any) {
@@ -61,17 +56,24 @@ class KeyguardLogger @Inject constructor(@KeyguardLog private val buffer: LogBuf
// TODO: remove after b/237743330 is fixed
fun logStatusBarCalculatedAlpha(alpha: Float) {
- debugLog({ double1 = alpha.toDouble() }, { "Calculated new alpha: $double1" })
+ buffer.log(TAG, DEBUG, { double1 = alpha.toDouble() }, { "Calculated new alpha: $double1" })
}
// TODO: remove after b/237743330 is fixed
fun logStatusBarExplicitAlpha(alpha: Float) {
- debugLog({ double1 = alpha.toDouble() }, { "new mExplicitAlpha value: $double1" })
+ buffer.log(
+ TAG,
+ DEBUG,
+ { double1 = alpha.toDouble() },
+ { "new mExplicitAlpha value: $double1" }
+ )
}
// TODO: remove after b/237743330 is fixed
fun logStatusBarAlphaVisibility(visibility: Int, alpha: Float, state: String) {
- debugLog(
+ buffer.log(
+ TAG,
+ DEBUG,
{
int1 = visibility
double1 = alpha.toDouble()
@@ -80,4 +82,22 @@ class KeyguardLogger @Inject constructor(@KeyguardLog private val buffer: LogBuf
{ "changing visibility to $int1 with alpha $double1 in state: $str1" }
)
}
+
+ @JvmOverloads
+ fun logBiometricMessage(
+ @CompileTimeConstant context: String,
+ msgId: Int? = null,
+ msg: String? = null
+ ) {
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = context
+ str2 = "$msgId"
+ str3 = msg
+ },
+ { "$str1 msgId: $str2 msg: $str3" }
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 6276142d605f..81b8dfed36a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -166,13 +166,16 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
{ "Previously active sub id $int1 is now invalid, will remove" })
}
- fun logKeyguardBouncerChanged(bouncerIsOrWillBeShowing: Boolean, bouncerFullyShown: Boolean) {
+ fun logPrimaryKeyguardBouncerChanged(
+ primaryBouncerIsOrWillBeShowing: Boolean,
+ primaryBouncerFullyShown: Boolean
+ ) {
logBuffer.log(TAG, DEBUG, {
- bool1 = bouncerIsOrWillBeShowing
- bool2 = bouncerFullyShown
+ bool1 = primaryBouncerIsOrWillBeShowing
+ bool2 = primaryBouncerFullyShown
}, {
- "handleKeyguardBouncerChanged " +
- "bouncerIsOrWillBeShowing=$bool1 bouncerFullyShowing=$bool2"
+ "handlePrimaryBouncerChanged " +
+ "primaryBouncerIsOrWillBeShowing=$bool1 primaryBouncerFullyShown=$bool2"
})
}
@@ -229,16 +232,16 @@ class KeyguardUpdateMonitorLogger @Inject constructor(
{ "Retrying fingerprint attempt: $int1" })
}
- fun logSendKeyguardBouncerChanged(
- bouncerIsOrWillBeShowing: Boolean,
- bouncerFullyShown: Boolean,
+ fun logSendPrimaryBouncerChanged(
+ primaryBouncerIsOrWillBeShowing: Boolean,
+ primaryBouncerFullyShown: Boolean,
) {
logBuffer.log(TAG, DEBUG, {
- bool1 = bouncerIsOrWillBeShowing
- bool2 = bouncerFullyShown
+ bool1 = primaryBouncerIsOrWillBeShowing
+ bool2 = primaryBouncerFullyShown
}, {
- "sendKeyguardBouncerChanged bouncerIsOrWillBeShowing=$bool1 " +
- "bouncerFullyShown=$bool2"
+ "sendPrimaryBouncerChanged primaryBouncerIsOrWillBeShowing=$bool1 " +
+ "primaryBouncerFullyShown=$bool2"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
index 3015710e8a98..eee705dea277 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -26,8 +26,6 @@ import java.util.concurrent.Executor
import kotlin.math.roundToInt
-const val TAG = "CameraAvailabilityListener"
-
/**
* Listens for usage of the Camera and controls the ScreenDecorations transition to show extra
* protection around a display cutout based on config_frontBuiltInDisplayCutoutProtection and
diff --git a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
index a89cbf57f95b..9ac45b3c77cc 100644
--- a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
+++ b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
@@ -4,30 +4,32 @@ import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.util.Log
+import com.android.internal.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.FlagListenable
import com.android.systemui.flags.Flags
-import javax.inject.Inject
+import com.android.systemui.settings.UserTracker
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
+import javax.inject.Inject
@SysUISingleton
class ChooserSelector @Inject constructor(
private val context: Context,
+ private val userTracker: UserTracker,
private val featureFlags: FeatureFlags,
@Application private val coroutineScope: CoroutineScope,
- @Background private val bgDispatcher: CoroutineDispatcher
+ @Background private val bgDispatcher: CoroutineDispatcher,
) : CoreStartable {
- private val packageManager = context.packageManager
private val chooserComponent = ComponentName.unflattenFromString(
- context.resources.getString(ChooserSelectorResourceHelper.CONFIG_CHOOSER_ACTIVITY))
+ context.resources.getString(R.string.config_chooserActivity))
override fun start() {
coroutineScope.launch {
@@ -56,10 +58,17 @@ class ChooserSelector @Inject constructor(
} else {
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
}
- try {
- packageManager.setComponentEnabledSetting(chooserComponent, newState, /* flags = */ 0)
- } catch (e: IllegalArgumentException) {
- Log.w("ChooserSelector", "Unable to set IntentResolver enabled=" + enabled, e)
+ userTracker.userProfiles.forEach {
+ try {
+ context.createContextAsUser(it.userHandle, /* flags = */ 0).packageManager
+ .setComponentEnabledSetting(chooserComponent, newState, /* flags = */ 0)
+ } catch (e: IllegalArgumentException) {
+ Log.w(
+ "ChooserSelector",
+ "Unable to set IntentResolver enabled=$enabled for user ${it.id}",
+ e,
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
index 5d52056d8b17..90ecb466b5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
+++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
@@ -169,7 +169,7 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView {
return
}
cutoutPath.reset()
- display.getDisplayInfo(displayInfo)
+ context.display?.getDisplayInfo(displayInfo)
displayInfo.displayCutout?.cutoutPath?.let { path -> cutoutPath.set(path) }
invalidate()
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 11d579d481c1..45f9385a2620 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -170,6 +170,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
private Display.Mode mDisplayMode;
@VisibleForTesting
protected DisplayInfo mDisplayInfo = new DisplayInfo();
+ private DisplayCutout mDisplayCutout;
@VisibleForTesting
protected void showCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) {
@@ -384,6 +385,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
mRotation = mDisplayInfo.rotation;
mDisplayMode = mDisplayInfo.getMode();
mDisplayUniqueId = mDisplayInfo.uniqueId;
+ mDisplayCutout = mDisplayInfo.displayCutout;
mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(),
mDisplayUniqueId);
mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
@@ -1022,7 +1024,8 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
mRoundedCornerResDelegate.dump(pw, args);
}
- private void updateConfiguration() {
+ @VisibleForTesting
+ void updateConfiguration() {
Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
"must call on " + mHandler.getLooper().getThread()
+ ", but was " + Thread.currentThread());
@@ -1033,11 +1036,14 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
mDotViewController.setNewRotation(newRotation);
}
final Display.Mode newMod = mDisplayInfo.getMode();
+ final DisplayCutout newCutout = mDisplayInfo.displayCutout;
if (!mPendingConfigChange
- && (newRotation != mRotation || displayModeChanged(mDisplayMode, newMod))) {
+ && (newRotation != mRotation || displayModeChanged(mDisplayMode, newMod)
+ || !Objects.equals(newCutout, mDisplayCutout))) {
mRotation = newRotation;
mDisplayMode = newMod;
+ mDisplayCutout = newCutout;
mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
getPhysicalPixelDisplaySizeRatio());
if (mScreenDecorHwcLayer != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt
new file mode 100644
index 000000000000..b52ddc1dbc42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.battery
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorFilter
+import android.graphics.Matrix
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.PixelFormat
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffXfermode
+import android.graphics.Rect
+import android.graphics.drawable.DrawableWrapper
+import android.util.PathParser
+import com.android.settingslib.graph.ThemedBatteryDrawable
+import com.android.systemui.R
+import com.android.systemui.battery.BatterySpecs.BATTERY_HEIGHT
+import com.android.systemui.battery.BatterySpecs.BATTERY_HEIGHT_WITH_SHIELD
+import com.android.systemui.battery.BatterySpecs.BATTERY_WIDTH
+import com.android.systemui.battery.BatterySpecs.BATTERY_WIDTH_WITH_SHIELD
+import com.android.systemui.battery.BatterySpecs.SHIELD_LEFT_OFFSET
+import com.android.systemui.battery.BatterySpecs.SHIELD_STROKE
+import com.android.systemui.battery.BatterySpecs.SHIELD_TOP_OFFSET
+
+/**
+ * A battery drawable that accessorizes [ThemedBatteryDrawable] with additional information if
+ * necessary.
+ *
+ * For now, it adds a shield in the bottom-right corner when [displayShield] is true.
+ */
+class AccessorizedBatteryDrawable(
+ private val context: Context,
+ frameColor: Int,
+) : DrawableWrapper(ThemedBatteryDrawable(context, frameColor)) {
+ private val mainBatteryDrawable: ThemedBatteryDrawable
+ get() = drawable as ThemedBatteryDrawable
+
+ private val shieldPath = Path()
+ private val scaledShield = Path()
+ private val scaleMatrix = Matrix()
+
+ private var shieldLeftOffsetScaled = SHIELD_LEFT_OFFSET
+ private var shieldTopOffsetScaled = SHIELD_TOP_OFFSET
+
+ private var density = context.resources.displayMetrics.density
+
+ private val dualTone =
+ context.resources.getBoolean(com.android.internal.R.bool.config_batterymeterDualTone)
+
+ private val shieldTransparentOutlinePaint =
+ Paint(Paint.ANTI_ALIAS_FLAG).also { p ->
+ p.color = Color.TRANSPARENT
+ p.strokeWidth = ThemedBatteryDrawable.PROTECTION_MIN_STROKE_WIDTH
+ p.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
+ p.style = Paint.Style.FILL_AND_STROKE
+ }
+
+ private val shieldPaint =
+ Paint(Paint.ANTI_ALIAS_FLAG).also { p ->
+ p.color = Color.MAGENTA
+ p.style = Paint.Style.FILL
+ p.isDither = true
+ }
+
+ init {
+ loadPaths()
+ }
+
+ override fun onBoundsChange(bounds: Rect) {
+ super.onBoundsChange(bounds)
+ updateSizes()
+ }
+
+ var displayShield: Boolean = false
+
+ private fun updateSizes() {
+ val b = bounds
+ if (b.isEmpty) {
+ return
+ }
+
+ val mainWidth = BatterySpecs.getMainBatteryWidth(b.width().toFloat(), displayShield)
+ val mainHeight = BatterySpecs.getMainBatteryHeight(b.height().toFloat(), displayShield)
+
+ drawable?.setBounds(
+ b.left,
+ b.top,
+ /* right= */ b.left + mainWidth.toInt(),
+ /* bottom= */ b.top + mainHeight.toInt()
+ )
+
+ if (displayShield) {
+ val sx = b.right / BATTERY_WIDTH_WITH_SHIELD
+ val sy = b.bottom / BATTERY_HEIGHT_WITH_SHIELD
+ scaleMatrix.setScale(sx, sy)
+ shieldPath.transform(scaleMatrix, scaledShield)
+
+ shieldLeftOffsetScaled = sx * SHIELD_LEFT_OFFSET
+ shieldTopOffsetScaled = sy * SHIELD_TOP_OFFSET
+
+ val scaledStrokeWidth =
+ (sx * SHIELD_STROKE).coerceAtLeast(
+ ThemedBatteryDrawable.PROTECTION_MIN_STROKE_WIDTH
+ )
+ shieldTransparentOutlinePaint.strokeWidth = scaledStrokeWidth
+ }
+ }
+
+ override fun getIntrinsicHeight(): Int {
+ val height =
+ if (displayShield) {
+ BATTERY_HEIGHT_WITH_SHIELD
+ } else {
+ BATTERY_HEIGHT
+ }
+ return (height * density).toInt()
+ }
+
+ override fun getIntrinsicWidth(): Int {
+ val width =
+ if (displayShield) {
+ BATTERY_WIDTH_WITH_SHIELD
+ } else {
+ BATTERY_WIDTH
+ }
+ return (width * density).toInt()
+ }
+
+ override fun draw(c: Canvas) {
+ c.saveLayer(null, null)
+ // Draw the main battery icon
+ super.draw(c)
+
+ if (displayShield) {
+ c.translate(shieldLeftOffsetScaled, shieldTopOffsetScaled)
+ // We need a transparent outline around the shield, so first draw the transparent-ness
+ // then draw the shield
+ c.drawPath(scaledShield, shieldTransparentOutlinePaint)
+ c.drawPath(scaledShield, shieldPaint)
+ }
+ c.restore()
+ }
+
+ override fun getOpacity(): Int {
+ return PixelFormat.OPAQUE
+ }
+
+ override fun setAlpha(p0: Int) {
+ // Unused internally -- see [ThemedBatteryDrawable.setAlpha].
+ }
+
+ override fun setColorFilter(colorfilter: ColorFilter?) {
+ super.setColorFilter(colorFilter)
+ shieldPaint.colorFilter = colorFilter
+ }
+
+ /** Sets whether the battery is currently charging. */
+ fun setCharging(charging: Boolean) {
+ mainBatteryDrawable.charging = charging
+ }
+
+ /** Sets the current level (out of 100) of the battery. */
+ fun setBatteryLevel(level: Int) {
+ mainBatteryDrawable.setBatteryLevel(level)
+ }
+
+ /** Sets whether power save is enabled. */
+ fun setPowerSaveEnabled(powerSaveEnabled: Boolean) {
+ mainBatteryDrawable.powerSaveEnabled = powerSaveEnabled
+ }
+
+ /** Returns whether power save is currently enabled. */
+ fun getPowerSaveEnabled(): Boolean {
+ return mainBatteryDrawable.powerSaveEnabled
+ }
+
+ /** Sets the colors to use for the icon. */
+ fun setColors(fgColor: Int, bgColor: Int, singleToneColor: Int) {
+ shieldPaint.color = if (dualTone) fgColor else singleToneColor
+ mainBatteryDrawable.setColors(fgColor, bgColor, singleToneColor)
+ }
+
+ /** Notifies this drawable that the density might have changed. */
+ fun notifyDensityChanged() {
+ density = context.resources.displayMetrics.density
+ }
+
+ private fun loadPaths() {
+ val shieldPathString = context.resources.getString(R.string.config_batterymeterShieldPath)
+ shieldPath.set(PathParser.createPathFromPathData(shieldPathString))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index 6a10d4ab1e8b..03d999f697d0 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -45,7 +45,6 @@ import android.widget.TextView;
import androidx.annotation.StyleRes;
import androidx.annotation.VisibleForTesting;
-import com.android.settingslib.graph.ThemedBatteryDrawable;
import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
@@ -68,7 +67,7 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
public static final int MODE_OFF = 2;
public static final int MODE_ESTIMATE = 3;
- private final ThemedBatteryDrawable mDrawable;
+ private final AccessorizedBatteryDrawable mDrawable;
private final ImageView mBatteryIconView;
private TextView mBatteryPercentView;
@@ -77,7 +76,10 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
private int mLevel;
private int mShowPercentMode = MODE_DEFAULT;
private boolean mShowPercentAvailable;
+ private String mEstimateText = null;
private boolean mCharging;
+ private boolean mIsOverheated;
+ private boolean mDisplayShieldEnabled;
// Error state where we know nothing about the current battery state
private boolean mBatteryStateUnknown;
// Lazily-loaded since this is expected to be a rare-if-ever state
@@ -106,7 +108,7 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
context.getColor(R.color.meter_background_color));
mPercentageStyleId = atts.getResourceId(R.styleable.BatteryMeterView_textAppearance, 0);
- mDrawable = new ThemedBatteryDrawable(context, frameColor);
+ mDrawable = new AccessorizedBatteryDrawable(context, frameColor);
atts.recycle();
mShowPercentAvailable = context.getResources().getBoolean(
@@ -170,12 +172,14 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
if (mode == mShowPercentMode) return;
mShowPercentMode = mode;
updateShowPercent();
+ updatePercentText();
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updatePercentView();
+ mDrawable.notifyDensityChanged();
}
public void setColorsFromContext(Context context) {
@@ -203,6 +207,17 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
mDrawable.setPowerSaveEnabled(isPowerSave);
}
+ void onIsOverheatedChanged(boolean isOverheated) {
+ boolean valueChanged = mIsOverheated != isOverheated;
+ mIsOverheated = isOverheated;
+ if (valueChanged) {
+ updateContentDescription();
+ // The battery drawable is a different size depending on whether it's currently
+ // overheated or not, so we need to re-scale the view when overheated changes.
+ scaleBatteryMeterViews();
+ }
+ }
+
private TextView loadPercentView() {
return (TextView) LayoutInflater.from(getContext())
.inflate(R.layout.battery_percentage_view, null);
@@ -227,13 +242,17 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
mBatteryEstimateFetcher = fetcher;
}
+ void setDisplayShieldEnabled(boolean displayShieldEnabled) {
+ mDisplayShieldEnabled = displayShieldEnabled;
+ }
+
void updatePercentText() {
if (mBatteryStateUnknown) {
- setContentDescription(getContext().getString(R.string.accessibility_battery_unknown));
return;
}
if (mBatteryEstimateFetcher == null) {
+ setPercentTextAtCurrentLevel();
return;
}
@@ -245,10 +264,9 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
return;
}
if (estimate != null && mShowPercentMode == MODE_ESTIMATE) {
+ mEstimateText = estimate;
mBatteryPercentView.setText(estimate);
- setContentDescription(getContext().getString(
- R.string.accessibility_battery_level_with_estimate,
- mLevel, estimate));
+ updateContentDescription();
} else {
setPercentTextAtCurrentLevel();
}
@@ -257,28 +275,49 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
setPercentTextAtCurrentLevel();
}
} else {
- setContentDescription(
- getContext().getString(mCharging ? R.string.accessibility_battery_level_charging
- : R.string.accessibility_battery_level, mLevel));
+ updateContentDescription();
}
}
private void setPercentTextAtCurrentLevel() {
- if (mBatteryPercentView == null) {
- return;
+ if (mBatteryPercentView != null) {
+ mEstimateText = null;
+ String percentText = NumberFormat.getPercentInstance().format(mLevel / 100f);
+ // Setting text actually triggers a layout pass (because the text view is set to
+ // wrap_content width and TextView always relayouts for this). Avoid needless
+ // relayout if the text didn't actually change.
+ if (!TextUtils.equals(mBatteryPercentView.getText(), percentText)) {
+ mBatteryPercentView.setText(percentText);
+ }
}
- String percentText = NumberFormat.getPercentInstance().format(mLevel / 100f);
- // Setting text actually triggers a layout pass (because the text view is set to
- // wrap_content width and TextView always relayouts for this). Avoid needless
- // relayout if the text didn't actually change.
- if (!TextUtils.equals(mBatteryPercentView.getText(), percentText)) {
- mBatteryPercentView.setText(percentText);
+ updateContentDescription();
+ }
+
+ private void updateContentDescription() {
+ Context context = getContext();
+
+ String contentDescription;
+ if (mBatteryStateUnknown) {
+ contentDescription = context.getString(R.string.accessibility_battery_unknown);
+ } else if (mShowPercentMode == MODE_ESTIMATE && !TextUtils.isEmpty(mEstimateText)) {
+ contentDescription = context.getString(
+ mIsOverheated
+ ? R.string.accessibility_battery_level_charging_paused_with_estimate
+ : R.string.accessibility_battery_level_with_estimate,
+ mLevel,
+ mEstimateText);
+ } else if (mIsOverheated) {
+ contentDescription =
+ context.getString(R.string.accessibility_battery_level_charging_paused, mLevel);
+ } else if (mCharging) {
+ contentDescription =
+ context.getString(R.string.accessibility_battery_level_charging, mLevel);
+ } else {
+ contentDescription = context.getString(R.string.accessibility_battery_level, mLevel);
}
- setContentDescription(
- getContext().getString(mCharging ? R.string.accessibility_battery_level_charging
- : R.string.accessibility_battery_level, mLevel));
+ setContentDescription(contentDescription);
}
void updateShowPercent() {
@@ -329,6 +368,7 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
}
mBatteryStateUnknown = isUnknown;
+ updateContentDescription();
if (mBatteryStateUnknown) {
mBatteryIconView.setImageDrawable(getUnknownStateDrawable());
@@ -349,15 +389,43 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
res.getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
float iconScaleFactor = typedValue.getFloat();
- int batteryHeight = res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_height);
- int batteryWidth = res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_width);
+ float mainBatteryHeight =
+ res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_height) * iconScaleFactor;
+ float mainBatteryWidth =
+ res.getDimensionPixelSize(R.dimen.status_bar_battery_icon_width) * iconScaleFactor;
+
+ // If the battery is marked as overheated, we should display a shield indicating that the
+ // battery is being "defended".
+ boolean displayShield = mDisplayShieldEnabled && mIsOverheated;
+ float fullBatteryIconHeight =
+ BatterySpecs.getFullBatteryHeight(mainBatteryHeight, displayShield);
+ float fullBatteryIconWidth =
+ BatterySpecs.getFullBatteryWidth(mainBatteryWidth, displayShield);
+
+ int marginTop;
+ if (displayShield) {
+ // If the shield is displayed, we need some extra marginTop so that the bottom of the
+ // main icon is still aligned with the bottom of all the other system icons.
+ int shieldHeightAddition = Math.round(fullBatteryIconHeight - mainBatteryHeight);
+ // However, the other system icons have some embedded bottom padding that the battery
+ // doesn't have, so we shouldn't move the battery icon down by the full amount.
+ // See b/258672854.
+ marginTop = shieldHeightAddition
+ - res.getDimensionPixelSize(R.dimen.status_bar_battery_extra_vertical_spacing);
+ } else {
+ marginTop = 0;
+ }
+
int marginBottom = res.getDimensionPixelSize(R.dimen.battery_margin_bottom);
LinearLayout.LayoutParams scaledLayoutParams = new LinearLayout.LayoutParams(
- (int) (batteryWidth * iconScaleFactor), (int) (batteryHeight * iconScaleFactor));
- scaledLayoutParams.setMargins(0, 0, 0, marginBottom);
+ Math.round(fullBatteryIconWidth),
+ Math.round(fullBatteryIconHeight));
+ scaledLayoutParams.setMargins(0, marginTop, 0, marginBottom);
+ mDrawable.setDisplayShield(displayShield);
mBatteryIconView.setLayoutParams(scaledLayoutParams);
+ mBatteryIconView.invalidateDrawable(mDrawable);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
index ae9a32309d45..77cb9d1bf594 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
@@ -29,6 +29,8 @@ import android.view.View;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -84,6 +86,11 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView>
public void onBatteryUnknownStateChanged(boolean isUnknown) {
mView.onBatteryUnknownStateChanged(isUnknown);
}
+
+ @Override
+ public void onIsOverheatedChanged(boolean isOverheated) {
+ mView.onIsOverheatedChanged(isOverheated);
+ }
};
// Some places may need to show the battery conditionally, and not obey the tuner
@@ -98,6 +105,7 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView>
BroadcastDispatcher broadcastDispatcher,
@Main Handler mainHandler,
ContentResolver contentResolver,
+ FeatureFlags featureFlags,
BatteryController batteryController) {
super(view);
mConfigurationController = configurationController;
@@ -106,6 +114,7 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView>
mBatteryController = batteryController;
mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString);
+ mView.setDisplayShieldEnabled(featureFlags.isEnabled(Flags.BATTERY_SHIELD_ICON));
mSlotBattery = getResources().getString(com.android.internal.R.string.status_bar_battery);
mSettingObserver = new SettingObserver(mainHandler);
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatterySpecs.kt b/packages/SystemUI/src/com/android/systemui/battery/BatterySpecs.kt
new file mode 100644
index 000000000000..6455a9656fde
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatterySpecs.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.battery
+
+import com.android.settingslib.graph.ThemedBatteryDrawable
+
+/** An object storing specs related to the battery icon in the status bar. */
+object BatterySpecs {
+
+ /** Width of the main battery icon, not including the shield. */
+ const val BATTERY_WIDTH = ThemedBatteryDrawable.WIDTH
+ /** Height of the main battery icon, not including the shield. */
+ const val BATTERY_HEIGHT = ThemedBatteryDrawable.HEIGHT
+
+ private const val SHIELD_WIDTH = 10f
+ private const val SHIELD_HEIGHT = 13f
+
+ /**
+ * Amount that the left side of the shield should be offset from the left side of the battery.
+ */
+ const val SHIELD_LEFT_OFFSET = 8f
+ /** Amount that the top of the shield should be offset from the top of the battery. */
+ const val SHIELD_TOP_OFFSET = 10f
+
+ const val SHIELD_STROKE = 4f
+
+ /** The full width of the battery icon, including the main battery icon *and* the shield. */
+ const val BATTERY_WIDTH_WITH_SHIELD = SHIELD_LEFT_OFFSET + SHIELD_WIDTH
+ /** The full height of the battery icon, including the main battery icon *and* the shield. */
+ const val BATTERY_HEIGHT_WITH_SHIELD = SHIELD_TOP_OFFSET + SHIELD_HEIGHT
+
+ /**
+ * Given the desired height of the main battery icon in pixels, returns the height that the full
+ * battery icon will take up in pixels.
+ *
+ * If there's no shield, this will just return [mainBatteryHeight]. Otherwise, the shield
+ * extends slightly below the bottom of the main battery icon so we need some extra height.
+ */
+ @JvmStatic
+ fun getFullBatteryHeight(mainBatteryHeight: Float, displayShield: Boolean): Float {
+ return if (!displayShield) {
+ mainBatteryHeight
+ } else {
+ val verticalScaleFactor = mainBatteryHeight / BATTERY_HEIGHT
+ verticalScaleFactor * BATTERY_HEIGHT_WITH_SHIELD
+ }
+ }
+
+ /**
+ * Given the desired width of the main battery icon in pixels, returns the width that the full
+ * battery icon will take up in pixels.
+ *
+ * If there's no shield, this will just return [mainBatteryWidth]. Otherwise, the shield extends
+ * past the right side of the main battery icon so we need some extra width.
+ */
+ @JvmStatic
+ fun getFullBatteryWidth(mainBatteryWidth: Float, displayShield: Boolean): Float {
+ return if (!displayShield) {
+ mainBatteryWidth
+ } else {
+ val horizontalScaleFactor = mainBatteryWidth / BATTERY_WIDTH
+ horizontalScaleFactor * BATTERY_WIDTH_WITH_SHIELD
+ }
+ }
+
+ /**
+ * Given the height of the full battery icon, return how tall the main battery icon should be.
+ *
+ * If there's no shield, this will just return [fullBatteryHeight]. Otherwise, the shield takes
+ * up some of the view's height so the main battery width will be just a portion of
+ * [fullBatteryHeight].
+ */
+ @JvmStatic
+ fun getMainBatteryHeight(fullBatteryHeight: Float, displayShield: Boolean): Float {
+ return if (!displayShield) {
+ fullBatteryHeight
+ } else {
+ return (BATTERY_HEIGHT / BATTERY_HEIGHT_WITH_SHIELD) * fullBatteryHeight
+ }
+ }
+
+ /**
+ * Given the width of the full battery icon, return how wide the main battery icon should be.
+ *
+ * If there's no shield, this will just return [fullBatteryWidth]. Otherwise, the shield takes
+ * up some of the view's width so the main battery width will be just a portion of
+ * [fullBatteryWidth].
+ */
+ @JvmStatic
+ fun getMainBatteryWidth(fullBatteryWidth: Float, displayShield: Boolean): Float {
+ return if (!displayShield) {
+ fullBatteryWidth
+ } else {
+ return (BATTERY_WIDTH / BATTERY_WIDTH_WITH_SHIELD) * fullBatteryWidth
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 0a2d8ec97ba6..94f71580901c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -132,8 +132,7 @@ public class AuthContainerView extends LinearLayout
private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
private final @Background DelayableExecutor mBackgroundExecutor;
- private int mOrientation;
- private boolean mSkipFirstLostFocus = false;
+ private boolean mIsOrientationChanged = false;
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
@Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason;
@@ -444,6 +443,7 @@ public class AuthContainerView extends LinearLayout
@Override
public void onOrientationChanged() {
maybeUpdatePositionForUdfps(true /* invalidate */);
+ mIsOrientationChanged = true;
}
@Override
@@ -452,8 +452,8 @@ public class AuthContainerView extends LinearLayout
if (!hasWindowFocus) {
//it's a workaround to avoid closing BP incorrectly
//BP gets a onWindowFocusChanged(false) and then gets a onWindowFocusChanged(true)
- if (mSkipFirstLostFocus) {
- mSkipFirstLostFocus = false;
+ if (mIsOrientationChanged) {
+ mIsOrientationChanged = false;
return;
}
Log.v(TAG, "Lost window focus, dismissing the dialog");
@@ -465,9 +465,6 @@ public class AuthContainerView extends LinearLayout
public void onAttachedToWindow() {
super.onAttachedToWindow();
- //save the first orientation
- mOrientation = getResources().getConfiguration().orientation;
-
mWakefulnessLifecycle.addObserver(this);
if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
@@ -623,7 +620,7 @@ public class AuthContainerView extends LinearLayout
}
if (savedState != null) {
- mSkipFirstLostFocus = savedState.getBoolean(
+ mIsOrientationChanged = savedState.getBoolean(
AuthDialog.KEY_BIOMETRIC_ORIENTATION_CHANGED);
}
@@ -717,9 +714,7 @@ public class AuthContainerView extends LinearLayout
mBiometricView != null && mCredentialView == null);
outState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, mCredentialView != null);
- if (mOrientation != getResources().getConfiguration().orientation) {
- outState.putBoolean(AuthDialog.KEY_BIOMETRIC_ORIENTATION_CHANGED, true);
- }
+ outState.putBoolean(AuthDialog.KEY_BIOMETRIC_ORIENTATION_CHANGED, mIsOrientationChanged);
if (mBiometricView != null) {
mBiometricView.onSaveState(outState);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index eedf423744a8..eb974dd909be 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -782,8 +782,17 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
mUdfpsBounds = udfpsProp.getLocation().getRect();
mUdfpsBounds.scale(mScaleFactor);
- final UdfpsOverlayParams overlayParams = new UdfpsOverlayParams(mUdfpsBounds,
- mCachedDisplayInfo.getNaturalWidth(), mCachedDisplayInfo.getNaturalHeight(),
+ final Rect overlayBounds = new Rect(
+ 0, /* left */
+ mCachedDisplayInfo.getNaturalHeight() / 2, /* top */
+ mCachedDisplayInfo.getNaturalWidth(), /* right */
+ mCachedDisplayInfo.getNaturalHeight() /* bottom */);
+
+ final UdfpsOverlayParams overlayParams = new UdfpsOverlayParams(
+ mUdfpsBounds,
+ overlayBounds,
+ mCachedDisplayInfo.getNaturalWidth(),
+ mCachedDisplayInfo.getNaturalHeight(),
mScaleFactor, mCachedDisplayInfo.rotation);
mUdfpsController.updateOverlayParams(udfpsProp, overlayParams);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index c93fe6ac9f34..4b57d455a137 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -29,7 +29,7 @@ import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
import com.android.systemui.animation.Interpolators
-import com.android.systemui.ripple.RippleShader
+import com.android.systemui.surfaceeffects.ripple.RippleShader
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 71711a20c020..bc10868e36c8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -67,7 +67,7 @@ import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -140,7 +140,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull private final LatencyTracker mLatencyTracker;
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
- @NonNull private final BouncerInteractor mBouncerInteractor;
+ @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -229,7 +229,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mUdfpsDisplayMode, requestId, reason, callback,
(view, event, fromUdfpsView) -> onTouch(requestId, event,
fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags,
- mBouncerInteractor)));
+ mPrimaryBouncerInteractor)));
}
@Override
@@ -320,14 +320,14 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (!mOverlayParams.equals(overlayParams)) {
mOverlayParams = overlayParams;
- final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
+ final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateBouncer();
// When the bounds change it's always necessary to re-create the overlay's window with
// new LayoutParams. If the overlay needs to be shown, this will re-create and show the
// overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden.
redrawOverlay();
if (wasShowingAltAuth) {
- mKeyguardViewManager.showGenericBouncer(true);
+ mKeyguardViewManager.showBouncer(true);
}
}
}
@@ -626,7 +626,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
@NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider,
@NonNull @BiometricsBackground Executor biometricsExecutor,
- @NonNull BouncerInteractor bouncerInteractor) {
+ @NonNull PrimaryBouncerInteractor primaryBouncerInteractor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -643,6 +643,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mDumpManager = dumpManager;
mDialogManager = dialogManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mFeatureFlags = featureFlags;
mFalsingManager = falsingManager;
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
@@ -664,8 +665,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
false /* resetLockoutRequiresHardwareAuthToken */);
mBiometricExecutor = biometricsExecutor;
- mFeatureFlags = featureFlags;
- mBouncerInteractor = bouncerInteractor;
+ mPrimaryBouncerInteractor = primaryBouncerInteractor;
mDumpManager.registerDumpable(TAG, this);
@@ -757,8 +757,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
onFingerUp(mOverlay.getRequestId(), oldView);
}
final boolean removed = mOverlay.hide();
- if (mKeyguardViewManager.isShowingAlternateAuth()) {
- mKeyguardViewManager.resetAlternateAuth(true);
+ if (mKeyguardViewManager.isShowingAlternateBouncer()) {
+ mKeyguardViewManager.hideAlternateBouncer(true);
}
Log.v(TAG, "hideUdfpsOverlay | removing window: " + removed);
} else {
@@ -798,7 +798,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
Log.v(TAG, "aod lock icon long-press rejected by the falsing manager.");
return;
}
- mKeyguardViewManager.showBouncer(true);
+ mKeyguardViewManager.showPrimaryBouncer(true);
// play the same haptic as the LockIconViewController longpress
mVibrator.vibrate(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index d70861ac5f19..0bb24f8663ec 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -49,7 +49,7 @@ import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -95,7 +95,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
private val activityLaunchAnimator: ActivityLaunchAnimator,
private val featureFlags: FeatureFlags,
- private val bouncerInteractor: BouncerInteractor,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val isDebuggable: Boolean = Build.IS_DEBUGGABLE
) {
/** The view, when [isShowing], or null. */
@@ -252,7 +252,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
controller,
activityLaunchAnimator,
featureFlags,
- bouncerInteractor
+ primaryBouncerInteractor
)
}
REASON_AUTH_BP -> {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
index 5bae2dc502d6..91967f95c861 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
@@ -31,7 +31,7 @@ import com.android.systemui.animation.Interpolators
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionListener
@@ -40,9 +40,9 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.KeyguardBouncer
-import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback
+import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.AlternateAuthInterceptor
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.AlternateBouncer
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
@@ -73,7 +73,7 @@ constructor(
private val udfpsController: UdfpsController,
private val activityLaunchAnimator: ActivityLaunchAnimator,
featureFlags: FeatureFlags,
- private val bouncerInteractor: BouncerInteractor
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor
) :
UdfpsAnimationViewController<UdfpsKeyguardView>(
view,
@@ -146,8 +146,8 @@ constructor(
}
}
- private val bouncerExpansionCallback: BouncerExpansionCallback =
- object : BouncerExpansionCallback {
+ private val mPrimaryBouncerExpansionCallback: PrimaryBouncerExpansionCallback =
+ object : PrimaryBouncerExpansionCallback {
override fun onExpansionChanged(expansion: Float) {
inputBouncerHiddenAmount = expansion
updateAlpha()
@@ -180,7 +180,7 @@ constructor(
private val shadeExpansionListener = ShadeExpansionListener { (fraction) ->
panelExpansionFraction =
- if (keyguardViewManager.isBouncerInTransit) {
+ if (keyguardViewManager.isPrimaryBouncerInTransit) {
aboutToShowBouncerProgress(fraction)
} else {
fraction
@@ -237,17 +237,17 @@ constructor(
}
}
- private val alternateAuthInterceptor: AlternateAuthInterceptor =
- object : AlternateAuthInterceptor {
- override fun showAlternateAuthBouncer(): Boolean {
+ private val mAlternateBouncer: AlternateBouncer =
+ object : AlternateBouncer {
+ override fun showAlternateBouncer(): Boolean {
return showUdfpsBouncer(true)
}
- override fun hideAlternateAuthBouncer(): Boolean {
+ override fun hideAlternateBouncer(): Boolean {
return showUdfpsBouncer(false)
}
- override fun isShowingAlternateAuthBouncer(): Boolean {
+ override fun isShowingAlternateBouncer(): Boolean {
return showingUdfpsBouncer
}
@@ -268,7 +268,7 @@ constructor(
override fun onInit() {
super.onInit()
- keyguardViewManager.setAlternateAuthInterceptor(alternateAuthInterceptor)
+ keyguardViewManager.setAlternateBouncer(mAlternateBouncer)
}
init {
@@ -285,7 +285,7 @@ constructor(
@VisibleForTesting
internal suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
return scope.launch {
- bouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
+ primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
inputBouncerExpansion = bouncerExpansion
updateAlpha()
updatePauseAuth()
@@ -306,10 +306,10 @@ constructor(
qsExpansion = keyguardViewManager.qsExpansion
keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback)
if (!isModernBouncerEnabled) {
- val bouncer = keyguardViewManager.bouncer
+ val bouncer = keyguardViewManager.primaryBouncer
bouncer?.expansion?.let {
- bouncerExpansionCallback.onExpansionChanged(it)
- bouncer.addBouncerExpansionCallback(bouncerExpansionCallback)
+ mPrimaryBouncerExpansionCallback.onExpansionChanged(it)
+ bouncer.addBouncerExpansionCallback(mPrimaryBouncerExpansionCallback)
}
updateBouncerHiddenAmount()
}
@@ -319,7 +319,7 @@ constructor(
view.updatePadding()
updateAlpha()
updatePauseAuth()
- keyguardViewManager.setAlternateAuthInterceptor(alternateAuthInterceptor)
+ keyguardViewManager.setAlternateBouncer(mAlternateBouncer)
lockScreenShadeTransitionController.udfpsKeyguardViewController = this
activityLaunchAnimator.addListener(activityLaunchAnimatorListener)
}
@@ -329,7 +329,7 @@ constructor(
faceDetectRunning = false
keyguardStateController.removeCallback(keyguardStateControllerCallback)
statusBarStateController.removeCallback(stateListener)
- keyguardViewManager.removeAlternateAuthInterceptor(alternateAuthInterceptor)
+ keyguardViewManager.removeAlternateAuthInterceptor(mAlternateBouncer)
keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false)
configurationController.removeCallback(configurationListener)
shadeExpansionStateManager.removeExpansionListener(shadeExpansionListener)
@@ -339,7 +339,9 @@ constructor(
activityLaunchAnimator.removeListener(activityLaunchAnimatorListener)
keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback)
if (!isModernBouncerEnabled) {
- keyguardViewManager.bouncer?.removeBouncerExpansionCallback(bouncerExpansionCallback)
+ keyguardViewManager.primaryBouncer?.removeBouncerExpansionCallback(
+ mPrimaryBouncerExpansionCallback
+ )
}
}
@@ -442,7 +444,7 @@ constructor(
return if (isModernBouncerEnabled) {
inputBouncerExpansion == 1f
} else {
- keyguardViewManager.isBouncerShowing && !keyguardViewManager.isShowingAlternateAuth
+ keyguardViewManager.isBouncerShowing && !keyguardViewManager.isShowingAlternateBouncer
}
}
@@ -462,7 +464,7 @@ constructor(
*/
private fun maybeShowInputBouncer() {
if (showingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
- keyguardViewManager.showBouncer(true)
+ keyguardViewManager.showPrimaryBouncer(true)
}
}
@@ -535,8 +537,8 @@ constructor(
if (isModernBouncerEnabled) {
return
}
- val altBouncerShowing = keyguardViewManager.isShowingAlternateAuth
- if (altBouncerShowing || !keyguardViewManager.bouncerIsOrWillBeShowing()) {
+ val altBouncerShowing = keyguardViewManager.isShowingAlternateBouncer
+ if (altBouncerShowing || !keyguardViewManager.primaryBouncerIsOrWillBeShowing()) {
inputBouncerHiddenAmount = 1f
} else if (keyguardViewManager.isBouncerShowing) {
// input bouncer is fully showing
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
index d725dfbfe216..98d4c22d927d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
@@ -20,6 +20,7 @@ import android.view.Surface.Rotation
data class UdfpsOverlayParams(
val sensorBounds: Rect = Rect(),
+ val overlayBounds: Rect = Rect(),
val naturalDisplayWidth: Int = 0,
val naturalDisplayHeight: Int = 0,
val scaleFactor: Float = 1f,
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 616e49c0b709..1454210ad798 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -31,7 +31,7 @@ import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.ripple.RippleView
+import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index e82d0ea85490..3808ab742419 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -30,7 +30,7 @@ import android.view.WindowManager;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.ripple.RippleShader.RippleShape;
+import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
/**
* A WirelessChargingAnimation is a view containing view + animation for wireless charging.
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 145569919e8e..36103f8db8d8 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -33,9 +33,9 @@ import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.ripple.RippleAnimationConfig;
-import com.android.systemui.ripple.RippleShader.RippleShape;
-import com.android.systemui.ripple.RippleView;
+import com.android.systemui.surfaceeffects.ripple.RippleAnimationConfig;
+import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
+import com.android.systemui.surfaceeffects.ripple.RippleView;
import java.text.NumberFormat;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
index 588ef5c4e68f..4dfcd6398a4d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
@@ -16,16 +16,120 @@
package com.android.systemui.controls
+import android.Manifest
+import android.content.ComponentName
import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE
+import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
+import android.os.UserHandle
+import android.service.controls.ControlsProviderService
+import androidx.annotation.WorkerThread
import com.android.settingslib.applications.DefaultAppInfo
+import java.util.Objects
class ControlsServiceInfo(
- context: Context,
+ private val context: Context,
val serviceInfo: ServiceInfo
) : DefaultAppInfo(
context,
context.packageManager,
context.userId,
serviceInfo.componentName
-) \ No newline at end of file
+) {
+ private val _panelActivity: ComponentName?
+
+ init {
+ val metadata = serviceInfo.metaData
+ ?.getString(ControlsProviderService.META_DATA_PANEL_ACTIVITY) ?: ""
+ val unflatenned = ComponentName.unflattenFromString(metadata)
+ if (unflatenned != null && unflatenned.packageName == componentName.packageName) {
+ _panelActivity = unflatenned
+ } else {
+ _panelActivity = null
+ }
+ }
+
+ /**
+ * Component name of an activity that will be shown embedded in the device controls space
+ * instead of using the controls rendered by SystemUI.
+ *
+ * The activity must be in the same package, exported, enabled and protected by the
+ * [Manifest.permission.BIND_CONTROLS] permission.
+ */
+ var panelActivity: ComponentName? = null
+ private set
+
+ private var resolved: Boolean = false
+
+ @WorkerThread
+ fun resolvePanelActivity() {
+ if (resolved) return
+ resolved = true
+ panelActivity = _panelActivity?.let {
+ val resolveInfos = mPm.queryIntentActivitiesAsUser(
+ Intent().setComponent(it),
+ PackageManager.ResolveInfoFlags.of(
+ MATCH_DIRECT_BOOT_AWARE.toLong() or
+ MATCH_DIRECT_BOOT_UNAWARE.toLong()
+ ),
+ UserHandle.of(userId)
+ )
+ if (resolveInfos.isNotEmpty() && verifyResolveInfo(resolveInfos[0])) {
+ it
+ } else {
+ null
+ }
+ }
+ }
+
+ /**
+ * Verifies that the panel activity is enabled, exported and protected by the correct
+ * permission. This last check is to prevent apps from forgetting to protect the activity, as
+ * they won't be able to see the panel until they do.
+ */
+ @WorkerThread
+ private fun verifyResolveInfo(resolveInfo: ResolveInfo): Boolean {
+ return resolveInfo.activityInfo?.let {
+ it.permission == Manifest.permission.BIND_CONTROLS &&
+ it.exported && isComponentActuallyEnabled(it)
+ } ?: false
+ }
+
+ @WorkerThread
+ private fun isComponentActuallyEnabled(activityInfo: ActivityInfo): Boolean {
+ return when (mPm.getComponentEnabledSetting(activityInfo.componentName)) {
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> true
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> false
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT -> activityInfo.enabled
+ else -> false
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is ControlsServiceInfo &&
+ userId == other.userId &&
+ componentName == other.componentName &&
+ panelActivity == other.panelActivity
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(userId, componentName, panelActivity)
+ }
+
+ fun copy(): ControlsServiceInfo {
+ return ControlsServiceInfo(context, serviceInfo).also {
+ it.panelActivity = this.panelActivity
+ }
+ }
+
+ override fun toString(): String {
+ return """
+ ControlsServiceInfo(serviceInfo=$serviceInfo, panelActivity=$panelActivity, resolved=$resolved)
+ """.trimIndent()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 5e8ce6db971c..b11103a4d27b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -20,11 +20,14 @@ import android.app.ActivityOptions
import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
+import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import android.widget.Button
import android.widget.TextView
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
@@ -42,7 +45,7 @@ import javax.inject.Inject
/**
* Activity for rearranging and removing controls for a given structure
*/
-class ControlsEditingActivity @Inject constructor(
+open class ControlsEditingActivity @Inject constructor(
private val controller: ControlsControllerImpl,
private val broadcastDispatcher: BroadcastDispatcher,
private val customIconCache: CustomIconCache,
@@ -50,8 +53,9 @@ class ControlsEditingActivity @Inject constructor(
) : ComponentActivity() {
companion object {
+ private const val DEBUG = false
private const val TAG = "ControlsEditingActivity"
- private const val EXTRA_STRUCTURE = ControlsFavoritingActivity.EXTRA_STRUCTURE
+ const val EXTRA_STRUCTURE = ControlsFavoritingActivity.EXTRA_STRUCTURE
private val SUBTITLE_ID = R.string.controls_favorite_rearrange
private val EMPTY_TEXT_ID = R.string.controls_favorite_removed
}
@@ -73,6 +77,13 @@ class ControlsEditingActivity @Inject constructor(
}
}
+ private val mOnBackInvokedCallback = OnBackInvokedCallback {
+ if (DEBUG) {
+ Log.d(TAG, "Predictive Back dispatcher called mOnBackInvokedCallback")
+ }
+ onBackPressed()
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -94,11 +105,22 @@ class ControlsEditingActivity @Inject constructor(
setUpList()
currentUserTracker.startTracking()
+
+ if (DEBUG) {
+ Log.d(TAG, "Registered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback)
}
override fun onStop() {
super.onStop()
currentUserTracker.stopTracking()
+
+ if (DEBUG) {
+ Log.d(TAG, "Unregistered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.unregisterOnBackInvokedCallback(mOnBackInvokedCallback)
}
override fun onBackPressed() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index be572c503bda..9b2a72823d86 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -24,6 +24,7 @@ import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
import android.text.TextUtils
+import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
@@ -32,6 +33,8 @@ import android.widget.Button
import android.widget.FrameLayout
import android.widget.TextView
import android.widget.Toast
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
import androidx.viewpager2.widget.ViewPager2
import com.android.systemui.Prefs
@@ -50,7 +53,7 @@ import java.util.concurrent.Executor
import java.util.function.Consumer
import javax.inject.Inject
-class ControlsFavoritingActivity @Inject constructor(
+open class ControlsFavoritingActivity @Inject constructor(
@Main private val executor: Executor,
private val controller: ControlsControllerImpl,
private val listingController: ControlsListingController,
@@ -59,6 +62,7 @@ class ControlsFavoritingActivity @Inject constructor(
) : ComponentActivity() {
companion object {
+ private const val DEBUG = false
private const val TAG = "ControlsFavoritingActivity"
// If provided and no structure is available, use as the title
@@ -67,7 +71,7 @@ class ControlsFavoritingActivity @Inject constructor(
// If provided, show this structure page first
const val EXTRA_STRUCTURE = "extra_structure"
const val EXTRA_SINGLE_STRUCTURE = "extra_single_structure"
- internal const val EXTRA_FROM_PROVIDER_SELECTOR = "extra_from_provider_selector"
+ const val EXTRA_FROM_PROVIDER_SELECTOR = "extra_from_provider_selector"
private const val TOOLTIP_PREFS_KEY = Prefs.Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
private const val TOOLTIP_MAX_SHOWN = 2
}
@@ -102,6 +106,13 @@ class ControlsFavoritingActivity @Inject constructor(
}
}
+ private val mOnBackInvokedCallback = OnBackInvokedCallback {
+ if (DEBUG) {
+ Log.d(TAG, "Predictive Back dispatcher called mOnBackInvokedCallback")
+ }
+ onBackPressed()
+ }
+
private val listingCallback = object : ControlsListingController.ControlsListingCallback {
override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
@@ -346,13 +357,19 @@ class ControlsFavoritingActivity @Inject constructor(
override fun onPause() {
super.onPause()
mTooltipManager?.hide(false)
- }
+ }
override fun onStart() {
super.onStart()
listingController.addCallback(listingCallback)
currentUserTracker.startTracking()
+
+ if (DEBUG) {
+ Log.d(TAG, "Registered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback)
}
override fun onResume() {
@@ -365,13 +382,19 @@ class ControlsFavoritingActivity @Inject constructor(
loadControls()
isPagerLoaded = true
}
- }
+ }
override fun onStop() {
super.onStop()
listingController.removeCallback(listingCallback)
currentUserTracker.stopTracking()
+
+ if (DEBUG) {
+ Log.d(TAG, "Unregistered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.unregisterOnBackInvokedCallback(
+ mOnBackInvokedCallback)
}
override fun onConfigurationChanged(newConfig: Configuration) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 2d76ff2774d6..115edd115ffe 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -18,17 +18,23 @@ package com.android.systemui.controls.management
import android.content.ComponentName
import android.content.Context
-import android.content.pm.ServiceInfo
import android.os.UserHandle
import android.service.controls.ControlsProviderService
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.applications.ServiceListing
import com.android.settingslib.widget.CandidateInfo
+import com.android.systemui.Dumpable
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.indentIfPossible
+import java.io.PrintWriter
import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject
@@ -57,16 +63,19 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
private val context: Context,
@Background private val backgroundExecutor: Executor,
private val serviceListingBuilder: (Context) -> ServiceListing,
- userTracker: UserTracker
-) : ControlsListingController {
+ private val userTracker: UserTracker,
+ dumpManager: DumpManager,
+ featureFlags: FeatureFlags
+) : ControlsListingController, Dumpable {
@Inject
- constructor(context: Context, executor: Executor, userTracker: UserTracker): this(
- context,
- executor,
- ::createServiceListing,
- userTracker
- )
+ constructor(
+ context: Context,
+ @Background executor: Executor,
+ userTracker: UserTracker,
+ dumpManager: DumpManager,
+ featureFlags: FeatureFlags
+ ) : this(context, executor, ::createServiceListing, userTracker, dumpManager, featureFlags)
private var serviceListing = serviceListingBuilder(context)
// All operations in background thread
@@ -76,27 +85,25 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
private const val TAG = "ControlsListingControllerImpl"
}
- private var availableComponents = emptySet<ComponentName>()
- private var availableServices = emptyList<ServiceInfo>()
+ private var availableServices = emptyList<ControlsServiceInfo>()
private var userChangeInProgress = AtomicInteger(0)
override var currentUserId = userTracker.userId
private set
private val serviceListingCallback = ServiceListing.Callback {
- val newServices = it.toList()
- val newComponents =
- newServices.mapTo(mutableSetOf<ComponentName>(), { s -> s.getComponentName() })
-
backgroundExecutor.execute {
if (userChangeInProgress.get() > 0) return@execute
- if (!newComponents.equals(availableComponents)) {
- Log.d(TAG, "ServiceConfig reloaded, count: ${newComponents.size}")
- availableComponents = newComponents
+ Log.d(TAG, "ServiceConfig reloaded, count: ${it.size}")
+ val newServices = it.map { ControlsServiceInfo(userTracker.userContext, it) }
+ if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
+ newServices.forEach(ControlsServiceInfo::resolvePanelActivity)
+ }
+
+ if (newServices != availableServices) {
availableServices = newServices
- val currentServices = getCurrentServices()
callbacks.forEach {
- it.onServicesUpdated(currentServices)
+ it.onServicesUpdated(getCurrentServices())
}
}
}
@@ -104,6 +111,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
init {
Log.d(TAG, "Initializing")
+ dumpManager.registerDumpable(TAG, this)
serviceListing.addCallback(serviceListingCallback)
serviceListing.setListening(true)
serviceListing.reload()
@@ -165,7 +173,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
* [ControlsProviderService]
*/
override fun getCurrentServices(): List<ControlsServiceInfo> =
- availableServices.map { ControlsServiceInfo(context, it) }
+ availableServices.map(ControlsServiceInfo::copy)
/**
* Get the localized label for the component.
@@ -174,7 +182,15 @@ class ControlsListingControllerImpl @VisibleForTesting constructor(
* @return a label as returned by [CandidateInfo.loadLabel] or `null`.
*/
override fun getAppLabel(name: ComponentName): CharSequence? {
- return getCurrentServices().firstOrNull { it.componentName == name }
+ return availableServices.firstOrNull { it.componentName == name }
?.loadLabel()
}
+
+ override fun dump(writer: PrintWriter, args: Array<out String>) {
+ writer.println("ControlsListingController:")
+ writer.asIndenting().indentIfPossible {
+ println("Callbacks: $callbacks")
+ println("Services: ${getCurrentServices()}")
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index b26615fe4702..47690a7fa487 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -20,16 +20,18 @@ import android.app.ActivityOptions
import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import android.widget.Button
import android.widget.TextView
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.controller.ControlsController
@@ -44,7 +46,7 @@ import javax.inject.Inject
/**
* Activity to select an application to favorite the [Control] provided by them.
*/
-class ControlsProviderSelectorActivity @Inject constructor(
+open class ControlsProviderSelectorActivity @Inject constructor(
@Main private val executor: Executor,
@Background private val backExecutor: Executor,
private val listingController: ControlsListingController,
@@ -54,6 +56,7 @@ class ControlsProviderSelectorActivity @Inject constructor(
) : ComponentActivity() {
companion object {
+ private const val DEBUG = false
private const val TAG = "ControlsProviderSelectorActivity"
const val BACK_SHOULD_EXIT = "back_should_exit"
}
@@ -70,6 +73,13 @@ class ControlsProviderSelectorActivity @Inject constructor(
}
}
+ private val mOnBackInvokedCallback = OnBackInvokedCallback {
+ if (DEBUG) {
+ Log.d(TAG, "Predictive Back dispatcher called mOnBackInvokedCallback")
+ }
+ onBackPressed()
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -141,11 +151,22 @@ class ControlsProviderSelectorActivity @Inject constructor(
}
})
}
+
+ if (DEBUG) {
+ Log.d(TAG, "Registered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback)
}
override fun onStop() {
super.onStop()
currentUserTracker.stopTracking()
+
+ if (DEBUG) {
+ Log.d(TAG, "Unregistered onBackInvokedCallback")
+ }
+ onBackInvokedDispatcher.unregisterOnBackInvokedCallback(mOnBackInvokedCallback)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 139a8b769583..b8030b2a0013 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.dagger;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
@@ -68,6 +69,7 @@ import android.os.PowerManager;
import android.os.ServiceManager;
import android.os.UserManager;
import android.os.Vibrator;
+import android.os.storage.StorageManager;
import android.permission.PermissionManager;
import android.safetycenter.SafetyCenterManager;
import android.service.dreams.DreamService;
@@ -109,6 +111,7 @@ import dagger.Provides;
/**
* Provides Non-SystemUI, Framework-Owned instances to the dependency graph.
*/
+@SuppressLint("NonInjectedService")
@Module
public class FrameworkServicesModule {
@Provides
@@ -462,7 +465,13 @@ public class FrameworkServicesModule {
@Provides
@Singleton
- static SubscriptionManager provideSubcriptionManager(Context context) {
+ static StorageManager provideStorageManager(Context context) {
+ return context.getSystemService(StorageManager.class);
+ }
+
+ @Provides
+ @Singleton
+ static SubscriptionManager provideSubscriptionManager(Context context) {
return context.getSystemService(SubscriptionManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index fe89c9a1e3b9..9e8c0ec7423e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -21,24 +21,21 @@ import android.content.Context;
import com.android.systemui.dagger.qualifiers.InstrumentationTest;
import com.android.systemui.util.InitializationChecker;
-import javax.inject.Singleton;
-
import dagger.BindsInstance;
-import dagger.Component;
/**
* Base root component for Dagger injection.
*
+ * This class is not actually annotated as a Dagger component, since it is not used directly as one.
+ * Doing so generates unnecessary code bloat.
+ *
* See {@link ReferenceGlobalRootComponent} for the one actually used by AOSP.
*/
-@Singleton
-@Component(modules = {GlobalModule.class})
public interface GlobalRootComponent {
/**
* Builder for a GlobalRootComponent.
*/
- @Component.Builder
interface Builder {
@BindsInstance
Builder context(Context context);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index 7ab36e84178e..d3555eec0243 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -16,6 +16,7 @@
package com.android.systemui.dagger;
+import com.android.systemui.keyguard.KeyguardQuickAffordanceProvider;
import com.android.systemui.statusbar.QsFrameTranslateModule;
import dagger.Subcomponent;
@@ -42,4 +43,9 @@ public interface ReferenceSysUIComponent extends SysUIComponent {
interface Builder extends SysUIComponent.Builder {
ReferenceSysUIComponent build();
}
+
+ /**
+ * Member injection into the supplied argument.
+ */
+ void inject(KeyguardQuickAffordanceProvider keyguardQuickAffordanceProvider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 48bef97c30fb..fd690dfd5dfa 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -44,6 +44,7 @@ import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeControllerImpl;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
@@ -162,7 +163,8 @@ public abstract class ReferenceSystemUIModule {
ConfigurationController configurationController,
@Main Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ ShadeExpansionStateManager shadeExpansionStateManager) {
return new HeadsUpManagerPhone(
context,
headsUpManagerLogger,
@@ -173,7 +175,8 @@ public abstract class ReferenceSystemUIModule {
configurationController,
handler,
accessibilityManagerWrapper,
- uiEventLogger
+ uiEventLogger,
+ shadeExpansionStateManager
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index d0d01846c3d9..5c6d24813570 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -35,7 +35,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.dagger.DreamOverlayModule;
-import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -76,14 +76,14 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
// Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
private final Handler mHandler;
private final int mDreamOverlayMaxTranslationY;
- private final BouncerCallbackInteractor mBouncerCallbackInteractor;
+ private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
private long mJitterStartTimeMillis;
private boolean mBouncerAnimating;
- private final KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback =
- new KeyguardBouncer.BouncerExpansionCallback() {
+ private final KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback =
+ new KeyguardBouncer.PrimaryBouncerExpansionCallback() {
@Override
public void onStartingToShow() {
@@ -136,7 +136,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
@Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
burnInProtectionUpdateInterval,
@Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter,
- BouncerCallbackInteractor bouncerCallbackInteractor,
+ PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor,
DreamOverlayAnimationsController animationsController,
DreamOverlayStateController stateController) {
super(containerView);
@@ -160,7 +160,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
mMaxBurnInOffset = maxBurnInOffset;
mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
mMillisUntilFullJitter = millisUntilFullJitter;
- mBouncerCallbackInteractor = bouncerCallbackInteractor;
+ mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor;
}
@Override
@@ -173,11 +173,11 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
protected void onViewAttached() {
mJitterStartTimeMillis = System.currentTimeMillis();
mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
- final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getBouncer();
+ final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getPrimaryBouncer();
if (bouncer != null) {
bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback);
}
- mBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback);
+ mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback);
// Start dream entry animations. Skip animations for low light clock.
if (!mStateController.isLowLightActive()) {
@@ -188,11 +188,11 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
@Override
protected void onViewDetached() {
mHandler.removeCallbacks(this::updateBurnInOffsets);
- final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getBouncer();
+ final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getPrimaryBouncer();
if (bouncer != null) {
bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback);
}
- mBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
+ mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
mDreamOverlayAnimationsController.cancelRunningEntryAnimations();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index 5694f6da0ea5..440dcbc18a12 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -194,7 +194,9 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll
break;
}
- if (!isRoot) {
+ // Add margin if specified by the complication. Otherwise add default margin
+ // between complications.
+ if (mLayoutParams.isMarginSpecified() || !isRoot) {
final int margin = mLayoutParams.getMargin(mDefaultMargin);
switch(direction) {
case ComplicationLayoutParams.DIRECTION_DOWN:
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
index a21eb19bd548..2b32d349dd67 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
@@ -261,6 +261,13 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
}
/**
+ * Returns whether margin has been specified by the complication.
+ */
+ public boolean isMarginSpecified() {
+ return mMargin != MARGIN_UNSPECIFIED;
+ }
+
+ /**
* Returns the margin to apply between complications, or the given default if no margin is
* specified.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
index cedd850ac2ef..c01cf43eae74 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
@@ -33,6 +33,7 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.CoreStartable;
import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.controls.ControlsServiceInfo;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.controls.management.ControlsListingController;
import com.android.systemui.controls.ui.ControlsActivity;
@@ -42,6 +43,8 @@ import com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplica
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.util.ViewController;
+import java.util.List;
+
import javax.inject.Inject;
import javax.inject.Named;
@@ -76,16 +79,25 @@ public class DreamHomeControlsComplication implements Complication {
private final DreamOverlayStateController mDreamOverlayStateController;
private final ControlsComponent mControlsComponent;
- private boolean mControlServicesAvailable = false;
+ private boolean mOverlayActive = false;
// Callback for when the home controls service availability changes.
private final ControlsListingController.ControlsListingCallback mControlsCallback =
- serviceInfos -> {
- boolean available = !serviceInfos.isEmpty();
+ services -> updateHomeControlsComplication();
+
+ private final DreamOverlayStateController.Callback mOverlayStateCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ if (mOverlayActive == mDreamOverlayStateController.isOverlayActive()) {
+ return;
+ }
- if (available != mControlServicesAvailable) {
- mControlServicesAvailable = available;
- updateComplicationAvailability();
+ mOverlayActive = !mOverlayActive;
+
+ if (mOverlayActive) {
+ updateHomeControlsComplication();
+ }
}
};
@@ -102,18 +114,29 @@ public class DreamHomeControlsComplication implements Complication {
public void start() {
mControlsComponent.getControlsListingController().ifPresent(
c -> c.addCallback(mControlsCallback));
+ mDreamOverlayStateController.addCallback(mOverlayStateCallback);
+ }
+
+ private void updateHomeControlsComplication() {
+ mControlsComponent.getControlsListingController().ifPresent(c -> {
+ if (isHomeControlsAvailable(c.getCurrentServices())) {
+ mDreamOverlayStateController.addComplication(mComplication);
+ } else {
+ mDreamOverlayStateController.removeComplication(mComplication);
+ }
+ });
}
- private void updateComplicationAvailability() {
+ private boolean isHomeControlsAvailable(List<ControlsServiceInfo> controlsServices) {
+ if (controlsServices.isEmpty()) {
+ return false;
+ }
+
final boolean hasFavorites = mControlsComponent.getControlsController()
.map(c -> !c.getFavorites().isEmpty())
.orElse(false);
- if (!hasFavorites || !mControlServicesAvailable
- || mControlsComponent.getVisibility() == UNAVAILABLE) {
- mDreamOverlayStateController.removeComplication(mComplication);
- } else {
- mDreamOverlayStateController.addComplication(mComplication);
- }
+ final ControlsComponent.Visibility visibility = mControlsComponent.getVisibility();
+ return hasFavorites && visibility != UNAVAILABLE;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index 7d2ce51ffbf6..69b85b5a5e51 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -48,9 +48,9 @@ public interface RegisteredComplicationsModule {
int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT = 1;
int DREAM_SMARTSPACE_COMPLICATION_WEIGHT = 0;
- int DREAM_MEDIA_COMPLICATION_WEIGHT = -1;
- int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 1;
- int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 0;
+ int DREAM_MEDIA_COMPLICATION_WEIGHT = 0;
+ int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 2;
+ int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 1;
/**
* Provides layout parameters for the clock time complication.
@@ -60,10 +60,11 @@ public interface RegisteredComplicationsModule {
static ComplicationLayoutParams provideClockTimeLayoutParams() {
return new ComplicationLayoutParams(0,
ViewGroup.LayoutParams.WRAP_CONTENT,
- ComplicationLayoutParams.POSITION_TOP
+ ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_DOWN,
- DREAM_CLOCK_TIME_COMPLICATION_WEIGHT);
+ ComplicationLayoutParams.DIRECTION_UP,
+ DREAM_CLOCK_TIME_COMPLICATION_WEIGHT,
+ 0 /*margin*/);
}
/**
@@ -77,8 +78,10 @@ public interface RegisteredComplicationsModule {
res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_END,
- DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
+ ComplicationLayoutParams.DIRECTION_UP,
+ DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT,
+ // Add margin to the bottom of home controls to horizontally align with smartspace.
+ res.getDimensionPixelSize(R.dimen.dream_overlay_complication_clock_time_padding));
}
/**
@@ -101,14 +104,13 @@ public interface RegisteredComplicationsModule {
*/
@Provides
@Named(DREAM_SMARTSPACE_LAYOUT_PARAMS)
- static ComplicationLayoutParams provideSmartspaceLayoutParams() {
+ static ComplicationLayoutParams provideSmartspaceLayoutParams(@Main Resources res) {
return new ComplicationLayoutParams(0,
ViewGroup.LayoutParams.WRAP_CONTENT,
- ComplicationLayoutParams.POSITION_TOP
+ ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_DOWN,
+ ComplicationLayoutParams.DIRECTION_END,
DREAM_SMARTSPACE_COMPLICATION_WEIGHT,
- 0,
- true /*snapToGuide*/);
+ res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 0dba4ff84c99..92cdcf99f013 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -116,7 +116,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
if (mCapture) {
// Since the user is dragging the bouncer up, set scrimmed to false.
- mStatusBarKeyguardViewManager.showBouncer(false);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
index a49aaccf92ba..95e7ad969e35 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -34,9 +34,6 @@ interface FeatureFlags : FlagListenable, Dumpable {
fun isEnabled(flag: ResourceBooleanFlag): Boolean
/** Returns a boolean value for the given flag. */
- fun isEnabled(flag: DeviceConfigBooleanFlag): Boolean
-
- /** Returns a boolean value for the given flag. */
fun isEnabled(flag: SysPropBooleanFlag): Boolean
/** Returns a string value for the given flag. */
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index 98d10a77e2d1..ec3fdecb919a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -39,7 +39,6 @@ import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.settings.SecureSettings;
import org.jetbrains.annotations.NotNull;
@@ -76,7 +75,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
private final SecureSettings mSecureSettings;
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
- private final DeviceConfigProxy mDeviceConfigProxy;
private final ServerFlagReader mServerFlagReader;
private final Map<Integer, Flag<?>> mAllFlags;
private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
@@ -99,7 +97,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
SecureSettings secureSettings,
SystemPropertiesHelper systemProperties,
@Main Resources resources,
- DeviceConfigProxy deviceConfigProxy,
ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
Restarter restarter) {
@@ -108,7 +105,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
mSecureSettings = secureSettings;
mResources = resources;
mSystemProperties = systemProperties;
- mDeviceConfigProxy = deviceConfigProxy;
mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
mRestarter = restarter;
@@ -140,7 +136,7 @@ public class FeatureFlagsDebug implements FeatureFlags {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id,
- readFlagValue(id, flag.getDefault()));
+ readBooleanFlagInternal(flag, flag.getDefault()));
}
return mBooleanFlagCache.get(id);
@@ -151,19 +147,7 @@ public class FeatureFlagsDebug implements FeatureFlags {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id,
- readFlagValue(id, mResources.getBoolean(flag.getResourceId())));
- }
-
- return mBooleanFlagCache.get(id);
- }
-
- @Override
- public boolean isEnabled(@NonNull DeviceConfigBooleanFlag flag) {
- int id = flag.getId();
- if (!mBooleanFlagCache.containsKey(id)) {
- boolean deviceConfigValue = mDeviceConfigProxy.getBoolean(flag.getNamespace(),
- flag.getName(), flag.getDefault());
- mBooleanFlagCache.put(id, readFlagValue(id, deviceConfigValue));
+ readBooleanFlagInternal(flag, mResources.getBoolean(flag.getResourceId())));
}
return mBooleanFlagCache.get(id);
@@ -179,7 +163,7 @@ public class FeatureFlagsDebug implements FeatureFlags {
id,
mSystemProperties.getBoolean(
flag.getName(),
- readFlagValue(id, flag.getDefault())));
+ readBooleanFlagInternal(flag, flag.getDefault())));
}
return mBooleanFlagCache.get(id);
@@ -191,7 +175,7 @@ public class FeatureFlagsDebug implements FeatureFlags {
int id = flag.getId();
if (!mStringFlagCache.containsKey(id)) {
mStringFlagCache.put(id,
- readFlagValue(id, flag.getDefault(), StringFlagSerializer.INSTANCE));
+ readFlagValueInternal(id, flag.getDefault(), StringFlagSerializer.INSTANCE));
}
return mStringFlagCache.get(id);
@@ -203,20 +187,21 @@ public class FeatureFlagsDebug implements FeatureFlags {
int id = flag.getId();
if (!mStringFlagCache.containsKey(id)) {
mStringFlagCache.put(id,
- readFlagValue(id, mResources.getString(flag.getResourceId()),
+ readFlagValueInternal(id, mResources.getString(flag.getResourceId()),
StringFlagSerializer.INSTANCE));
}
return mStringFlagCache.get(id);
}
+
@NonNull
@Override
public int getInt(@NonNull IntFlag flag) {
int id = flag.getId();
if (!mIntFlagCache.containsKey(id)) {
mIntFlagCache.put(id,
- readFlagValue(id, flag.getDefault(), IntFlagSerializer.INSTANCE));
+ readFlagValueInternal(id, flag.getDefault(), IntFlagSerializer.INSTANCE));
}
return mIntFlagCache.get(id);
@@ -228,27 +213,31 @@ public class FeatureFlagsDebug implements FeatureFlags {
int id = flag.getId();
if (!mIntFlagCache.containsKey(id)) {
mIntFlagCache.put(id,
- readFlagValue(id, mResources.getInteger(flag.getResourceId()),
+ readFlagValueInternal(id, mResources.getInteger(flag.getResourceId()),
IntFlagSerializer.INSTANCE));
}
return mIntFlagCache.get(id);
}
- /** Specific override for Boolean flags that checks against the teamfood list. */
- private boolean readFlagValue(int id, boolean defaultValue) {
- Boolean result = readBooleanFlagOverride(id);
- boolean hasServerOverride = mServerFlagReader.hasOverride(id);
+ /** Specific override for Boolean flags that checks against the teamfood list.*/
+ private boolean readBooleanFlagInternal(Flag<Boolean> flag, boolean defaultValue) {
+ Boolean result = readBooleanFlagOverride(flag.getId());
+ boolean hasServerOverride = mServerFlagReader.hasOverride(
+ flag.getNamespace(), flag.getName());
// Only check for teamfood if the default is false
// and there is no server override.
- if (!hasServerOverride && !defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
- if (mAllFlags.containsKey(id) && mAllFlags.get(id).getTeamfood()) {
- return isEnabled(Flags.TEAMFOOD);
- }
+ if (!hasServerOverride
+ && !defaultValue
+ && result == null
+ && flag.getId() != Flags.TEAMFOOD.getId()
+ && flag.getTeamfood()) {
+ return isEnabled(Flags.TEAMFOOD);
}
- return result == null ? mServerFlagReader.readServerOverride(id, defaultValue) : result;
+ return result == null ? mServerFlagReader.readServerOverride(
+ flag.getNamespace(), flag.getName(), defaultValue) : result;
}
private Boolean readBooleanFlagOverride(int id) {
@@ -256,7 +245,8 @@ public class FeatureFlagsDebug implements FeatureFlags {
}
@NonNull
- private <T> T readFlagValue(int id, @NonNull T defaultValue, FlagSerializer<T> serializer) {
+ private <T> T readFlagValueInternal(
+ int id, @NonNull T defaultValue, FlagSerializer<T> serializer) {
requireNonNull(defaultValue, "defaultValue");
T result = readFlagValueInternal(id, serializer);
return result == null ? defaultValue : result;
@@ -355,8 +345,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceBooleanFlag) {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
- } else if (flag instanceof DeviceConfigBooleanFlag) {
- setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof SysPropBooleanFlag) {
// Store SysProp flags in SystemProperties where they can read by outside parties.
mSystemProperties.setBoolean(((SysPropBooleanFlag) flag).getName(), value);
@@ -474,9 +462,6 @@ public class FeatureFlagsDebug implements FeatureFlags {
} else if (f instanceof ResourceBooleanFlag) {
enabled = isEnabled((ResourceBooleanFlag) f);
overridden = readBooleanFlagOverride(f.getId()) != null;
- } else if (f instanceof DeviceConfigBooleanFlag) {
- enabled = isEnabled((DeviceConfigBooleanFlag) f);
- overridden = false;
} else if (f instanceof SysPropBooleanFlag) {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet.
enabled = isEnabled((SysPropBooleanFlag) f);
@@ -489,9 +474,11 @@ public class FeatureFlagsDebug implements FeatureFlags {
}
if (enabled) {
- return new ReleasedFlag(f.getId(), teamfood, overridden);
+ return new ReleasedFlag(
+ f.getId(), f.getName(), f.getNamespace(), teamfood, overridden);
} else {
- return new UnreleasedFlag(f.getId(), teamfood, overridden);
+ return new UnreleasedFlag(
+ f.getId(), f.getName(), f.getNamespace(), teamfood, overridden);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 8996d5258920..3c83682210b6 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -101,7 +101,7 @@ public class FeatureFlagsRelease implements FeatureFlags {
@Override
public boolean isEnabled(@NotNull ReleasedFlag flag) {
- return mServerFlagReader.readServerOverride(flag.getId(), true);
+ return mServerFlagReader.readServerOverride(flag.getNamespace(), flag.getName(), true);
}
@Override
@@ -115,18 +115,6 @@ public class FeatureFlagsRelease implements FeatureFlags {
}
@Override
- public boolean isEnabled(@NonNull DeviceConfigBooleanFlag flag) {
- int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
- if (cacheIndex < 0) {
- boolean deviceConfigValue = mDeviceConfigProxy.getBoolean(flag.getNamespace(),
- flag.getName(), flag.getDefault());
- return isEnabled(flag.getId(), deviceConfigValue);
- }
-
- return mBooleanCache.valueAt(cacheIndex);
- }
-
- @Override
public boolean isEnabled(SysPropBooleanFlag flag) {
int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
if (cacheIndex < 0) {
@@ -180,10 +168,10 @@ public class FeatureFlagsRelease implements FeatureFlags {
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: false");
- Map<Integer, Flag<?>> knownFlags = Flags.collectFlags();
- for (Map.Entry<Integer, Flag<?>> idToFlag : knownFlags.entrySet()) {
- int id = idToFlag.getKey();
- Flag<?> flag = idToFlag.getValue();
+ Map<String, Flag<?>> knownFlags = FlagsFactory.INSTANCE.getKnownFlags();
+ for (Map.Entry<String, Flag<?>> nameToFlag : knownFlags.entrySet()) {
+ Flag<?> flag = nameToFlag.getValue();
+ int id = flag.getId();
boolean def = false;
if (mBooleanCache.indexOfKey(flag.getId()) < 0) {
if (flag instanceof SysPropBooleanFlag) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
index ad4b87da0744..b7fc0e41ea69 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
@@ -229,7 +229,7 @@ public class FlagCommand implements Command {
}
private int flagNameToId(String flagName) {
- Map<String, Flag<?>> flagFields = Flags.getFlagFields();
+ Map<String, Flag<?>> flagFields = FlagsFactory.INSTANCE.getKnownFlags();
for (String fieldName : flagFields.keySet()) {
if (flagName.equals(fieldName)) {
return flagFields.get(fieldName).getId();
@@ -240,7 +240,7 @@ public class FlagCommand implements Command {
}
private void printKnownFlags(PrintWriter pw) {
- Map<String, Flag<?>> fields = Flags.getFlagFields();
+ Map<String, Flag<?>> fields = FlagsFactory.INSTANCE.getKnownFlags();
int longestFieldName = 0;
for (String fieldName : fields.keySet()) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 10786e39ab04..19fd25db8349 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,12 +17,11 @@ package com.android.systemui.flags
import android.provider.DeviceConfig
import com.android.internal.annotations.Keep
-import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
-import kotlin.reflect.KClass
-import kotlin.reflect.full.declaredMembers
-import kotlin.reflect.full.isSubclassOf
-import kotlin.reflect.full.staticProperties
+import com.android.systemui.flags.FlagsFactory.releasedFlag
+import com.android.systemui.flags.FlagsFactory.resourceBooleanFlag
+import com.android.systemui.flags.FlagsFactory.sysPropBooleanFlag
+import com.android.systemui.flags.FlagsFactory.unreleasedFlag
/**
* List of [Flag] objects for use in SystemUI.
@@ -37,64 +36,83 @@ import kotlin.reflect.full.staticProperties
* See [FeatureFlagsDebug] for instructions on flipping the flags via adb.
*/
object Flags {
- @JvmField val TEAMFOOD = UnreleasedFlag(1)
+ @JvmField val TEAMFOOD = unreleasedFlag(1, "teamfood")
// 100 - notification
// TODO(b/254512751): Tracking Bug
- val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING = UnreleasedFlag(103)
+ val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
+ unreleasedFlag(103, "notification_pipeline_developer_logging")
// TODO(b/254512732): Tracking Bug
- @JvmField val NSSL_DEBUG_LINES = UnreleasedFlag(105)
+ @JvmField val NSSL_DEBUG_LINES = unreleasedFlag(105, "nssl_debug_lines")
// TODO(b/254512505): Tracking Bug
- @JvmField val NSSL_DEBUG_REMOVE_ANIMATION = UnreleasedFlag(106)
+ @JvmField val NSSL_DEBUG_REMOVE_ANIMATION = unreleasedFlag(106, "nssl_debug_remove_animation")
// TODO(b/254512624): Tracking Bug
@JvmField
val NOTIFICATION_DRAG_TO_CONTENTS =
- ResourceBooleanFlag(108, R.bool.config_notificationToContents)
+ resourceBooleanFlag(
+ 108,
+ R.bool.config_notificationToContents,
+ "notification_drag_to_contents"
+ )
// TODO(b/254512517): Tracking Bug
- val FSI_REQUIRES_KEYGUARD = UnreleasedFlag(110, teamfood = true)
+ val FSI_REQUIRES_KEYGUARD = unreleasedFlag(110, "fsi_requires_keyguard", teamfood = true)
// TODO(b/254512538): Tracking Bug
- val INSTANT_VOICE_REPLY = UnreleasedFlag(111, teamfood = true)
+ val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply", teamfood = true)
// TODO(b/254512425): Tracking Bug
- val NOTIFICATION_MEMORY_MONITOR_ENABLED = ReleasedFlag(112)
+ val NOTIFICATION_MEMORY_MONITOR_ENABLED =
+ releasedFlag(112, "notification_memory_monitor_enabled")
// TODO(b/254512731): Tracking Bug
- @JvmField val NOTIFICATION_DISMISSAL_FADE = UnreleasedFlag(113, teamfood = true)
- val STABILITY_INDEX_FIX = UnreleasedFlag(114, teamfood = true)
- val SEMI_STABLE_SORT = UnreleasedFlag(115, teamfood = true)
+ @JvmField
+ val NOTIFICATION_DISMISSAL_FADE =
+ unreleasedFlag(113, "notification_dismissal_fade", teamfood = true)
+ val STABILITY_INDEX_FIX = unreleasedFlag(114, "stability_index_fix", teamfood = true)
+ val SEMI_STABLE_SORT = unreleasedFlag(115, "semi_stable_sort", teamfood = true)
+
+ @JvmField
+ val NOTIFICATION_GROUP_CORNER =
+ unreleasedFlag(116, "notification_group_corner", teamfood = true)
+
+ // TODO(b/257506350): Tracking Bug
+ val FSI_CHROME = unreleasedFlag(117, "fsi_chrome")
+
+ // TODO(b/257315550): Tracking Bug
+ val NO_HUN_FOR_OLD_WHEN = unreleasedFlag(118, "no_hun_for_old_when")
- @JvmField val NOTIFICATION_GROUP_CORNER = UnreleasedFlag(116, teamfood = true)
- // next id: 117
+ // next id: 119
// 200 - keyguard/lockscreen
// ** Flag retired **
// public static final BooleanFlag KEYGUARD_LAYOUT =
// new BooleanFlag(200, true);
// TODO(b/254512713): Tracking Bug
- @JvmField val LOCKSCREEN_ANIMATIONS = ReleasedFlag(201)
+ @JvmField val LOCKSCREEN_ANIMATIONS = releasedFlag(201, "lockscreen_animations")
// TODO(b/254512750): Tracking Bug
- val NEW_UNLOCK_SWIPE_ANIMATION = ReleasedFlag(202)
- val CHARGING_RIPPLE = ResourceBooleanFlag(203, R.bool.flag_charging_ripple)
+ val NEW_UNLOCK_SWIPE_ANIMATION = releasedFlag(202, "new_unlock_swipe_animation")
+ val CHARGING_RIPPLE = resourceBooleanFlag(203, R.bool.flag_charging_ripple, "charging_ripple")
// TODO(b/254512281): Tracking Bug
@JvmField
- val BOUNCER_USER_SWITCHER = ResourceBooleanFlag(204, R.bool.config_enableBouncerUserSwitcher)
+ val BOUNCER_USER_SWITCHER =
+ resourceBooleanFlag(204, R.bool.config_enableBouncerUserSwitcher, "bouncer_user_switcher")
// TODO(b/254512676): Tracking Bug
- @JvmField val LOCKSCREEN_CUSTOM_CLOCKS = UnreleasedFlag(207, teamfood = true)
+ @JvmField
+ val LOCKSCREEN_CUSTOM_CLOCKS = unreleasedFlag(207, "lockscreen_custom_clocks", teamfood = true)
/**
* Flag to enable the usage of the new bouncer data source. This is a refactor of and eventual
* replacement of KeyguardBouncer.java.
*/
// TODO(b/254512385): Tracking Bug
- @JvmField val MODERN_BOUNCER = ReleasedFlag(208)
+ @JvmField val MODERN_BOUNCER = releasedFlag(208, "modern_bouncer")
/**
* Whether the user interactor and repository should use `UserSwitcherController`.
@@ -103,7 +121,8 @@ object Flags {
* framework APIs.
*/
// TODO(b/254513286): Tracking Bug
- val USER_INTERACTOR_AND_REPO_USE_CONTROLLER = UnreleasedFlag(210)
+ val USER_INTERACTOR_AND_REPO_USE_CONTROLLER =
+ unreleasedFlag(210, "user_interactor_and_repo_use_controller")
/**
* Whether `UserSwitcherController` should use the user interactor.
@@ -115,20 +134,26 @@ object Flags {
* would created a cycle between controller -> interactor -> controller.
*/
// TODO(b/254513102): Tracking Bug
- val USER_CONTROLLER_USES_INTERACTOR = ReleasedFlag(211)
+ val USER_CONTROLLER_USES_INTERACTOR = releasedFlag(211, "user_controller_uses_interactor")
/**
* Whether the clock on a wide lock screen should use the new "stepping" animation for moving
* the digits when the clock moves.
*/
- @JvmField val STEP_CLOCK_ANIMATION = UnreleasedFlag(212)
+ @JvmField val STEP_CLOCK_ANIMATION = unreleasedFlag(212, "step_clock_animation")
/**
* Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
* will occur in stages. This is one stage of many to come.
*/
// TODO(b/255607168): Tracking Bug
- @JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213)
+ @JvmField val DOZING_MIGRATION_1 = unreleasedFlag(213, "dozing_migration_1")
+
+ // TODO(b/252897742): Tracking Bug
+ @JvmField val NEW_ELLIPSE_DETECTION = unreleasedFlag(214, "new_ellipse_detection")
+
+ // TODO(b/252897742): Tracking Bug
+ @JvmField val NEW_UDFPS_OVERLAY = unreleasedFlag(215, "new_udfps_overlay")
/**
* Whether to enable the code powering customizable lock screen quick affordances.
@@ -136,267 +161,264 @@ object Flags {
* Note that this flag does not enable individual implementations of quick affordances like the
* new camera quick affordance. Look for individual flags for those.
*/
- @JvmField val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES = UnreleasedFlag(214, teamfood = false)
+ @JvmField
+ val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES =
+ unreleasedFlag(216, "customizable_lock_screen_quick_affordances", teamfood = false)
+
+ /** Shows chipbar UI whenever the device is unlocked by ActiveUnlock (watch). */
+ // TODO(b/240196500): Tracking Bug
+ @JvmField val ACTIVE_UNLOCK_CHIPBAR = unreleasedFlag(217, "active_unlock_chipbar")
// 300 - power menu
// TODO(b/254512600): Tracking Bug
- @JvmField val POWER_MENU_LITE = ReleasedFlag(300)
+ @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
// 400 - smartspace
// TODO(b/254513100): Tracking Bug
- val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED = ReleasedFlag(401)
- val SMARTSPACE = ResourceBooleanFlag(402, R.bool.flag_smartspace)
+ val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
+ releasedFlag(401, "smartspace_shared_element_transition_enabled")
+ val SMARTSPACE = resourceBooleanFlag(402, R.bool.flag_smartspace, "smartspace")
// 500 - quick settings
// TODO(b/254512321): Tracking Bug
- @JvmField val COMBINED_QS_HEADERS = ReleasedFlag(501)
- val PEOPLE_TILE = ResourceBooleanFlag(502, R.bool.flag_conversations)
+ @JvmField val COMBINED_QS_HEADERS = releasedFlag(501, "combined_qs_headers")
+ val PEOPLE_TILE = resourceBooleanFlag(502, R.bool.flag_conversations, "people_tile")
@JvmField
val QS_USER_DETAIL_SHORTCUT =
- ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut)
+ resourceBooleanFlag(
+ 503,
+ R.bool.flag_lockscreen_qs_user_detail_shortcut,
+ "qs_user_detail_shortcut"
+ )
// TODO(b/254512747): Tracking Bug
- val NEW_HEADER = ReleasedFlag(505)
+ val NEW_HEADER = releasedFlag(505, "new_header")
// TODO(b/254512383): Tracking Bug
@JvmField
val FULL_SCREEN_USER_SWITCHER =
- ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher)
+ resourceBooleanFlag(
+ 506,
+ R.bool.config_enableFullscreenUserSwitcher,
+ "full_screen_user_switcher"
+ )
// TODO(b/254512678): Tracking Bug
- @JvmField val NEW_FOOTER_ACTIONS = ReleasedFlag(507)
+ @JvmField val NEW_FOOTER_ACTIONS = releasedFlag(507, "new_footer_actions")
+
+ // TODO(b/244064524): Tracking Bug
+ @JvmField
+ val QS_SECONDARY_DATA_SUB_INFO =
+ unreleasedFlag(508, "qs_secondary_data_sub_info", teamfood = true)
// 600- status bar
// TODO(b/254513246): Tracking Bug
- val STATUS_BAR_USER_SWITCHER = ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip)
+ val STATUS_BAR_USER_SWITCHER =
+ resourceBooleanFlag(602, R.bool.flag_user_switcher_chip, "status_bar_user_switcher")
// TODO(b/254512623): Tracking Bug
@Deprecated("Replaced by mobile and wifi specific flags.")
- val NEW_STATUS_BAR_PIPELINE_BACKEND = UnreleasedFlag(604, teamfood = false)
+ val NEW_STATUS_BAR_PIPELINE_BACKEND =
+ unreleasedFlag(604, "new_status_bar_pipeline_backend", teamfood = false)
// TODO(b/254512660): Tracking Bug
@Deprecated("Replaced by mobile and wifi specific flags.")
- val NEW_STATUS_BAR_PIPELINE_FRONTEND = UnreleasedFlag(605, teamfood = false)
+ val NEW_STATUS_BAR_PIPELINE_FRONTEND =
+ unreleasedFlag(605, "new_status_bar_pipeline_frontend", teamfood = false)
// TODO(b/256614753): Tracking Bug
- val NEW_STATUS_BAR_MOBILE_ICONS = UnreleasedFlag(606)
+ val NEW_STATUS_BAR_MOBILE_ICONS = unreleasedFlag(606, "new_status_bar_mobile_icons")
// TODO(b/256614210): Tracking Bug
- val NEW_STATUS_BAR_WIFI_ICON = UnreleasedFlag(607)
+ val NEW_STATUS_BAR_WIFI_ICON = unreleasedFlag(607, "new_status_bar_wifi_icon")
// TODO(b/256614751): Tracking Bug
- val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND = UnreleasedFlag(608)
+ val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND =
+ unreleasedFlag(608, "new_status_bar_mobile_icons_backend")
// TODO(b/256613548): Tracking Bug
- val NEW_STATUS_BAR_WIFI_ICON_BACKEND = UnreleasedFlag(609)
+ val NEW_STATUS_BAR_WIFI_ICON_BACKEND = unreleasedFlag(609, "new_status_bar_wifi_icon_backend")
+
+ // TODO(b/256623670): Tracking Bug
+ @JvmField val BATTERY_SHIELD_ICON = unreleasedFlag(610, "battery_shield_icon")
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
- val ONGOING_CALL_STATUS_BAR_CHIP = ReleasedFlag(700)
+ val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag(700, "ongoing_call_status_bar_chip")
// TODO(b/254512681): Tracking Bug
- val ONGOING_CALL_IN_IMMERSIVE = ReleasedFlag(701)
+ val ONGOING_CALL_IN_IMMERSIVE = releasedFlag(701, "ongoing_call_in_immersive")
// TODO(b/254512753): Tracking Bug
- val ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP = ReleasedFlag(702)
+ val ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP = releasedFlag(702, "ongoing_call_in_immersive_chip_tap")
// 800 - general visual/theme
- @JvmField val MONET = ResourceBooleanFlag(800, R.bool.flag_monet)
+ @JvmField val MONET = resourceBooleanFlag(800, R.bool.flag_monet, "monet")
// 801 - region sampling
// TODO(b/254512848): Tracking Bug
- val REGION_SAMPLING = UnreleasedFlag(801)
+ val REGION_SAMPLING = unreleasedFlag(801, "region_sampling")
// 802 - wallpaper rendering
// TODO(b/254512923): Tracking Bug
- @JvmField val USE_CANVAS_RENDERER = UnreleasedFlag(802, teamfood = true)
+ @JvmField val USE_CANVAS_RENDERER = unreleasedFlag(802, "use_canvas_renderer")
// 803 - screen contents translation
// TODO(b/254513187): Tracking Bug
- val SCREEN_CONTENTS_TRANSLATION = UnreleasedFlag(803)
+ val SCREEN_CONTENTS_TRANSLATION = unreleasedFlag(803, "screen_contents_translation")
// 804 - monochromatic themes
@JvmField
- val MONOCHROMATIC_THEMES = SysPropBooleanFlag(804, "persist.sysui.monochromatic", false)
+ val MONOCHROMATIC_THEMES =
+ sysPropBooleanFlag(804, "persist.sysui.monochromatic", default = false)
// 900 - media
// TODO(b/254512697): Tracking Bug
- val MEDIA_TAP_TO_TRANSFER = ReleasedFlag(900)
+ val MEDIA_TAP_TO_TRANSFER = releasedFlag(900, "media_tap_to_transfer")
// TODO(b/254512502): Tracking Bug
- val MEDIA_SESSION_ACTIONS = UnreleasedFlag(901)
+ val MEDIA_SESSION_ACTIONS = unreleasedFlag(901, "media_session_actions")
// TODO(b/254512726): Tracking Bug
- val MEDIA_NEARBY_DEVICES = ReleasedFlag(903)
+ val MEDIA_NEARBY_DEVICES = releasedFlag(903, "media_nearby_devices")
// TODO(b/254512695): Tracking Bug
- val MEDIA_MUTE_AWAIT = ReleasedFlag(904)
+ val MEDIA_MUTE_AWAIT = releasedFlag(904, "media_mute_await")
// TODO(b/254512654): Tracking Bug
- @JvmField val DREAM_MEDIA_COMPLICATION = UnreleasedFlag(905)
+ @JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag(905, "dream_media_complication")
// TODO(b/254512673): Tracking Bug
- @JvmField val DREAM_MEDIA_TAP_TO_OPEN = UnreleasedFlag(906)
+ @JvmField val DREAM_MEDIA_TAP_TO_OPEN = unreleasedFlag(906, "dream_media_tap_to_open")
// TODO(b/254513168): Tracking Bug
- @JvmField val UMO_SURFACE_RIPPLE = UnreleasedFlag(907)
+ @JvmField val UMO_SURFACE_RIPPLE = unreleasedFlag(907, "umo_surface_ripple")
// 1000 - dock
- val SIMULATE_DOCK_THROUGH_CHARGING = ReleasedFlag(1000)
+ val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
// TODO(b/254512758): Tracking Bug
- @JvmField val ROUNDED_BOX_RIPPLE = ReleasedFlag(1002)
+ @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag(1002, "rounded_box_ripple")
// 1100 - windowing
@Keep
@JvmField
val WM_ENABLE_SHELL_TRANSITIONS =
- SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false)
+ sysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", default = false)
// TODO(b/254513207): Tracking Bug
@Keep
@JvmField
val WM_ENABLE_PARTIAL_SCREEN_SHARING =
- DeviceConfigBooleanFlag(
+ unreleasedFlag(
1102,
- "record_task_content",
- DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- false,
+ name = "record_task_content",
+ namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
teamfood = true
)
// TODO(b/254512674): Tracking Bug
@Keep
@JvmField
- val HIDE_NAVBAR_WINDOW = SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false)
+ val HIDE_NAVBAR_WINDOW =
+ sysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", default = false)
@Keep
@JvmField
- val WM_DESKTOP_WINDOWING = SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false)
+ val WM_DESKTOP_WINDOWING =
+ sysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", default = false)
@Keep
@JvmField
- val WM_CAPTION_ON_SHELL = SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false)
+ val WM_CAPTION_ON_SHELL =
+ sysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", default = false)
@Keep
@JvmField
val ENABLE_FLING_TO_DISMISS_BUBBLE =
- SysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", true)
+ sysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", default = true)
@Keep
@JvmField
val ENABLE_FLING_TO_DISMISS_PIP =
- SysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", true)
+ sysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", default = true)
@Keep
@JvmField
val ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
- SysPropBooleanFlag(1110, "persist.wm.debug.enable_pip_keep_clear_algorithm", false)
+ sysPropBooleanFlag(
+ 1110,
+ "persist.wm.debug.enable_pip_keep_clear_algorithm",
+ default = false
+ )
// TODO(b/256873975): Tracking Bug
- @JvmField @Keep val WM_BUBBLE_BAR = UnreleasedFlag(1111)
+ @JvmField @Keep val WM_BUBBLE_BAR = unreleasedFlag(1111, "wm_bubble_bar")
// 1200 - predictive back
@Keep
@JvmField
val WM_ENABLE_PREDICTIVE_BACK =
- SysPropBooleanFlag(1200, "persist.wm.debug.predictive_back", true)
+ sysPropBooleanFlag(1200, "persist.wm.debug.predictive_back", default = true)
@Keep
@JvmField
val WM_ENABLE_PREDICTIVE_BACK_ANIM =
- SysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", false)
+ sysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", default = false)
@Keep
@JvmField
val WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
- SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false)
+ sysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", default = false)
// TODO(b/254512728): Tracking Bug
- @JvmField val NEW_BACK_AFFORDANCE = UnreleasedFlag(1203, teamfood = false)
+ @JvmField
+ val NEW_BACK_AFFORDANCE = unreleasedFlag(1203, "new_back_affordance", teamfood = false)
// 1300 - screenshots
// TODO(b/254512719): Tracking Bug
- @JvmField val SCREENSHOT_REQUEST_PROCESSOR = UnreleasedFlag(1300, teamfood = true)
+ @JvmField
+ val SCREENSHOT_REQUEST_PROCESSOR =
+ unreleasedFlag(1300, "screenshot_request_processor", teamfood = true)
// TODO(b/254513155): Tracking Bug
- @JvmField val SCREENSHOT_WORK_PROFILE_POLICY = UnreleasedFlag(1301)
+ @JvmField
+ val SCREENSHOT_WORK_PROFILE_POLICY =
+ unreleasedFlag(1301, "screenshot_work_profile_policy", teamfood = true)
// 1400 - columbus
// TODO(b/254512756): Tracking Bug
- val QUICK_TAP_IN_PCC = ReleasedFlag(1400)
+ val QUICK_TAP_IN_PCC = releasedFlag(1400, "quick_tap_in_pcc")
// 1500 - chooser
// TODO(b/254512507): Tracking Bug
- val CHOOSER_UNBUNDLED = UnreleasedFlag(1500, teamfood = true)
+ val CHOOSER_UNBUNDLED = unreleasedFlag(1500, "chooser_unbundled", teamfood = true)
+
+ // 1600 - accessibility
+ @JvmField
+ val A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS =
+ unreleasedFlag(1600, "a11y_floating_menu_fling_spring_animations")
// 1700 - clipboard
- @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700, teamfood = true)
- @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = UnreleasedFlag(1701)
+ @JvmField
+ val CLIPBOARD_OVERLAY_REFACTOR =
+ unreleasedFlag(1700, "clipboard_overlay_refactor", teamfood = true)
+ @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = unreleasedFlag(1701, "clipboard_remote_behavior")
// 1800 - shade container
- @JvmField val LEAVE_SHADE_OPEN_FOR_BUGREPORT = UnreleasedFlag(1800, teamfood = true)
+ @JvmField
+ val LEAVE_SHADE_OPEN_FOR_BUGREPORT =
+ unreleasedFlag(1800, "leave_shade_open_for_bugreport", teamfood = true)
// 1900 - note task
- @JvmField val NOTE_TASKS = SysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
+ @JvmField val NOTE_TASKS = sysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
// 2000 - device controls
- @Keep @JvmField val USE_APP_PANELS = UnreleasedFlag(2000, teamfood = true)
+ @Keep @JvmField val USE_APP_PANELS = unreleasedFlag(2000, "use_app_panels", teamfood = true)
// 2100 - Falsing Manager
- @JvmField val FALSING_FOR_LONG_TAPS = ReleasedFlag(2100)
-
- // Pay no attention to the reflection behind the curtain.
- // ========================== Curtain ==========================
- // | |
- // | . . . . . . . . . . . . . . . . . . . |
- @JvmStatic
- fun collectFlags(): Map<Int, Flag<*>> {
- return flagFields.mapKeys { field -> field.value.id }
- }
-
- // | . . . . . . . . . . . . . . . . . . . |
- @JvmStatic
- val flagFields: Map<String, Flag<*>>
- get() = collectFlagsInClass(Flags)
-
- @VisibleForTesting
- fun collectFlagsInClass(instance: Any): Map<String, Flag<*>> {
- val cls = instance::class
- val javaPropNames = cls.java.fields.map { it.name }
- val props = cls.declaredMembers
- val staticProps = cls.staticProperties
- val staticPropNames = staticProps.map { it.name }
- return props
- .mapNotNull { property ->
- if ((property.returnType.classifier as KClass<*>).isSubclassOf(Flag::class)) {
- // Fields with @JvmStatic should be accessed via java mechanisms
- if (javaPropNames.contains(property.name)) {
- property.name to cls.java.getField(property.name)[null] as Flag<*>
- // Fields with @Keep but not @JvmField. Don't do this.
- } else if (staticPropNames.contains(property.name)) {
- // The below code causes access violation exceptions. I don't know why.
- // property.name to (property.call() as Flag<*>)
- // property.name to (staticProps.find { it.name == property.name }!!
- // .getter.call() as Flag<*>)
- throw java.lang.RuntimeException(
- "The {$property.name} flag needs @JvmField"
- )
- // Everything else. Skip the `get` prefixed fields that kotlin adds.
- } else if (property.name.subSequence(0, 3) != "get") {
- property.name to (property.call(instance) as Flag<*>)
- } else {
- null
- }
- } else {
- null
- }
- }
- .toMap()
- }
- // | |
- // \_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
+ @JvmField val FALSING_FOR_LONG_TAPS = releasedFlag(2100, "falsing_for_long_taps")
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
index e1f4944741a0..18d7bcf9b3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
@@ -30,7 +30,7 @@ interface FlagsCommonModule {
@Provides
@Named(ALL_FLAGS)
fun providesAllFlags(): Map<Int, Flag<*>> {
- return Flags.collectFlags()
+ return FlagsFactory.knownFlags.map { it.value.id to it.value }.toMap()
}
@JvmStatic
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
index 694fa01bb4a5..ae05c4656349 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
@@ -27,11 +27,10 @@ import javax.inject.Inject
interface ServerFlagReader {
/** Returns true if there is a server-side setting stored. */
- fun hasOverride(flagId: Int): Boolean
+ fun hasOverride(namespace: String, name: String): Boolean
/** Returns any stored server-side setting or the default if not set. */
- fun readServerOverride(flagId: Int, default: Boolean): Boolean
-
+ fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean
/** Register a listener for changes to any of the passed in flags. */
fun listenForChanges(values: Collection<Flag<*>>, listener: ChangeListener)
@@ -68,19 +67,19 @@ class ServerFlagReaderImpl @Inject constructor(
}
}
- override fun hasOverride(flagId: Int): Boolean =
- deviceConfig.getProperty(
+ override fun hasOverride(namespace: String, name: String): Boolean =
+ !namespace.isBlank() && !name.isBlank() && deviceConfig.getProperty(
namespace,
- getServerOverrideName(flagId)
+ name
) != null
- override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
- return deviceConfig.getBoolean(
+
+ override fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean =
+ !namespace.isBlank() && !name.isBlank() && deviceConfig.getBoolean(
namespace,
- getServerOverrideName(flagId),
+ name,
default
)
- }
override fun listenForChanges(
flags: Collection<Flag<*>>,
@@ -121,24 +120,24 @@ interface ServerFlagReaderModule {
}
class ServerFlagReaderFake : ServerFlagReader {
- private val flagMap: MutableMap<Int, Boolean> = mutableMapOf()
+ private val flagMap: MutableMap<String, Boolean> = mutableMapOf()
private val listeners =
mutableListOf<Pair<ServerFlagReader.ChangeListener, Collection<Flag<*>>>>()
- override fun hasOverride(flagId: Int): Boolean {
- return flagMap.containsKey(flagId)
+ override fun hasOverride(namespace: String, name: String): Boolean {
+ return flagMap.containsKey(name)
}
- override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
- return flagMap.getOrDefault(flagId, default)
+ override fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean {
+ return flagMap.getOrDefault(name, default)
}
- fun setFlagValue(flagId: Int, value: Boolean) {
- flagMap.put(flagId, value)
+ fun setFlagValue(namespace: String, name: String, value: Boolean) {
+ flagMap.put(name, value)
for ((listener, flags) in listeners) {
flagLoop@ for (flag in flags) {
- if (flagId == flag.id) {
+ if (name == flag.name) {
listener.onChange()
break@flagLoop
}
@@ -146,8 +145,8 @@ class ServerFlagReaderFake : ServerFlagReader {
}
}
- fun eraseFlag(flagId: Int) {
- flagMap.remove(flagId)
+ fun eraseFlag(namespace: String, name: String) {
+ flagMap.remove(name)
}
override fun listenForChanges(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
new file mode 100644
index 000000000000..0f4581ce3e61
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard
+
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.content.Context
+import android.content.UriMatcher
+import android.content.pm.ProviderInfo
+import android.database.Cursor
+import android.database.MatrixCursor
+import android.net.Uri
+import android.util.Log
+import com.android.systemui.SystemUIAppComponentFactoryBase
+import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
+import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import javax.inject.Inject
+import kotlinx.coroutines.runBlocking
+
+class KeyguardQuickAffordanceProvider :
+ ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer {
+
+ @Inject lateinit var interactor: KeyguardQuickAffordanceInteractor
+
+ private lateinit var contextAvailableCallback: ContextAvailableCallback
+
+ private val uriMatcher =
+ UriMatcher(UriMatcher.NO_MATCH).apply {
+ addURI(
+ Contract.AUTHORITY,
+ Contract.SlotTable.TABLE_NAME,
+ MATCH_CODE_ALL_SLOTS,
+ )
+ addURI(
+ Contract.AUTHORITY,
+ Contract.AffordanceTable.TABLE_NAME,
+ MATCH_CODE_ALL_AFFORDANCES,
+ )
+ addURI(
+ Contract.AUTHORITY,
+ Contract.SelectionTable.TABLE_NAME,
+ MATCH_CODE_ALL_SELECTIONS,
+ )
+ }
+
+ override fun onCreate(): Boolean {
+ return true
+ }
+
+ override fun attachInfo(context: Context?, info: ProviderInfo?) {
+ contextAvailableCallback.onContextAvailable(checkNotNull(context))
+ super.attachInfo(context, info)
+ }
+
+ override fun setContextAvailableCallback(callback: ContextAvailableCallback) {
+ contextAvailableCallback = callback
+ }
+
+ override fun getType(uri: Uri): String? {
+ val prefix =
+ when (uriMatcher.match(uri)) {
+ MATCH_CODE_ALL_SLOTS,
+ MATCH_CODE_ALL_AFFORDANCES,
+ MATCH_CODE_ALL_SELECTIONS -> "vnd.android.cursor.dir/vnd."
+ else -> null
+ }
+
+ val tableName =
+ when (uriMatcher.match(uri)) {
+ MATCH_CODE_ALL_SLOTS -> Contract.SlotTable.TABLE_NAME
+ MATCH_CODE_ALL_AFFORDANCES -> Contract.AffordanceTable.TABLE_NAME
+ MATCH_CODE_ALL_SELECTIONS -> Contract.SelectionTable.TABLE_NAME
+ else -> null
+ }
+
+ if (prefix == null || tableName == null) {
+ return null
+ }
+
+ return "$prefix${Contract.AUTHORITY}.$tableName"
+ }
+
+ override fun insert(uri: Uri, values: ContentValues?): Uri? {
+ if (uriMatcher.match(uri) != MATCH_CODE_ALL_SELECTIONS) {
+ throw UnsupportedOperationException()
+ }
+
+ return insertSelection(values)
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array<out String>?,
+ selection: String?,
+ selectionArgs: Array<out String>?,
+ sortOrder: String?,
+ ): Cursor? {
+ return when (uriMatcher.match(uri)) {
+ MATCH_CODE_ALL_AFFORDANCES -> queryAffordances()
+ MATCH_CODE_ALL_SLOTS -> querySlots()
+ MATCH_CODE_ALL_SELECTIONS -> querySelections()
+ else -> null
+ }
+ }
+
+ override fun update(
+ uri: Uri,
+ values: ContentValues?,
+ selection: String?,
+ selectionArgs: Array<out String>?,
+ ): Int {
+ Log.e(TAG, "Update is not supported!")
+ return 0
+ }
+
+ override fun delete(
+ uri: Uri,
+ selection: String?,
+ selectionArgs: Array<out String>?,
+ ): Int {
+ if (uriMatcher.match(uri) != MATCH_CODE_ALL_SELECTIONS) {
+ throw UnsupportedOperationException()
+ }
+
+ return deleteSelection(uri, selectionArgs)
+ }
+
+ private fun insertSelection(values: ContentValues?): Uri? {
+ if (values == null) {
+ throw IllegalArgumentException("Cannot insert selection, no values passed in!")
+ }
+
+ if (!values.containsKey(Contract.SelectionTable.Columns.SLOT_ID)) {
+ throw IllegalArgumentException(
+ "Cannot insert selection, " +
+ "\"${Contract.SelectionTable.Columns.SLOT_ID}\" not specified!"
+ )
+ }
+
+ if (!values.containsKey(Contract.SelectionTable.Columns.AFFORDANCE_ID)) {
+ throw IllegalArgumentException(
+ "Cannot insert selection, " +
+ "\"${Contract.SelectionTable.Columns.AFFORDANCE_ID}\" not specified!"
+ )
+ }
+
+ val slotId = values.getAsString(Contract.SelectionTable.Columns.SLOT_ID)
+ val affordanceId = values.getAsString(Contract.SelectionTable.Columns.AFFORDANCE_ID)
+
+ if (slotId.isNullOrEmpty()) {
+ throw IllegalArgumentException("Cannot insert selection, slot ID was empty!")
+ }
+
+ if (affordanceId.isNullOrEmpty()) {
+ throw IllegalArgumentException("Cannot insert selection, affordance ID was empty!")
+ }
+
+ val success = runBlocking {
+ interactor.select(
+ slotId = slotId,
+ affordanceId = affordanceId,
+ )
+ }
+
+ return if (success) {
+ Log.d(TAG, "Successfully selected $affordanceId for slot $slotId")
+ context?.contentResolver?.notifyChange(Contract.SelectionTable.URI, null)
+ Contract.SelectionTable.URI
+ } else {
+ Log.d(TAG, "Failed to select $affordanceId for slot $slotId")
+ null
+ }
+ }
+
+ private fun querySelections(): Cursor {
+ return MatrixCursor(
+ arrayOf(
+ Contract.SelectionTable.Columns.SLOT_ID,
+ Contract.SelectionTable.Columns.AFFORDANCE_ID,
+ )
+ )
+ .apply {
+ val affordanceIdsBySlotId = runBlocking { interactor.getSelections() }
+ affordanceIdsBySlotId.entries.forEach { (slotId, affordanceIds) ->
+ affordanceIds.forEach { affordanceId ->
+ addRow(
+ arrayOf(
+ slotId,
+ affordanceId,
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun queryAffordances(): Cursor {
+ return MatrixCursor(
+ arrayOf(
+ Contract.AffordanceTable.Columns.ID,
+ Contract.AffordanceTable.Columns.NAME,
+ Contract.AffordanceTable.Columns.ICON,
+ )
+ )
+ .apply {
+ interactor.getAffordancePickerRepresentations().forEach { representation ->
+ addRow(
+ arrayOf(
+ representation.id,
+ representation.name,
+ representation.iconResourceId,
+ )
+ )
+ }
+ }
+ }
+
+ private fun querySlots(): Cursor {
+ return MatrixCursor(
+ arrayOf(
+ Contract.SlotTable.Columns.ID,
+ Contract.SlotTable.Columns.CAPACITY,
+ )
+ )
+ .apply {
+ interactor.getSlotPickerRepresentations().forEach { representation ->
+ addRow(
+ arrayOf(
+ representation.id,
+ representation.maxSelectedAffordances,
+ )
+ )
+ }
+ }
+ }
+
+ private fun deleteSelection(
+ uri: Uri,
+ selectionArgs: Array<out String>?,
+ ): Int {
+ if (selectionArgs == null) {
+ throw IllegalArgumentException(
+ "Cannot delete selection, selection arguments not included!"
+ )
+ }
+
+ val (slotId, affordanceId) =
+ when (selectionArgs.size) {
+ 1 -> Pair(selectionArgs[0], null)
+ 2 -> Pair(selectionArgs[0], selectionArgs[1])
+ else ->
+ throw IllegalArgumentException(
+ "Cannot delete selection, selection arguments has wrong size, expected to" +
+ " have 1 or 2 arguments, had ${selectionArgs.size} instead!"
+ )
+ }
+
+ val deleted = runBlocking {
+ interactor.unselect(
+ slotId = slotId,
+ affordanceId = affordanceId,
+ )
+ }
+
+ return if (deleted) {
+ Log.d(TAG, "Successfully unselected $affordanceId for slot $slotId")
+ context?.contentResolver?.notifyChange(uri, null)
+ 1
+ } else {
+ Log.d(TAG, "Failed to unselect $affordanceId for slot $slotId")
+ 0
+ }
+ }
+
+ companion object {
+ private const val TAG = "KeyguardQuickAffordanceProvider"
+ private const val MATCH_CODE_ALL_SLOTS = 1
+ private const val MATCH_CODE_ALL_AFFORDANCES = 2
+ private const val MATCH_CODE_ALL_SELECTIONS = 3
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 0046256c677b..9a90fe7e60bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -16,19 +16,20 @@
package com.android.systemui.keyguard.data.repository
-import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.statusbar.phone.KeyguardBouncer
import javax.inject.Inject
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
-/** Encapsulates app state for the lock screen bouncer. */
+/** Encapsulates app state for the lock screen primary and alternate bouncer. */
@SysUISingleton
class KeyguardBouncerRepository
@Inject
@@ -36,12 +37,24 @@ constructor(
private val viewMediatorCallback: ViewMediatorCallback,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
) {
- var bouncerPromptReason: Int? = null
- /** Determines if we want to instantaneously show the bouncer instead of translating. */
- private val _isScrimmed = MutableStateFlow(false)
- val isScrimmed = _isScrimmed.asStateFlow()
+ /** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
+ private val _primaryBouncerVisible = MutableStateFlow(false)
+ val primaryBouncerVisible = _primaryBouncerVisible.asStateFlow()
+ private val _primaryBouncerShow = MutableStateFlow<KeyguardBouncerModel?>(null)
+ val primaryBouncerShow = _primaryBouncerShow.asStateFlow()
+ private val _primaryBouncerShowingSoon = MutableStateFlow(false)
+ val primaryBouncerShowingSoon = _primaryBouncerShowingSoon.asStateFlow()
+ private val _primaryBouncerHide = MutableStateFlow(false)
+ val primaryBouncerHide = _primaryBouncerHide.asStateFlow()
+ private val _primaryBouncerStartingToHide = MutableStateFlow(false)
+ val primaryBouncerStartingToHide = _primaryBouncerStartingToHide.asStateFlow()
+ private val _primaryBouncerDisappearAnimation = MutableStateFlow<Runnable?>(null)
+ val primaryBouncerStartingDisappearAnimation = _primaryBouncerDisappearAnimation.asStateFlow()
+ /** Determines if we want to instantaneously show the primary bouncer instead of translating. */
+ private val _primaryBouncerScrimmed = MutableStateFlow(false)
+ val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
/**
- * Set how much of the panel is showing on the screen.
+ * Set how much of the notification panel is showing on the screen.
* ```
* 0f = panel fully hidden = bouncer fully showing
* 1f = panel fully showing = bouncer fully hidden
@@ -49,82 +62,58 @@ constructor(
*/
private val _panelExpansionAmount = MutableStateFlow(KeyguardBouncer.EXPANSION_HIDDEN)
val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
- private val _isVisible = MutableStateFlow(false)
- val isVisible = _isVisible.asStateFlow()
- private val _show = MutableStateFlow<KeyguardBouncerModel?>(null)
- val show = _show.asStateFlow()
- private val _showingSoon = MutableStateFlow(false)
- val showingSoon = _showingSoon.asStateFlow()
- private val _hide = MutableStateFlow(false)
- val hide = _hide.asStateFlow()
- private val _startingToHide = MutableStateFlow(false)
- val startingToHide = _startingToHide.asStateFlow()
- private val _disappearAnimation = MutableStateFlow<Runnable?>(null)
- val startingDisappearAnimation = _disappearAnimation.asStateFlow()
private val _keyguardPosition = MutableStateFlow(0f)
val keyguardPosition = _keyguardPosition.asStateFlow()
- private val _resourceUpdateRequests = MutableStateFlow(false)
- val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
- private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
- val showMessage = _showMessage.asStateFlow()
+ private val _onScreenTurnedOff = MutableStateFlow(false)
+ val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
+ private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
+ val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
/** Determines if user is already unlocked */
val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
- private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
- val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
- private val _onScreenTurnedOff = MutableStateFlow(false)
- val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
-
+ private val _showMessage =
+ MutableSharedFlow<BouncerShowMessageModel?>(
+ replay = 1,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
+ val showMessage = _showMessage.asSharedFlow()
+ private val _resourceUpdateRequests = MutableStateFlow(false)
+ val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
+ val bouncerPromptReason: Int
+ get() = viewMediatorCallback.bouncerPromptReason
val bouncerErrorMessage: CharSequence?
get() = viewMediatorCallback.consumeCustomMessage()
- init {
- val callback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onStrongAuthStateChanged(userId: Int) {
- bouncerPromptReason = viewMediatorCallback.bouncerPromptReason
- }
-
- override fun onLockedOutStateChanged(type: BiometricSourceType) {
- if (type == BiometricSourceType.FINGERPRINT) {
- bouncerPromptReason = viewMediatorCallback.bouncerPromptReason
- }
- }
- }
-
- keyguardUpdateMonitor.registerCallback(callback)
+ fun setPrimaryScrimmed(isScrimmed: Boolean) {
+ _primaryBouncerScrimmed.value = isScrimmed
}
- fun setScrimmed(isScrimmed: Boolean) {
- _isScrimmed.value = isScrimmed
+ fun setPrimaryVisible(isVisible: Boolean) {
+ _primaryBouncerVisible.value = isVisible
}
- fun setPanelExpansion(panelExpansion: Float) {
- _panelExpansionAmount.value = panelExpansion
+ fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) {
+ _primaryBouncerShow.value = keyguardBouncerModel
}
- fun setVisible(isVisible: Boolean) {
- _isVisible.value = isVisible
+ fun setPrimaryShowingSoon(showingSoon: Boolean) {
+ _primaryBouncerShowingSoon.value = showingSoon
}
- fun setShow(keyguardBouncerModel: KeyguardBouncerModel?) {
- _show.value = keyguardBouncerModel
+ fun setPrimaryHide(hide: Boolean) {
+ _primaryBouncerHide.value = hide
}
- fun setShowingSoon(showingSoon: Boolean) {
- _showingSoon.value = showingSoon
+ fun setPrimaryStartingToHide(startingToHide: Boolean) {
+ _primaryBouncerStartingToHide.value = startingToHide
}
- fun setHide(hide: Boolean) {
- _hide.value = hide
+ fun setPrimaryStartDisappearAnimation(runnable: Runnable?) {
+ _primaryBouncerDisappearAnimation.value = runnable
}
- fun setStartingToHide(startingToHide: Boolean) {
- _startingToHide.value = startingToHide
- }
-
- fun setStartDisappearAnimation(runnable: Runnable?) {
- _disappearAnimation.value = runnable
+ fun setPanelExpansion(panelExpansion: Float) {
+ _panelExpansionAmount.value = panelExpansion
}
fun setKeyguardPosition(keyguardPosition: Float) {
@@ -136,7 +125,7 @@ constructor(
}
fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
- _showMessage.value = bouncerShowMessageModel
+ _showMessage.tryEmit(bouncerShowMessageModel)
}
fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index c867c6e9229b..9d5d8bbd4f40 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -16,6 +16,8 @@
package com.android.systemui.keyguard.data.repository
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
@@ -68,6 +70,9 @@ interface KeyguardRepository {
*/
val isKeyguardShowing: Flow<Boolean>
+ /** Observable for the signal that keyguard is about to go away. */
+ val isKeyguardGoingAway: Flow<Boolean>
+
/** Observable for whether the bouncer is showing. */
val isBouncerShowing: Flow<Boolean>
@@ -84,6 +89,14 @@ interface KeyguardRepository {
val isDozing: Flow<Boolean>
/**
+ * Observable for whether the device is dreaming.
+ *
+ * Dozing/AOD is a specific type of dream, but it is also possible for other non-systemui dreams
+ * to be active, such as screensavers.
+ */
+ val isDreaming: Flow<Boolean>
+
+ /**
* Observable for the amount of doze we are currently in.
*
* While in doze state, this amount can change - driving a cycle of animations designed to avoid
@@ -123,6 +136,11 @@ interface KeyguardRepository {
* Sets the relative offset of the lock-screen clock from its natural position on the screen.
*/
fun setClockPosition(x: Int, y: Int)
+
+ /**
+ * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+ */
+ fun isUdfpsSupported(): Boolean
}
/** Encapsulates application state for the keyguard. */
@@ -131,10 +149,11 @@ class KeyguardRepositoryImpl
@Inject
constructor(
statusBarStateController: StatusBarStateController,
- private val keyguardStateController: KeyguardStateController,
dozeHost: DozeHost,
wakefulnessLifecycle: WakefulnessLifecycle,
biometricUnlockController: BiometricUnlockController,
+ private val keyguardStateController: KeyguardStateController,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -169,6 +188,29 @@ constructor(
awaitClose { keyguardStateController.removeCallback(callback) }
}
+ override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
+ val callback =
+ object : KeyguardStateController.Callback {
+ override fun onKeyguardGoingAwayChanged() {
+ trySendWithFailureLogging(
+ keyguardStateController.isKeyguardGoingAway,
+ TAG,
+ "updated isKeyguardGoingAway"
+ )
+ }
+ }
+
+ keyguardStateController.addCallback(callback)
+ // Adding the callback does not send an initial update.
+ trySendWithFailureLogging(
+ keyguardStateController.isKeyguardGoingAway,
+ TAG,
+ "initial isKeyguardGoingAway"
+ )
+
+ awaitClose { keyguardStateController.removeCallback(callback) }
+ }
+
override val isBouncerShowing: Flow<Boolean> = conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
@@ -211,6 +253,25 @@ constructor(
}
.distinctUntilChanged()
+ override val isDreaming: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onDreamingStateChanged(isDreaming: Boolean) {
+ trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ trySendWithFailureLogging(
+ keyguardUpdateMonitor.isDreaming,
+ TAG,
+ "initial isDreaming",
+ )
+
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+ .distinctUntilChanged()
+
override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
val callback =
object : StatusBarStateController.StateListener {
@@ -311,6 +372,8 @@ constructor(
_clockPosition.value = Position(x, y)
}
+ override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported
+
private fun statusBarStateIntToObject(value: Int): StatusBarState {
return when (value) {
0 -> StatusBarState.SHADE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index e3d1a27dad2b..bce7d92cd8fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -94,11 +94,13 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
*/
private val _transitions =
MutableSharedFlow<TransitionStep>(
+ replay = 2,
extraBufferCapacity = 10,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
override val transitions = _transitions.asSharedFlow().distinctUntilChanged()
private var lastStep: TransitionStep = TransitionStep()
+ private var lastAnimator: ValueAnimator? = null
/*
* When manual control of the transition is requested, a unique [UUID] is used as the handle
@@ -106,19 +108,39 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
*/
private var updateTransitionId: UUID? = null
+ init {
+ // Seed with transitions signaling a boot into lockscreen state
+ emitTransition(
+ TransitionStep(
+ KeyguardState.OFF,
+ KeyguardState.LOCKSCREEN,
+ 0f,
+ TransitionState.STARTED,
+ )
+ )
+ emitTransition(
+ TransitionStep(
+ KeyguardState.OFF,
+ KeyguardState.LOCKSCREEN,
+ 1f,
+ TransitionState.FINISHED,
+ )
+ )
+ }
+
override fun startTransition(info: TransitionInfo): UUID? {
if (lastStep.transitionState != TransitionState.FINISHED) {
- // Open questions:
- // * Queue of transitions? buffer of 1?
- // * Are transitions cancellable if a new one is triggered?
- // * What validation does this need to do?
- Log.wtf(TAG, "Transition still active: $lastStep")
- return null
+ Log.i(TAG, "Transition still active: $lastStep, canceling")
}
+ val startingValue = 1f - lastStep.value
+ lastAnimator?.cancel()
+ lastAnimator = info.animator
+
info.animator?.let { animator ->
// An animator was provided, so use it to run the transition
- animator.setFloatValues(0f, 1f)
+ animator.setFloatValues(startingValue, 1f)
+ animator.duration = ((1f - startingValue) * animator.duration).toLong()
val updateListener =
object : AnimatorUpdateListener {
override fun onAnimationUpdate(animation: ValueAnimator) {
@@ -134,15 +156,24 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
val adapter =
object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
- emitTransition(TransitionStep(info, 0f, TransitionState.STARTED))
+ emitTransition(TransitionStep(info, startingValue, TransitionState.STARTED))
}
override fun onAnimationCancel(animation: Animator) {
- Log.i(TAG, "Cancelling transition: $info")
+ endAnimation(animation, lastStep.value, TransitionState.CANCELED)
}
override fun onAnimationEnd(animation: Animator) {
- emitTransition(TransitionStep(info, 1f, TransitionState.FINISHED))
+ endAnimation(animation, 1f, TransitionState.FINISHED)
+ }
+
+ private fun endAnimation(
+ animation: Animator,
+ value: Float,
+ state: TransitionState
+ ) {
+ emitTransition(TransitionStep(info, value, state))
animator.removeListener(this)
animator.removeUpdateListener(updateListener)
+ lastAnimator = null
}
}
animator.addListener(adapter)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
index 0aeff7fc69fd..e5521c76705d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
@@ -20,10 +20,11 @@ import android.animation.ValueAnimator
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isSleepingOrStartingToSleep
+import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -35,18 +36,30 @@ class AodLockscreenTransitionInteractor
@Inject
constructor(
@Application private val scope: CoroutineScope,
- private val keyguardRepository: KeyguardRepository,
+ private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) : TransitionInteractor("AOD<->LOCKSCREEN") {
override fun start() {
scope.launch {
- keyguardRepository.isDozing
- .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ /*
+ * Listening to the startedKeyguardTransitionStep (last started step) allows this code
+ * to interrupt an active transition, as long as they were either going to LOCKSCREEN or
+ * AOD state. One example is when the user presses the power button in the middle of an
+ * active transition.
+ */
+ keyguardInteractor.wakefulnessState
+ .sample(
+ keyguardTransitionInteractor.startedKeyguardTransitionStep,
+ { a, b -> Pair(a, b) }
+ )
.collect { pair ->
- val (isDozing, keyguardState) = pair
- if (isDozing && keyguardState == KeyguardState.LOCKSCREEN) {
+ val (wakefulnessState, lastStartedStep) = pair
+ if (
+ isSleepingOrStartingToSleep(wakefulnessState) &&
+ lastStartedStep.to == KeyguardState.LOCKSCREEN
+ ) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
@@ -55,7 +68,10 @@ constructor(
getAnimator(),
)
)
- } else if (!isDozing && keyguardState == KeyguardState.AOD) {
+ } else if (
+ isWakingOrStartingToWake(wakefulnessState) &&
+ lastStartedStep.to == KeyguardState.AOD
+ ) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt
new file mode 100644
index 000000000000..dd2967334307
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.util.kotlin.sample
+import java.util.UUID
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class BouncerToGoneTransitionInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val shadeRepository: ShadeRepository,
+ private val keyguardTransitionRepository: KeyguardTransitionRepository,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor
+) : TransitionInteractor("BOUNCER->GONE") {
+
+ private var transitionId: UUID? = null
+
+ override fun start() {
+ listenForKeyguardGoingAway()
+ }
+
+ private fun listenForKeyguardGoingAway() {
+ scope.launch {
+ keyguardInteractor.isKeyguardGoingAway
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (isKeyguardGoingAway, keyguardState) = pair
+ if (isKeyguardGoingAway && keyguardState == KeyguardState.BOUNCER) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = name,
+ from = KeyguardState.BOUNCER,
+ to = KeyguardState.GONE,
+ animator = getAnimator(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(TRANSITION_DURATION_MS)
+ }
+ }
+
+ companion object {
+ private const val TRANSITION_DURATION_MS = 300L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt
new file mode 100644
index 000000000000..c44cda42c68d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class DreamingLockscreenTransitionInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionRepository: KeyguardTransitionRepository,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) : TransitionInteractor("DREAMING<->LOCKSCREEN") {
+
+ override fun start() {
+ scope.launch {
+ keyguardInteractor.isDreaming
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (isDreaming, keyguardState) = pair
+ if (isDreaming && keyguardState == KeyguardState.LOCKSCREEN) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.DREAMING,
+ getAnimator(),
+ )
+ )
+ } else if (!isDreaming && keyguardState == KeyguardState.DREAMING) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.DREAMING,
+ KeyguardState.LOCKSCREEN,
+ getAnimator(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(TRANSITION_DURATION_MS)
+ }
+ }
+
+ companion object {
+ private const val TRANSITION_DURATION_MS = 500L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
new file mode 100644
index 000000000000..9e2b7241ade2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isSleepingOrStartingToSleep
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class DreamingToAodTransitionInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionRepository: KeyguardTransitionRepository,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) : TransitionInteractor("DREAMING->AOD") {
+
+ override fun start() {
+ scope.launch {
+ keyguardInteractor.wakefulnessState
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (wakefulnessState, keyguardState) = pair
+ if (
+ isSleepingOrStartingToSleep(wakefulnessState) &&
+ keyguardState == KeyguardState.DREAMING
+ ) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.DREAMING,
+ KeyguardState.AOD,
+ getAnimator(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(TRANSITION_DURATION_MS)
+ }
+ }
+
+ companion object {
+ private const val TRANSITION_DURATION_MS = 300L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
index ede50b068de3..d2a7486eed0b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
@@ -48,4 +48,9 @@ constructor(
fun setAnimateDozingTransitions(animate: Boolean) {
repository.setAnimateDozingTransitions(animate)
}
+
+ /**
+ * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+ */
+ fun shouldConstrainToTopOfLockIcon(): Boolean = repository.isUdfpsSupported()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 614ff8d930d8..5a1c70264ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -41,8 +41,15 @@ constructor(
val dozeAmount: Flow<Float> = repository.dozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
+ /**
+ * Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true,
+ * but not vice-versa.
+ */
+ val isDreaming: Flow<Boolean> = repository.isDreaming
/** Whether the keyguard is showing or not. */
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+ /** Whether the keyguard is going away. */
+ val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
/** Whether the bouncer is showing or not. */
val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
/** The device wake/sleep state */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 57fb4a114700..58a8093d49d2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -41,12 +41,24 @@ constructor(
}
scope.launch {
+ keyguardInteractor.isBouncerShowing.collect { logger.v("Bouncer showing", it) }
+ }
+
+ scope.launch { keyguardInteractor.isDozing.collect { logger.v("isDozing", it) } }
+
+ scope.launch {
interactor.finishedKeyguardTransitionStep.collect {
logger.i("Finished transition", it)
}
}
scope.launch {
+ interactor.canceledKeyguardTransitionStep.collect {
+ logger.i("Canceled transition", it)
+ }
+ }
+
+ scope.launch {
interactor.startedKeyguardTransitionStep.collect { logger.i("Started transition", it) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index a7c6d4450336..43dd358e4808 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -42,6 +42,9 @@ constructor(
is GoneAodTransitionInteractor -> Log.d(TAG, "Started $it")
is LockscreenGoneTransitionInteractor -> Log.d(TAG, "Started $it")
is AodToGoneTransitionInteractor -> Log.d(TAG, "Started $it")
+ is BouncerToGoneTransitionInteractor -> Log.d(TAG, "Started $it")
+ is DreamingLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
+ is DreamingToAodTransitionInteractor -> Log.d(TAG, "Started $it")
}
it.start()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 749183e241e5..54a4f493d21d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -57,6 +57,14 @@ constructor(
lockscreenToAodTransition,
)
+ /* The last [TransitionStep] with a [TransitionState] of STARTED */
+ val startedKeyguardTransitionStep: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
+
+ /* The last [TransitionStep] with a [TransitionState] of CANCELED */
+ val canceledKeyguardTransitionStep: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.transitionState == TransitionState.CANCELED }
+
/* The last [TransitionStep] with a [TransitionState] of FINISHED */
val finishedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
@@ -64,8 +72,4 @@ constructor(
/* The last completed [KeyguardState] transition */
val finishedKeyguardState: Flow<KeyguardState> =
finishedKeyguardTransitionStep.map { step -> step.to }
-
- /* The last [TransitionStep] with a [TransitionState] of STARTED */
- val startedKeyguardTransitionStep: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index fd4814d2bc94..cca2d566556e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -56,10 +56,20 @@ constructor(
private fun listenForBouncerHiding() {
scope.launch {
keyguardInteractor.isBouncerShowing
- .sample(keyguardInteractor.wakefulnessState, { a, b -> Pair(a, b) })
- .collect { pair ->
- val (isBouncerShowing, wakefulnessState) = pair
- if (!isBouncerShowing) {
+ .sample(
+ combine(
+ keyguardInteractor.wakefulnessState,
+ keyguardTransitionInteractor.startedKeyguardTransitionStep,
+ ) { a, b ->
+ Pair(a, b)
+ },
+ { a, bc -> Triple(a, bc.first, bc.second) }
+ )
+ .collect { triple ->
+ val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
+ if (
+ !isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
+ ) {
val to =
if (
wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP ||
@@ -90,10 +100,10 @@ constructor(
combine(
keyguardTransitionInteractor.finishedKeyguardState,
keyguardInteractor.statusBarState,
- ) { keyguardState, statusBarState ->
- Pair(keyguardState, statusBarState)
+ ) { a, b ->
+ Pair(a, b)
},
- { shadeModel, pair -> Triple(shadeModel, pair.first, pair.second) }
+ { a, bc -> Triple(a, bc.first, bc.second) }
)
.collect { triple ->
val (shadeModel, keyguardState, statusBarState) = triple
@@ -116,8 +126,7 @@ constructor(
)
} else {
// TODO (b/251849525): Remove statusbarstate check when that state is
- // integrated
- // into KeyguardTransitionRepository
+ // integrated into KeyguardTransitionRepository
if (
keyguardState == KeyguardState.LOCKSCREEN &&
shadeModel.isUserDragging &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt
index 6c1adbd68ef2..4100f7a8413a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt
@@ -23,6 +23,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
@@ -34,23 +35,27 @@ class LockscreenGoneTransitionInteractor
constructor(
@Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
) : TransitionInteractor("LOCKSCREEN->GONE") {
override fun start() {
scope.launch {
- keyguardInteractor.isKeyguardShowing.collect { isShowing ->
- if (!isShowing) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- name,
- KeyguardState.LOCKSCREEN,
- KeyguardState.GONE,
- getAnimator(),
+ keyguardInteractor.isKeyguardGoingAway
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (isKeyguardGoingAway, keyguardState) = pair
+ if (!isKeyguardGoingAway && keyguardState == KeyguardState.LOCKSCREEN) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.GONE,
+ getAnimator(),
+ )
)
- )
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt
index 10c7a3774e09..c5e49c61e581 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt
@@ -24,9 +24,9 @@ import javax.inject.Inject
/** Interactor to add and remove callbacks for the bouncer. */
@SysUISingleton
-class BouncerCallbackInteractor @Inject constructor() {
+class PrimaryBouncerCallbackInteractor @Inject constructor() {
private var resetCallbacks = ListenerSet<KeyguardBouncer.KeyguardResetCallback>()
- private var expansionCallbacks = ArrayList<KeyguardBouncer.BouncerExpansionCallback>()
+ private var expansionCallbacks = ArrayList<KeyguardBouncer.PrimaryBouncerExpansionCallback>()
/** Add a KeyguardResetCallback. */
fun addKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) {
resetCallbacks.addIfAbsent(callback)
@@ -38,7 +38,7 @@ class BouncerCallbackInteractor @Inject constructor() {
}
/** Adds a callback to listen to bouncer expansion updates. */
- fun addBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) {
+ fun addBouncerExpansionCallback(callback: KeyguardBouncer.PrimaryBouncerExpansionCallback) {
if (!expansionCallbacks.contains(callback)) {
expansionCallbacks.add(callback)
}
@@ -48,7 +48,7 @@ class BouncerCallbackInteractor @Inject constructor() {
* Removes a previously added callback. If the callback was never added, this method does
* nothing.
*/
- fun removeBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) {
+ fun removeBouncerExpansionCallback(callback: KeyguardBouncer.PrimaryBouncerExpansionCallback) {
expansionCallbacks.remove(callback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index dbb0352c2187..910cdf2df5a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -44,24 +44,27 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
-/** Encapsulates business logic for interacting with the lock-screen bouncer. */
+/**
+ * Encapsulates business logic for interacting with the lock-screen primary (pin/pattern/password)
+ * bouncer.
+ */
@SysUISingleton
-class BouncerInteractor
+class PrimaryBouncerInteractor
@Inject
constructor(
private val repository: KeyguardBouncerRepository,
- private val bouncerView: BouncerView,
+ private val primaryBouncerView: BouncerView,
@Main private val mainHandler: Handler,
private val keyguardStateController: KeyguardStateController,
private val keyguardSecurityModel: KeyguardSecurityModel,
- private val callbackInteractor: BouncerCallbackInteractor,
+ private val primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor,
private val falsingCollector: FalsingCollector,
private val dismissCallbackRegistry: DismissCallbackRegistry,
keyguardBypassController: KeyguardBypassController,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
) {
/** Whether we want to wait for face auth. */
- private val bouncerFaceDelay =
+ private val primaryBouncerFaceDelay =
keyguardStateController.isFaceAuthEnabled &&
!keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
KeyguardUpdateMonitor.getCurrentUser()
@@ -70,38 +73,39 @@ constructor(
!keyguardUpdateMonitor.userNeedsStrongAuth() &&
!keyguardBypassController.bypassEnabled
- /** Runnable to show the bouncer. */
+ /** Runnable to show the primary bouncer. */
val showRunnable = Runnable {
- repository.setVisible(true)
- repository.setShow(
+ repository.setPrimaryVisible(true)
+ repository.setPrimaryShow(
KeyguardBouncerModel(
promptReason = repository.bouncerPromptReason ?: 0,
errorMessage = repository.bouncerErrorMessage,
expansionAmount = repository.panelExpansionAmount.value
)
)
- repository.setShowingSoon(false)
+ repository.setPrimaryShowingSoon(false)
}
val keyguardAuthenticated: Flow<Boolean> = repository.keyguardAuthenticated.filterNotNull()
val screenTurnedOff: Flow<Unit> = repository.onScreenTurnedOff.filter { it }.map {}
- val show: Flow<KeyguardBouncerModel> = repository.show.filterNotNull()
- val hide: Flow<Unit> = repository.hide.filter { it }.map {}
- val startingToHide: Flow<Unit> = repository.startingToHide.filter { it }.map {}
- val isVisible: Flow<Boolean> = repository.isVisible
+ val show: Flow<KeyguardBouncerModel> = repository.primaryBouncerShow.filterNotNull()
+ val hide: Flow<Unit> = repository.primaryBouncerHide.filter { it }.map {}
+ val startingToHide: Flow<Unit> = repository.primaryBouncerStartingToHide.filter { it }.map {}
+ val isVisible: Flow<Boolean> = repository.primaryBouncerVisible
val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull()
val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull()
val startingDisappearAnimation: Flow<Runnable> =
- repository.startingDisappearAnimation.filterNotNull()
+ repository.primaryBouncerStartingDisappearAnimation.filterNotNull()
val resourceUpdateRequests: Flow<Boolean> = repository.resourceUpdateRequests.filter { it }
val keyguardPosition: Flow<Float> = repository.keyguardPosition
val panelExpansionAmount: Flow<Float> = repository.panelExpansionAmount
/** 0f = bouncer fully hidden. 1f = bouncer fully visible. */
- val bouncerExpansion: Flow<Float> = //
- combine(repository.panelExpansionAmount, repository.isVisible) { expansionAmount, isVisible
- ->
- if (isVisible) {
- 1f - expansionAmount
+ val bouncerExpansion: Flow<Float> =
+ combine(repository.panelExpansionAmount, repository.primaryBouncerVisible) {
+ panelExpansion,
+ primaryBouncerVisible ->
+ if (primaryBouncerVisible) {
+ 1f - panelExpansion
} else {
0f
}
@@ -113,16 +117,16 @@ constructor(
@JvmOverloads
fun show(isScrimmed: Boolean) {
// Reset some states as we show the bouncer.
- repository.setShowMessage(null)
repository.setOnScreenTurnedOff(false)
repository.setKeyguardAuthenticated(null)
- repository.setHide(false)
- repository.setStartingToHide(false)
+ repository.setPrimaryHide(false)
+ repository.setPrimaryStartingToHide(false)
val resumeBouncer =
- (repository.isVisible.value || repository.showingSoon.value) && needsFullscreenBouncer()
+ (repository.primaryBouncerVisible.value ||
+ repository.primaryBouncerShowingSoon.value) && needsFullscreenBouncer()
- if (!resumeBouncer && repository.show.value != null) {
+ if (!resumeBouncer && repository.primaryBouncerShow.value != null) {
// If bouncer is visible, the bouncer is already showing.
return
}
@@ -134,29 +138,29 @@ constructor(
}
Trace.beginSection("KeyguardBouncer#show")
- repository.setScrimmed(isScrimmed)
+ repository.setPrimaryScrimmed(isScrimmed)
if (isScrimmed) {
setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
}
if (resumeBouncer) {
- bouncerView.delegate?.resume()
+ primaryBouncerView.delegate?.resume()
// Bouncer is showing the next security screen and we just need to prompt a resume.
return
}
- if (bouncerView.delegate?.showNextSecurityScreenOrFinish() == true) {
+ if (primaryBouncerView.delegate?.showNextSecurityScreenOrFinish() == true) {
// Keyguard is done.
return
}
- repository.setShowingSoon(true)
- if (bouncerFaceDelay) {
+ repository.setPrimaryShowingSoon(true)
+ if (primaryBouncerFaceDelay) {
mainHandler.postDelayed(showRunnable, 1200L)
} else {
DejankUtils.postAfterTraversal(showRunnable)
}
keyguardStateController.notifyBouncerShowing(true)
- callbackInteractor.dispatchStartingToShow()
+ primaryBouncerCallbackInteractor.dispatchStartingToShow()
Trace.endSection()
}
@@ -174,10 +178,10 @@ constructor(
falsingCollector.onBouncerHidden()
keyguardStateController.notifyBouncerShowing(false /* showing */)
cancelShowRunnable()
- repository.setShowingSoon(false)
- repository.setVisible(false)
- repository.setHide(true)
- repository.setShow(null)
+ repository.setPrimaryShowingSoon(false)
+ repository.setPrimaryVisible(false)
+ repository.setPrimaryHide(true)
+ repository.setPrimaryShow(null)
Trace.endSection()
}
@@ -191,7 +195,7 @@ constructor(
fun setPanelExpansion(expansion: Float) {
val oldExpansion = repository.panelExpansionAmount.value
val expansionChanged = oldExpansion != expansion
- if (repository.startingDisappearAnimation.value == null) {
+ if (repository.primaryBouncerStartingDisappearAnimation.value == null) {
repository.setPanelExpansion(expansion)
}
@@ -200,25 +204,28 @@ constructor(
oldExpansion != KeyguardBouncer.EXPANSION_VISIBLE
) {
falsingCollector.onBouncerShown()
- callbackInteractor.dispatchFullyShown()
+ primaryBouncerCallbackInteractor.dispatchFullyShown()
} else if (
expansion == KeyguardBouncer.EXPANSION_HIDDEN &&
oldExpansion != KeyguardBouncer.EXPANSION_HIDDEN
) {
- repository.setVisible(false)
- repository.setShow(null)
- falsingCollector.onBouncerHidden()
- DejankUtils.postAfterTraversal { callbackInteractor.dispatchReset() }
- callbackInteractor.dispatchFullyHidden()
+ /*
+ * There are cases where #hide() was not invoked, such as when
+ * NotificationPanelViewController controls the hide animation. Make sure the state gets
+ * updated by calling #hide() directly.
+ */
+ hide()
+ DejankUtils.postAfterTraversal { primaryBouncerCallbackInteractor.dispatchReset() }
+ primaryBouncerCallbackInteractor.dispatchFullyHidden()
} else if (
expansion != KeyguardBouncer.EXPANSION_VISIBLE &&
oldExpansion == KeyguardBouncer.EXPANSION_VISIBLE
) {
- callbackInteractor.dispatchStartingToHide()
- repository.setStartingToHide(true)
+ primaryBouncerCallbackInteractor.dispatchStartingToHide()
+ repository.setPrimaryStartingToHide(true)
}
if (expansionChanged) {
- callbackInteractor.dispatchExpansionChanged(expansion)
+ primaryBouncerCallbackInteractor.dispatchExpansionChanged(expansion)
}
}
@@ -236,7 +243,7 @@ constructor(
onDismissAction: ActivityStarter.OnDismissAction?,
cancelAction: Runnable?
) {
- bouncerView.delegate?.setDismissAction(onDismissAction, cancelAction)
+ primaryBouncerView.delegate?.setDismissAction(onDismissAction, cancelAction)
}
/** Update the resources of the views. */
@@ -266,7 +273,7 @@ constructor(
/** Notify that view visibility has changed. */
fun notifyBouncerVisibilityHasChanged(visibility: Int) {
- callbackInteractor.dispatchVisibilityChanged(visibility)
+ primaryBouncerCallbackInteractor.dispatchVisibilityChanged(visibility)
}
/** Notify that the resources have been updated */
@@ -283,38 +290,39 @@ constructor(
fun startDisappearAnimation(runnable: Runnable) {
val finishRunnable = Runnable {
runnable.run()
- repository.setStartDisappearAnimation(null)
+ repository.setPrimaryStartDisappearAnimation(null)
}
- repository.setStartDisappearAnimation(finishRunnable)
+ repository.setPrimaryStartDisappearAnimation(finishRunnable)
}
/** Returns whether bouncer is fully showing. */
fun isFullyShowing(): Boolean {
- return (repository.showingSoon.value || repository.isVisible.value) &&
+ return (repository.primaryBouncerShowingSoon.value ||
+ repository.primaryBouncerVisible.value) &&
repository.panelExpansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE &&
- repository.startingDisappearAnimation.value == null
+ repository.primaryBouncerStartingDisappearAnimation.value == null
}
/** Returns whether bouncer is scrimmed. */
fun isScrimmed(): Boolean {
- return repository.isScrimmed.value
+ return repository.primaryBouncerScrimmed.value
}
/** If bouncer expansion is between 0f and 1f non-inclusive. */
fun isInTransit(): Boolean {
- return repository.showingSoon.value ||
+ return repository.primaryBouncerShowingSoon.value ||
repository.panelExpansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN &&
repository.panelExpansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE
}
/** Return whether bouncer is animating away. */
fun isAnimatingAway(): Boolean {
- return repository.startingDisappearAnimation.value != null
+ return repository.primaryBouncerStartingDisappearAnimation.value != null
}
/** Return whether bouncer will dismiss with actions */
fun willDismissWithAction(): Boolean {
- return bouncerView.delegate?.willDismissWithActions() == true
+ return primaryBouncerView.delegate?.willDismissWithActions() == true
}
/** Returns whether the bouncer should be full screen. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index 37f33afbf53e..dbffeab436a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -46,5 +46,19 @@ abstract class StartKeyguardTransitionModule {
@Binds
@IntoSet
+ abstract fun bouncerGone(impl: BouncerToGoneTransitionInteractor): TransitionInteractor
+
+ @Binds
+ @IntoSet
abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor
+
+ @Binds
+ @IntoSet
+ abstract fun dreamingLockscreen(
+ impl: DreamingLockscreenTransitionInteractor
+ ): TransitionInteractor
+
+ @Binds
+ @IntoSet
+ abstract fun dreamingToAod(impl: DreamingToAodTransitionInteractor): TransitionInteractor
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 7958033ba017..dd908c420fcb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -17,12 +17,29 @@ package com.android.systemui.keyguard.shared.model
/** List of all possible states to transition to/from */
enum class KeyguardState {
+ /*
+ * The display is completely off, as well as any sensors that would trigger the device to wake
+ * up.
+ */
+ OFF,
/**
- * For initialization as well as when the security method is set to NONE, indicating that
- * the keyguard should never be shown.
+ * The device has entered a special low-power mode within SystemUI. Doze is technically a
+ * special dream service implementation. No UI is visible. In this state, a least some
+ * low-powered sensors such as lift to wake or tap to wake are enabled, or wake screen for
+ * notifications is enabled, allowing the device to quickly wake up.
+ */
+ DOZING,
+ /*
+ * A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
+ * DOZING is an example of special version of this state. Dreams may be implemented by third
+ * parties to present their own UI over keyguard, like a screensaver.
+ */
+ DREAMING,
+ /**
+ * The device has entered a special low-power mode within SystemUI, also called the Always-on
+ * Display (AOD). A minimal UI is presented to show critical information. If the device is in
+ * low-power mode without a UI, then it is DOZING.
*/
- NONE,
- /* Always-on Display. The device is in a low-power mode with a minimal UI visible */
AOD,
/*
* The security screen prompt UI, containing PIN, Password, Pattern, and all FPS
@@ -34,7 +51,6 @@ enum class KeyguardState {
* unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
*/
LOCKSCREEN,
-
/*
* Keyguard is no longer visible. In most cases the user has just authenticated and keyguard
* is being removed, but there are other cases where the user is swiping away keyguard, such as
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
index 0e0465bb5207..38a93b50ea97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
@@ -17,7 +17,12 @@ package com.android.systemui.keyguard.shared.model
/** Possible states for a running transition between [State] */
enum class TransitionState {
+ /* Transition has begun. */
STARTED,
+ /* Transition is actively running. */
RUNNING,
- FINISHED
+ /* Transition has completed successfully. */
+ FINISHED,
+ /* Transition has been interrupted, and not completed successfully. */
+ CANCELED,
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
index 732a6f7b887a..767fd58f78e9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
@@ -17,8 +17,8 @@ package com.android.systemui.keyguard.shared.model
/** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */
data class TransitionStep(
- val from: KeyguardState = KeyguardState.NONE,
- val to: KeyguardState = KeyguardState.NONE,
+ val from: KeyguardState = KeyguardState.OFF,
+ val to: KeyguardState = KeyguardState.OFF,
val value: Float = 0f, // constrained [0.0, 1.0]
val transitionState: TransitionState = TransitionState.FINISHED,
val ownerName: String = "",
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
index 64f834d6c5ab..92040f4f0348 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
@@ -24,5 +24,15 @@ enum class WakefulnessModel {
/** Device is now fully awake and interactive. */
AWAKE,
/** Signal that the device is now going to sleep. */
- STARTING_TO_SLEEP,
+ STARTING_TO_SLEEP;
+
+ companion object {
+ fun isSleepingOrStartingToSleep(model: WakefulnessModel): Boolean {
+ return model == ASLEEP || model == STARTING_TO_SLEEP
+ }
+
+ fun isWakingOrStartingToWake(model: WakefulnessModel): Boolean {
+ return model == AWAKE || model == STARTING_TO_WAKE
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 2c99ca59ba6b..3276b6dd9748 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -27,6 +27,8 @@ import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.LockIconViewController
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.Expandable
@@ -69,6 +71,11 @@ object KeyguardBottomAreaViewBinder {
/** Notifies that device configuration has changed. */
fun onConfigurationChanged()
+
+ /**
+ * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+ */
+ fun shouldConstrainToTopOfLockIcon(): Boolean
}
/** Binds the view to the view-model, continuing to update the former based on the latter. */
@@ -208,6 +215,9 @@ object KeyguardBottomAreaViewBinder {
override fun onConfigurationChanged() {
configurationBasedDimensions.value = loadFromResources(view)
}
+
+ override fun shouldConstrainToTopOfLockIcon(): Boolean =
+ viewModel.shouldConstrainToTopOfLockIcon()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index a22958b74bb9..7739a456fcb7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -94,6 +94,10 @@ object KeyguardBouncerViewBinder {
viewModel.setBouncerViewDelegate(delegate)
launch {
viewModel.show.collect {
+ hostViewController.showPromptReason(it.promptReason)
+ it.errorMessage?.let { errorMessage ->
+ hostViewController.showErrorMessage(errorMessage)
+ }
hostViewController.showPrimarySecurityScreen()
hostViewController.appear(
SystemBarUtils.getStatusBarHeight(view.context)
@@ -102,18 +106,6 @@ object KeyguardBouncerViewBinder {
}
launch {
- viewModel.showPromptReason.collect { prompt ->
- hostViewController.showPromptReason(prompt)
- }
- }
-
- launch {
- viewModel.showBouncerErrorMessage.collect { errorMessage ->
- hostViewController.showErrorMessage(errorMessage)
- }
- }
-
- launch {
viewModel.showWithFullExpansion.collect { model ->
hostViewController.resetSecurityContainer()
hostViewController.showPromptReason(model.promptReason)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index b6b230441397..227796f43e35 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -90,6 +90,12 @@ constructor(
.distinctUntilChanged()
}
+ /**
+ * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+ */
+ fun shouldConstrainToTopOfLockIcon(): Boolean =
+ bottomAreaInteractor.shouldConstrainToTopOfLockIcon()
+
private fun button(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceViewModel> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
index 9a9284371074..526ae741793c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -19,14 +19,13 @@ package com.android.systemui.keyguard.ui.viewmodel
import android.view.View
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.BouncerViewDelegate
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
/** Models UI state for the lock screen bouncer; handles user input. */
@@ -34,7 +33,7 @@ class KeyguardBouncerViewModel
@Inject
constructor(
private val view: BouncerView,
- private val interactor: BouncerInteractor,
+ private val interactor: PrimaryBouncerInteractor,
) {
/** Observe on bouncer expansion amount. */
val bouncerExpansionAmount: Flow<Float> = interactor.panelExpansionAmount
@@ -45,13 +44,6 @@ constructor(
/** Observe whether bouncer is showing. */
val show: Flow<KeyguardBouncerModel> = interactor.show
- /** Observe bouncer prompt when bouncer is showing. */
- val showPromptReason: Flow<Int> = interactor.show.map { it.promptReason }
-
- /** Observe bouncer error message when bouncer is showing. */
- val showBouncerErrorMessage: Flow<CharSequence> =
- interactor.show.map { it.errorMessage }.filterNotNull()
-
/** Observe visible expansion when bouncer is showing. */
val showWithFullExpansion: Flow<KeyguardBouncerModel> =
interactor.show.filter { it.expansionAmount == EXPANSION_VISIBLE }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 397bffcaa64c..22f91f359de8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -18,6 +18,9 @@ package com.android.systemui.media;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static com.android.systemui.screenrecord.ScreenShareOptionKt.ENTIRE_SCREEN;
+import static com.android.systemui.screenrecord.ScreenShareOptionKt.SINGLE_APP;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
@@ -44,6 +47,8 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
+import com.android.systemui.screenrecord.ScreenShareOption;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils;
@@ -102,7 +107,9 @@ public class MediaProjectionPermissionActivity extends Activity
CharSequence dialogText = null;
CharSequence dialogTitle = null;
+ String appName = null;
if (Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName)) {
+ // TODO(b/253438807): handle special app name
dialogText = getString(R.string.media_projection_dialog_service_text);
dialogTitle = getString(R.string.media_projection_dialog_service_title);
} else {
@@ -132,7 +139,7 @@ public class MediaProjectionPermissionActivity extends Activity
String unsanitizedAppName = TextUtils.ellipsize(label,
paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString();
- String appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
+ appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
String actionText = getString(R.string.media_projection_dialog_text, appName);
SpannableString message = new SpannableString(actionText);
@@ -146,27 +153,28 @@ public class MediaProjectionPermissionActivity extends Activity
dialogTitle = getString(R.string.media_projection_dialog_title, appName);
}
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this,
- R.style.Theme_SystemUI_Dialog)
- .setTitle(dialogTitle)
- .setIcon(R.drawable.ic_media_projection_permission)
- .setMessage(dialogText)
- .setPositiveButton(R.string.media_projection_action_text, this)
- .setNeutralButton(android.R.string.cancel, this)
- .setOnCancelListener(this);
-
if (isPartialScreenSharingEnabled()) {
- // This is a temporary entry point before we have a new permission dialog
- // TODO(b/233183090): this activity should be redesigned to have a dropdown selector
- dialogBuilder.setNegativeButton("App", this);
+ mDialog = new MediaProjectionPermissionDialog(this, () -> {
+ ScreenShareOption selectedOption =
+ ((MediaProjectionPermissionDialog) mDialog).getSelectedScreenShareOption();
+ grantMediaProjectionPermission(selectedOption.getMode());
+ }, appName);
+ } else {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this,
+ R.style.Theme_SystemUI_Dialog)
+ .setTitle(dialogTitle)
+ .setIcon(R.drawable.ic_media_projection_permission)
+ .setMessage(dialogText)
+ .setPositiveButton(R.string.media_projection_action_text, this)
+ .setNeutralButton(android.R.string.cancel, this);
+ mDialog = dialogBuilder.create();
}
- mDialog = dialogBuilder.create();
-
SystemUIDialog.registerDismissListener(mDialog);
SystemUIDialog.applyFlags(mDialog);
SystemUIDialog.setDialogSize(mDialog);
+ mDialog.setOnCancelListener(this);
mDialog.create();
mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
@@ -186,12 +194,17 @@ public class MediaProjectionPermissionActivity extends Activity
@Override
public void onClick(DialogInterface dialog, int which) {
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ grantMediaProjectionPermission(ENTIRE_SCREEN);
+ }
+ }
+
+ private void grantMediaProjectionPermission(int screenShareMode) {
try {
- if (which == AlertDialog.BUTTON_POSITIVE) {
+ if (screenShareMode == ENTIRE_SCREEN) {
setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName));
}
-
- if (isPartialScreenSharingEnabled() && which == AlertDialog.BUTTON_NEGATIVE) {
+ if (isPartialScreenSharingEnabled() && screenShareMode == SINGLE_APP) {
IMediaProjection projection = createProjection(mUid, mPackageName);
final Intent intent = new Intent(this, MediaProjectionAppSelectorActivity.class);
intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
index a7f1b95555ba..a8f39fa9a456 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
@@ -26,7 +26,8 @@ import android.widget.TextView
import androidx.constraintlayout.widget.Barrier
import com.android.systemui.R
import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.ripple.MultiRippleView
+import com.android.systemui.surfaceeffects.ripple.MultiRippleView
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.TransitionLayout
private const val TAG = "MediaViewHolder"
@@ -38,6 +39,8 @@ class MediaViewHolder constructor(itemView: View) {
// Player information
val albumView = itemView.requireViewById<ImageView>(R.id.album_art)
val multiRippleView = itemView.requireViewById<MultiRippleView>(R.id.touch_ripple_view)
+ val turbulenceNoiseView =
+ itemView.requireViewById<TurbulenceNoiseView>(R.id.turbulence_noise_view)
val appIcon = itemView.requireViewById<ImageView>(R.id.icon)
val titleText = itemView.requireViewById<TextView>(R.id.header_title)
val artistText = itemView.requireViewById<TextView>(R.id.header_artist)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt
index a7ed69a9ab73..cacb3e2bbe4d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt
@@ -29,7 +29,6 @@ class SmartspaceMediaDataProvider @Inject constructor() : BcSmartspaceDataPlugin
private val smartspaceMediaTargetListeners: MutableList<SmartspaceTargetListener> =
mutableListOf()
- private var smartspaceMediaTargets: List<SmartspaceTarget> = listOf()
override fun registerListener(smartspaceTargetListener: SmartspaceTargetListener) {
smartspaceMediaTargetListeners.add(smartspaceTargetListener)
@@ -41,22 +40,7 @@ class SmartspaceMediaDataProvider @Inject constructor() : BcSmartspaceDataPlugin
/** Updates Smartspace data and propagates it to any listeners. */
override fun onTargetsAvailable(targets: List<SmartspaceTarget>) {
- // Filter out non-media targets.
- val mediaTargets = mutableListOf<SmartspaceTarget>()
- for (target in targets) {
- val smartspaceTarget = target
- if (smartspaceTarget.featureType == SmartspaceTarget.FEATURE_MEDIA) {
- mediaTargets.add(smartspaceTarget)
- }
- }
-
- if (!mediaTargets.isEmpty()) {
- Log.d(TAG, "Forwarding Smartspace media updates $mediaTargets")
- }
-
- smartspaceMediaTargets = mediaTargets
- smartspaceMediaTargetListeners.forEach {
- it.onSmartspaceTargetsUpdated(smartspaceMediaTargets)
- }
+ Log.d(TAG, "Forwarding Smartspace updates $targets")
+ smartspaceMediaTargetListeners.forEach { it.onSmartspaceTargetsUpdated(targets) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
index 918417fcd9a9..93be6a78ccd5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
@@ -29,7 +29,8 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.monet.ColorScheme
-import com.android.systemui.ripple.MultiRippleController
+import com.android.systemui.surfaceeffects.ripple.MultiRippleController
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
/**
* A [ColorTransition] is an object that updates the colors of views each time [updateColorScheme]
@@ -102,13 +103,21 @@ internal constructor(
private val context: Context,
private val mediaViewHolder: MediaViewHolder,
private val multiRippleController: MultiRippleController,
+ private val turbulenceNoiseController: TurbulenceNoiseController,
animatingColorTransitionFactory: AnimatingColorTransitionFactory
) {
constructor(
context: Context,
mediaViewHolder: MediaViewHolder,
multiRippleController: MultiRippleController,
- ) : this(context, mediaViewHolder, multiRippleController, ::AnimatingColorTransition)
+ turbulenceNoiseController: TurbulenceNoiseController
+ ) : this(
+ context,
+ mediaViewHolder,
+ multiRippleController,
+ turbulenceNoiseController,
+ ::AnimatingColorTransition
+ )
val bgColor = context.getColor(com.android.systemui.R.color.material_dynamic_secondary95)
val surfaceColor =
@@ -129,6 +138,7 @@ internal constructor(
mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary)
multiRippleController.updateColor(accentPrimary)
+ turbulenceNoiseController.updateNoiseColor(accentPrimary)
}
val accentSecondary =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index baeee9fc4f74..8aaee81a57dd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -610,7 +610,8 @@ constructor(
// are
// elements in mediaPlayers.
if (MediaPlayerData.players().size != mediaContent.childCount) {
- throw IllegalStateException(
+ Log.e(
+ TAG,
"Size of players list and number of views in carousel are out of sync. " +
"Players size is ${MediaPlayerData.players().size}. " +
"View count is ${mediaContent.childCount}."
@@ -671,7 +672,8 @@ constructor(
// are
// elements in mediaPlayers.
if (MediaPlayerData.players().size != mediaContent.childCount) {
- throw IllegalStateException(
+ Log.e(
+ TAG,
"Size of players list and number of views in carousel are out of sync. " +
"Players size is ${MediaPlayerData.players().size}. " +
"View count is ${mediaContent.childCount}."
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 5b14cf34827a..47470525240c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -31,6 +31,7 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
+import android.graphics.BlendMode;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
@@ -64,6 +65,7 @@ import androidx.annotation.UiThread;
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.settingslib.widget.AdaptiveIcon;
@@ -97,13 +99,16 @@ import com.android.systemui.monet.ColorScheme;
import com.android.systemui.monet.Style;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.ripple.MultiRippleController;
-import com.android.systemui.ripple.RippleAnimation;
-import com.android.systemui.ripple.RippleAnimationConfig;
-import com.android.systemui.ripple.RippleShader;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.surfaceeffects.ripple.MultiRippleController;
+import com.android.systemui.surfaceeffects.ripple.MultiRippleView;
+import com.android.systemui.surfaceeffects.ripple.RippleAnimation;
+import com.android.systemui.surfaceeffects.ripple.RippleAnimationConfig;
+import com.android.systemui.surfaceeffects.ripple.RippleShader;
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig;
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController;
import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.animation.TransitionLayout;
import com.android.systemui.util.time.SystemClock;
@@ -216,7 +221,9 @@ public class MediaControlPanel {
private boolean mShowBroadcastDialogButton = false;
private String mSwitchBroadcastApp;
private MultiRippleController mMultiRippleController;
+ private TurbulenceNoiseController mTurbulenceNoiseController;
private FeatureFlags mFeatureFlags;
+ private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig = null;
/**
* Initialize a new control panel
@@ -373,6 +380,7 @@ public class MediaControlPanel {
mMediaViewController.attach(player, MediaViewController.TYPE.PLAYER);
vh.getPlayer().setOnLongClickListener(v -> {
+ if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true;
if (!mMediaViewController.isGutsVisible()) {
openGuts();
return true;
@@ -393,9 +401,20 @@ public class MediaControlPanel {
AnimatorSet exit = loadAnimator(R.anim.media_metadata_exit,
Interpolators.EMPHASIZED_ACCELERATE, titleText, artistText);
- mMultiRippleController = new MultiRippleController(vh.getMultiRippleView());
+ MultiRippleView multiRippleView = vh.getMultiRippleView();
+ mMultiRippleController = new MultiRippleController(multiRippleView);
+ mTurbulenceNoiseController = new TurbulenceNoiseController(vh.getTurbulenceNoiseView());
+ multiRippleView.addRipplesFinishedListener(
+ () -> {
+ if (mTurbulenceNoiseAnimationConfig == null) {
+ mTurbulenceNoiseAnimationConfig = createLingeringNoiseAnimation();
+ }
+ // Color will be correctly updated in ColorSchemeTransition.
+ mTurbulenceNoiseController.play(mTurbulenceNoiseAnimationConfig);
+ }
+ );
mColorSchemeTransition = new ColorSchemeTransition(
- mContext, mMediaViewHolder, mMultiRippleController);
+ mContext, mMediaViewHolder, mMultiRippleController, mTurbulenceNoiseController);
mMetadataAnimationHandler = new MetadataAnimationHandler(exit, enter);
}
@@ -423,6 +442,7 @@ public class MediaControlPanel {
mMediaViewController.attach(recommendations, MediaViewController.TYPE.RECOMMENDATION);
mRecommendationViewHolder.getRecommendations().setOnLongClickListener(v -> {
+ if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true;
if (!mMediaViewController.isGutsVisible()) {
openGuts();
return true;
@@ -1025,7 +1045,7 @@ public class MediaControlPanel {
/* maxWidth= */ maxSize,
/* maxHeight= */ maxSize,
/* pixelDensity= */ getContext().getResources().getDisplayMetrics().density,
- mColorSchemeTransition.getAccentPrimary().getTargetColor(),
+ mColorSchemeTransition.getAccentPrimary().getCurrentColor(),
/* opacity= */ 100,
/* shouldFillRipple= */ false,
/* sparkleStrength= */ 0f,
@@ -1034,6 +1054,26 @@ public class MediaControlPanel {
);
}
+ private TurbulenceNoiseAnimationConfig createLingeringNoiseAnimation() {
+ return new TurbulenceNoiseAnimationConfig(
+ TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_GRID_COUNT,
+ TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER,
+ /* noiseMoveSpeedX= */ 0f,
+ /* noiseMoveSpeedY= */ 0f,
+ TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z,
+ /* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(),
+ // We want to add (BlendMode.PLUS) the turbulence noise on top of the album art.
+ // Thus, set the background color with alpha 0.
+ /* backgroundColor= */ ColorUtils.setAlphaComponent(Color.BLACK, 0),
+ TurbulenceNoiseAnimationConfig.DEFAULT_OPACITY,
+ /* width= */ mMediaViewHolder.getMultiRippleView().getWidth(),
+ /* height= */ mMediaViewHolder.getMultiRippleView().getHeight(),
+ TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_DURATION_IN_MILLIS,
+ this.getContext().getResources().getDisplayMetrics().density,
+ BlendMode.PLUS,
+ /* onAnimationEnd= */ null
+ );
+ }
private void clearButton(final ImageButton button) {
button.setImageDrawable(null);
button.setContentDescription(null);
@@ -1191,6 +1231,7 @@ public class MediaControlPanel {
setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation, itemIndex);
// Bubble up the long-click event to the card.
mediaCoverContainer.setOnLongClickListener(v -> {
+ if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true;
View parent = (View) v.getParent();
if (parent != null) {
parent.performLongClick();
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index a4a968067462..647beb95a3bc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -61,7 +61,7 @@ class MediaTttCommandLineHelper @Inject constructor(
@SuppressLint("WrongConstant") // sysui allowed to call STATUS_BAR_SERVICE
val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
as StatusBarManager
- val routeInfo = MediaRoute2Info.Builder("id", args[0])
+ val routeInfo = MediaRoute2Info.Builder(if (args.size >= 4) args[3] else "id", args[0])
.addFeature("feature")
val useAppIcon = !(args.size >= 3 && args[2] == "useAppIcon=false")
if (useAppIcon) {
@@ -107,7 +107,7 @@ class MediaTttCommandLineHelper @Inject constructor(
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND " +
- "<deviceName> <chipState> useAppIcon=[true|false]")
+ "<deviceName> <chipState> useAppIcon=[true|false] <id>")
}
}
@@ -127,8 +127,10 @@ class MediaTttCommandLineHelper @Inject constructor(
@SuppressLint("WrongConstant") // sysui is allowed to call STATUS_BAR_SERVICE
val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
as StatusBarManager
- val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
- .addFeature("feature")
+ val routeInfo = MediaRoute2Info.Builder(
+ if (args.size >= 3) args[2] else "id",
+ "Test Name"
+ ).addFeature("feature")
val useAppIcon = !(args.size >= 2 && args[1] == "useAppIcon=false")
if (useAppIcon) {
routeInfo.setClientPackageName(TEST_PACKAGE_NAME)
@@ -144,7 +146,7 @@ class MediaTttCommandLineHelper @Inject constructor(
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND " +
- "<chipState> useAppIcon=[true|false]")
+ "<chipState> useAppIcon=[true|false] <id>")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 8bddffc842f5..691953aaba36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -121,18 +121,32 @@ class MediaTttChipControllerReceiver @Inject constructor(
uiEventLogger.logReceiverStateChange(chipState)
if (chipState == ChipStateReceiver.FAR_FROM_SENDER) {
- removeView(removalReason = ChipStateReceiver.FAR_FROM_SENDER.name)
+ removeView(routeInfo.id, removalReason = ChipStateReceiver.FAR_FROM_SENDER.name)
return
}
if (appIcon == null) {
- displayView(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
+ displayView(
+ ChipReceiverInfo(
+ routeInfo,
+ appIconDrawableOverride = null,
+ appName,
+ id = routeInfo.id,
+ )
+ )
return
}
appIcon.loadDrawableAsync(
context,
Icon.OnDrawableLoadedListener { drawable ->
- displayView(ChipReceiverInfo(routeInfo, drawable, appName))
+ displayView(
+ ChipReceiverInfo(
+ routeInfo,
+ drawable,
+ appName,
+ id = routeInfo.id,
+ )
+ )
},
// Notify the listener on the main handler since the listener will update
// the UI.
@@ -234,4 +248,5 @@ data class ChipReceiverInfo(
val appNameOverride: CharSequence?,
override val windowTitle: String = MediaTttUtils.WINDOW_TITLE_RECEIVER,
override val wakeReason: String = MediaTttUtils.WAKE_REASON_RECEIVER,
+ override val id: String,
) : TemporaryViewInfo()
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index e354a03f1725..1ea202582f83 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -18,8 +18,8 @@ package com.android.systemui.media.taptotransfer.receiver
import android.content.Context
import android.util.AttributeSet
-import com.android.systemui.ripple.RippleShader
-import com.android.systemui.ripple.RippleView
+import com.android.systemui.surfaceeffects.ripple.RippleShader
+import com.android.systemui.surfaceeffects.ripple.RippleView
/**
* An expanding ripple effect for the media tap-to-transfer receiver chip.
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index d1ea2d0c83bd..bb7bc6fff99f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -108,7 +108,7 @@ constructor(
}
displayedState = null
- chipbarCoordinator.removeView(removalReason)
+ chipbarCoordinator.removeView(routeInfo.id, removalReason)
} else {
displayedState = chipState
chipbarCoordinator.displayView(
@@ -162,6 +162,7 @@ constructor(
windowTitle = MediaTttUtils.WINDOW_TITLE_SENDER,
wakeReason = MediaTttUtils.WAKE_REASON_SENDER,
timeoutMs = chipStateSender.timeout,
+ id = routeInfo.id,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 7fd100fd1398..6c41caab38a8 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -19,6 +19,7 @@ package com.android.systemui.mediaprojection.appselector
import android.app.Activity
import android.content.ComponentName
import android.content.Context
+import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.MediaProjectionAppSelectorActivity
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
@@ -92,6 +93,11 @@ interface MediaProjectionAppSelectorModule {
): ConfigurationController = ConfigurationControllerImpl(activity)
@Provides
+ fun bindIconFactory(
+ context: Context
+ ): IconFactory = IconFactory.obtain(context)
+
+ @Provides
@MediaProjectionAppSelector
@MediaProjectionAppSelectorScope
fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
index 0927f3b00724..b85d6285c35b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
@@ -19,13 +19,14 @@ package com.android.systemui.mediaprojection.appselector.data
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
-import android.content.pm.PackageManager.ComponentInfoFlags
import android.graphics.drawable.Drawable
import android.os.UserHandle
import com.android.launcher3.icons.BaseIconFactory.IconOptions
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.system.PackageManagerWrapper
import javax.inject.Inject
+import javax.inject.Provider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@@ -38,14 +39,18 @@ class IconLoaderLibAppIconLoader
constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val context: Context,
- private val packageManager: PackageManager
+ // Use wrapper to access hidden API that allows to get ActivityInfo for any user id
+ private val packageManagerWrapper: PackageManagerWrapper,
+ private val packageManager: PackageManager,
+ private val iconFactoryProvider: Provider<IconFactory>
) : AppIconLoader {
override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? =
withContext(backgroundDispatcher) {
- IconFactory.obtain(context).use<IconFactory, Drawable?> { iconFactory ->
- val activityInfo = packageManager
- .getActivityInfo(component, ComponentInfoFlags.of(0))
+ iconFactoryProvider.get().use<IconFactory, Drawable?> { iconFactory ->
+ val activityInfo =
+ packageManagerWrapper.getActivityInfo(component, userId)
+ ?: return@withContext null
val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null
val userHandler = UserHandle.of(userId)
val options = IconOptions().apply { setUser(userHandler) }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index b682bd172837..d4991f90a86b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -148,6 +148,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
val currentRotation: Int = display.rotation
val displayWidthPx = windowMetrics.bounds.width()
+ val displayHeightPx = windowMetrics.bounds.height()
val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
val isTablet = isTablet(context)
val taskbarSize =
@@ -163,6 +164,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
measuredWidth,
measuredHeight,
displayWidthPx,
+ displayHeightPx,
taskbarSize,
isTablet,
currentRotation,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index dc79f40ffef6..6f645b562008 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -26,6 +26,7 @@ import java.util.concurrent.Executor
import javax.inject.Inject
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
interface ChipVisibilityListener {
fun onChipVisibilityRefreshed(visible: Boolean)
@@ -54,7 +55,8 @@ class HeaderPrivacyIconsController @Inject constructor(
private val activityStarter: ActivityStarter,
private val appOpsController: AppOpsController,
private val broadcastDispatcher: BroadcastDispatcher,
- private val safetyCenterManager: SafetyCenterManager
+ private val safetyCenterManager: SafetyCenterManager,
+ private val deviceProvisionedController: DeviceProvisionedController
) {
var chipVisibilityListener: ChipVisibilityListener? = null
@@ -134,6 +136,8 @@ class HeaderPrivacyIconsController @Inject constructor(
fun onParentVisible() {
privacyChip.setOnClickListener {
+ // Do not expand dialog while device is not provisioned
+ if (!deviceProvisionedController.isDeviceProvisioned) return@setOnClickListener
// If the privacy chip is visible, it means there were some indicators
uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
if (safetyCenterEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 0697133a02f9..f92bbf75d027 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -364,13 +364,18 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private void distributeTiles() {
emptyAndInflateOrRemovePages();
- final int tileCount = mPages.get(0).maxTiles();
- if (DEBUG) Log.d(TAG, "Distributing tiles");
+ final int tilesPerPageCount = mPages.get(0).maxTiles();
int index = 0;
- final int NT = mTiles.size();
- for (int i = 0; i < NT; i++) {
+ final int totalTilesCount = mTiles.size();
+ if (DEBUG) {
+ Log.d(TAG, "Distributing tiles: "
+ + "[tilesPerPageCount=" + tilesPerPageCount + "]"
+ + "[totalTilesCount=" + totalTilesCount + "]"
+ );
+ }
+ for (int i = 0; i < totalTilesCount; i++) {
TileRecord tile = mTiles.get(i);
- if (mPages.get(index).mRecords.size() == tileCount) index++;
+ if (mPages.get(index).mRecords.size() == tilesPerPageCount) index++;
if (DEBUG) {
Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
+ index);
@@ -577,8 +582,8 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
});
setOffscreenPageLimit(lastPageNumber); // Ensure the page to reveal has been inflated.
int dx = getWidth() * lastPageNumber;
- mScroller.startScroll(getScrollX(), getScrollY(), isLayoutRtl() ? -dx : dx, 0,
- REVEAL_SCROLL_DURATION_MILLIS);
+ mScroller.startScroll(getScrollX(), getScrollY(), isLayoutRtl() ? -dx : dx, 0,
+ REVEAL_SCROLL_DURATION_MILLIS);
postInvalidateOnAnimation();
}
@@ -738,6 +743,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
public interface PageListener {
int INVALID_PAGE = -1;
+
void onPageChanged(boolean isFirst, int pageNumber);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 20f1a8ed7689..1422a25bfe12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -515,7 +515,13 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
public void setExpanded(boolean expanded) {
if (DEBUG) Log.d(TAG, "setExpanded " + expanded);
mQsExpanded = expanded;
- updateQsPanelControllerListening();
+ if (mInSplitShade && mQsExpanded) {
+ // in split shade QS is expanded immediately when shade expansion starts and then we
+ // also need to listen to changes - otherwise QS is updated only once its fully expanded
+ setListening(true);
+ } else {
+ updateQsPanelControllerListening();
+ }
updateQsState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index abc0adecbfeb..64962b495c96 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -237,7 +237,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
* @return if bouncer is in transit
*/
public boolean isBouncerInTransit() {
- return mStatusBarKeyguardViewManager.isBouncerInTransit();
+ return mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 2a80de0e24de..dd88c83949fb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -25,6 +25,7 @@ import android.content.ComponentName;
import android.content.res.Configuration;
import android.content.res.Configuration.Orientation;
import android.metrics.LogMaker;
+import android.util.Log;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
@@ -38,6 +39,7 @@ import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileViewImpl;
import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.animation.DisappearParameters;
@@ -237,6 +239,16 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
private void addTile(final QSTile tile, boolean collapsedView) {
final TileRecord r =
new TileRecord(tile, mHost.createTileView(getContext(), tile, collapsedView));
+ // TODO(b/250618218): Remove the QSLogger in QSTileViewImpl once we know the root cause of
+ // b/250618218.
+ try {
+ QSTileViewImpl qsTileView = (QSTileViewImpl) (r.tileView);
+ if (qsTileView != null) {
+ qsTileView.setQsLogger(mQSLogger);
+ }
+ } catch (ClassCastException e) {
+ Log.e(TAG, "Failed to cast QSTileView to QSTileViewImpl", e);
+ }
mView.addTile(r);
mRecords.add(r);
mCachedSpecs = getTilesSpecs();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 3d00dd49cb1c..7ee404756633 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -123,7 +123,6 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
public boolean updateResources() {
final Resources res = mContext.getResources();
mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
- updateColumns();
mMaxCellHeight = mContext.getResources().getDimensionPixelSize(mCellHeightResId);
mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
mSidePadding = useSidePadding() ? mCellMarginHorizontal / 2 : 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index cf10c7940871..79fcc7d81372 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -82,12 +82,12 @@ public class QSCustomizer extends LinearLayout {
DefaultItemAnimator animator = new DefaultItemAnimator();
animator.setMoveDuration(TileAdapter.MOVE_DURATION);
mRecyclerView.setItemAnimator(animator);
+
+ updateTransparentViewHeight();
}
void updateResources() {
- LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams();
- lp.height = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
- mTransparentView.setLayoutParams(lp);
+ updateTransparentViewHeight();
mRecyclerView.getAdapter().notifyItemChanged(0);
}
@@ -236,4 +236,10 @@ public class QSCustomizer extends LinearLayout {
public boolean isOpening() {
return mOpening;
}
+
+ private void updateTransparentViewHeight() {
+ LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams();
+ lp.height = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
+ mTransparentView.setLayoutParams(lp);
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 931dc8df151a..9f6317fd931b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -129,12 +129,36 @@ class QSLogger @Inject constructor(
})
}
- fun logInternetTileUpdate(lastType: Int, callback: String) {
+ fun logInternetTileUpdate(tileSpec: String, lastType: Int, callback: String) {
log(VERBOSE, {
+ str1 = tileSpec
int1 = lastType
- str1 = callback
+ str2 = callback
+ }, {
+ "[$str1] mLastTileState=$int1, Callback=$str2."
+ })
+ }
+
+ // TODO(b/250618218): Remove this method once we know the root cause of b/250618218.
+ fun logTileBackgroundColorUpdateIfInternetTile(
+ tileSpec: String,
+ state: Int,
+ disabledByPolicy: Boolean,
+ color: Int
+ ) {
+ // This method is added to further debug b/250618218 which has only been observed from the
+ // InternetTile, so we are only logging the background color change for the InternetTile
+ // to avoid spamming the QSLogger.
+ if (tileSpec != "internet") {
+ return
+ }
+ log(VERBOSE, {
+ str1 = tileSpec
+ int1 = state
+ bool1 = disabledByPolicy
+ int2 = color
}, {
- "mLastTileState=$int1, Callback=$str1."
+ "[$str1] state=$int1, disabledByPolicy=$bool1, color=$int2."
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 1f92b12c1a83..cd69f4edff63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -140,16 +140,21 @@ public class QSIconViewImpl extends QSIconView {
iv.setTag(R.id.qs_icon_tag, icon);
iv.setTag(R.id.qs_slash_tag, state.slash);
iv.setPadding(0, padding, 0, padding);
- if (shouldAnimate && d instanceof Animatable2) {
+ if (d instanceof Animatable2) {
Animatable2 a = (Animatable2) d;
a.start();
- if (state.isTransient) {
- a.registerAnimationCallback(new AnimationCallback() {
- @Override
- public void onAnimationEnd(Drawable drawable) {
- a.start();
- }
- });
+ if (shouldAnimate) {
+ if (state.isTransient) {
+ a.registerAnimationCallback(new AnimationCallback() {
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ a.start();
+ }
+ });
+ }
+ } else {
+ // Sends animator to end of animation. Needs to be called after calling start.
+ a.stop();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 972b24343d10..b355d4bb67fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -50,6 +50,7 @@ import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTile.BooleanState
import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
import java.util.Objects
@@ -116,7 +117,7 @@ open class QSTileViewImpl @JvmOverloads constructor(
protected lateinit var sideView: ViewGroup
private lateinit var customDrawableView: ImageView
private lateinit var chevronView: ImageView
-
+ private var mQsLogger: QSLogger? = null
protected var showRippleEffect = true
private lateinit var ripple: RippleDrawable
@@ -188,6 +189,10 @@ open class QSTileViewImpl @JvmOverloads constructor(
updateHeight()
}
+ fun setQsLogger(qsLogger: QSLogger) {
+ mQsLogger = qsLogger
+ }
+
fun updateResources() {
FontSizeUtils.updateFontSize(label, R.dimen.qs_tile_text_size)
FontSizeUtils.updateFontSize(secondaryLabel, R.dimen.qs_tile_text_size)
@@ -493,6 +498,11 @@ open class QSTileViewImpl @JvmOverloads constructor(
// Colors
if (state.state != lastState || state.disabledByPolicy || lastDisabledByPolicy) {
singleAnimator.cancel()
+ mQsLogger?.logTileBackgroundColorUpdateIfInternetTile(
+ state.spec,
+ state.state,
+ state.disabledByPolicy,
+ getBackgroundColorForState(state.state, state.disabledByPolicy))
if (allowAnimations) {
singleAnimator.setValues(
colorValuesHolder(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index d30402466d8d..5670b6de3c36 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -383,7 +383,8 @@ public class InternetTile extends QSTileImpl<SignalState> {
@Override
protected void handleUpdateState(SignalState state, Object arg) {
- mQSLogger.logInternetTileUpdate(mLastTileState, arg == null ? "null" : arg.toString());
+ mQSLogger.logInternetTileUpdate(
+ getTileSpec(), mLastTileState, arg == null ? "null" : arg.toString());
if (arg instanceof CellularCallbackInfo) {
mLastTileState = 0;
handleUpdateCellularState(state, arg);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index b41502213555..376d3d8da8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -115,7 +115,7 @@ public class QRCodeScannerTile extends QSTileImpl<QSTile.State> {
state.label = mContext.getString(R.string.qr_code_scanner_title);
state.contentDescription = state.label;
state.icon = ResourceIcon.get(R.drawable.ic_qr_code_scanner);
- state.state = mQRCodeScannerController.isEnabledForQuickSettings() ? Tile.STATE_ACTIVE
+ state.state = mQRCodeScannerController.isEnabledForQuickSettings() ? Tile.STATE_INACTIVE
: Tile.STATE_UNAVAILABLE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index f63f0444210b..64a8a146031b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles;
+import android.app.Dialog;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
@@ -43,7 +44,6 @@ import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
-import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -170,9 +170,9 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
getHost().collapsePanels();
};
- ScreenRecordDialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
- mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked);
+ Dialog dialog = mController.createScreenRecordDialog(mContext, mFlags,
+ mDialogLaunchAnimator, mActivityStarter, onStartRecordingClicked);
ActivityStarter.OnDismissAction dismissAction = () -> {
if (shouldAnimateFromView) {
mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 0dfb2f4063a9..ee3b13091d00 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -32,12 +32,14 @@ import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.text.Html;
+import android.text.Layout;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewStub;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
@@ -61,6 +63,7 @@ import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -71,6 +74,8 @@ import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
import java.util.concurrent.Executor;
+import javax.inject.Inject;
+
/**
* Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks.
*/
@@ -85,6 +90,7 @@ public class InternetDialog extends SystemUIDialog implements
private final Handler mHandler;
private final Executor mBackgroundExecutor;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
@VisibleForTesting
protected InternetAdapter mAdapter;
@@ -108,6 +114,7 @@ public class InternetDialog extends SystemUIDialog implements
private LinearLayout mInternetDialogLayout;
private LinearLayout mConnectedWifListLayout;
private LinearLayout mMobileNetworkLayout;
+ private LinearLayout mSecondaryMobileNetworkLayout;
private LinearLayout mTurnWifiOnLayout;
private LinearLayout mEthernetLayout;
private TextView mWifiToggleTitleText;
@@ -122,6 +129,8 @@ public class InternetDialog extends SystemUIDialog implements
private ImageView mSignalIcon;
private TextView mMobileTitleText;
private TextView mMobileSummaryText;
+ private TextView mSecondaryMobileTitleText;
+ private TextView mSecondaryMobileSummaryText;
private TextView mAirplaneModeSummaryText;
private Switch mMobileDataToggle;
private View mMobileToggleDivider;
@@ -157,9 +166,11 @@ public class InternetDialog extends SystemUIDialog implements
mInternetDialogSubTitle.setText(getSubtitleText());
};
+ @Inject
public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
InternetDialogController internetDialogController, boolean canConfigMobileData,
boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger,
+ DialogLaunchAnimator dialogLaunchAnimator,
@Main Handler handler, @Background Executor executor,
KeyguardStateController keyguardStateController) {
super(context);
@@ -182,6 +193,7 @@ public class InternetDialog extends SystemUIDialog implements
mKeyguard = keyguardStateController;
mUiEventLogger = uiEventLogger;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
mAdapter = new InternetAdapter(mInternetDialogController);
if (!aboveStatusBar) {
getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
@@ -237,15 +249,7 @@ public class InternetDialog extends SystemUIDialog implements
mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
-
- TypedArray typedArray = mContext.obtainStyledAttributes(
- new int[]{android.R.attr.selectableItemBackground});
- try {
- mBackgroundOff = typedArray.getDrawable(0 /* index */);
- } finally {
- typedArray.recycle();
- }
-
+ mBackgroundOff = mContext.getDrawable(R.drawable.internet_dialog_selected_effect);
setOnClickListener();
mTurnWifiOnLayout.setBackground(null);
mAirplaneModeButton.setVisibility(
@@ -286,6 +290,9 @@ public class InternetDialog extends SystemUIDialog implements
mMobileNetworkLayout.setOnClickListener(null);
mMobileDataToggle.setOnCheckedChangeListener(null);
mConnectedWifListLayout.setOnClickListener(null);
+ if (mSecondaryMobileNetworkLayout != null) {
+ mSecondaryMobileNetworkLayout.setOnClickListener(null);
+ }
mSeeAllLayout.setOnClickListener(null);
mWiFiToggle.setOnCheckedChangeListener(null);
mDoneButton.setOnClickListener(null);
@@ -340,6 +347,10 @@ public class InternetDialog extends SystemUIDialog implements
private void setOnClickListener() {
mMobileNetworkLayout.setOnClickListener(v -> {
+ int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+ if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ showTurnOffAutoDataSwitchDialog(autoSwitchNonDdsSubId);
+ }
if (mInternetDialogController.isMobileDataEnabled()
&& !mInternetDialogController.isDeviceLocked()) {
if (!mInternetDialogController.activeNetworkIsCellular()) {
@@ -389,41 +400,28 @@ public class InternetDialog extends SystemUIDialog implements
if (!mInternetDialogController.hasActiveSubId()
&& (!isWifiEnabled || !isCarrierNetworkActive)) {
mMobileNetworkLayout.setVisibility(View.GONE);
+ if (mSecondaryMobileNetworkLayout != null) {
+ mSecondaryMobileNetworkLayout.setVisibility(View.GONE);
+ }
} else {
mMobileNetworkLayout.setVisibility(View.VISIBLE);
mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
- mMobileTitleText.setText(getMobileNetworkTitle());
- String summary = getMobileNetworkSummary();
+ mMobileTitleText.setText(getMobileNetworkTitle(mDefaultDataSubId));
+ String summary = getMobileNetworkSummary(mDefaultDataSubId);
if (!TextUtils.isEmpty(summary)) {
mMobileSummaryText.setText(
Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY));
+ mMobileSummaryText.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
mMobileSummaryText.setVisibility(View.VISIBLE);
} else {
mMobileSummaryText.setVisibility(View.GONE);
}
mBackgroundExecutor.execute(() -> {
- Drawable drawable = getSignalStrengthDrawable();
+ Drawable drawable = getSignalStrengthDrawable(mDefaultDataSubId);
mHandler.post(() -> {
mSignalIcon.setImageDrawable(drawable);
});
});
- mMobileTitleText.setTextAppearance(isNetworkConnected
- ? R.style.TextAppearance_InternetDialog_Active
- : R.style.TextAppearance_InternetDialog);
- int secondaryRes = isNetworkConnected
- ? R.style.TextAppearance_InternetDialog_Secondary_Active
- : R.style.TextAppearance_InternetDialog_Secondary;
- mMobileSummaryText.setTextAppearance(secondaryRes);
- // Set airplane mode to the summary for carrier network
- if (mInternetDialogController.isAirplaneModeEnabled()) {
- mAirplaneModeSummaryText.setVisibility(View.VISIBLE);
- mAirplaneModeSummaryText.setText(mContext.getText(R.string.airplane_mode));
- mAirplaneModeSummaryText.setTextAppearance(secondaryRes);
- } else {
- mAirplaneModeSummaryText.setVisibility(View.GONE);
- }
- mMobileNetworkLayout.setBackground(
- isNetworkConnected ? mBackgroundOn : mBackgroundOff);
TypedArray array = mContext.obtainStyledAttributes(
R.style.InternetDialog_Divider_Active, new int[]{android.R.attr.background});
@@ -436,6 +434,86 @@ public class InternetDialog extends SystemUIDialog implements
mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
mMobileToggleDivider.setVisibility(
mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
+
+ // Display the info for the non-DDS if it's actively being used
+ int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+ int nonDdsVisibility = autoSwitchNonDdsSubId
+ != SubscriptionManager.INVALID_SUBSCRIPTION_ID ? View.VISIBLE : View.GONE;
+
+ int secondaryRes = isNetworkConnected
+ ? R.style.TextAppearance_InternetDialog_Secondary_Active
+ : R.style.TextAppearance_InternetDialog_Secondary;
+ if (nonDdsVisibility == View.VISIBLE) {
+ // non DDS is the currently active sub, set primary visual for it
+ ViewStub stub = mDialogView.findViewById(R.id.secondary_mobile_network_stub);
+ if (stub != null) {
+ stub.inflate();
+ }
+ mSecondaryMobileNetworkLayout = findViewById(R.id.secondary_mobile_network_layout);
+ mSecondaryMobileNetworkLayout.setOnClickListener(
+ this::onClickConnectedSecondarySub);
+ mSecondaryMobileNetworkLayout.setBackground(mBackgroundOn);
+
+ mSecondaryMobileTitleText = mDialogView.requireViewById(
+ R.id.secondary_mobile_title);
+ mSecondaryMobileTitleText.setText(getMobileNetworkTitle(autoSwitchNonDdsSubId));
+ mSecondaryMobileTitleText.setTextAppearance(
+ R.style.TextAppearance_InternetDialog_Active);
+
+ mSecondaryMobileSummaryText =
+ mDialogView.requireViewById(R.id.secondary_mobile_summary);
+ summary = getMobileNetworkSummary(autoSwitchNonDdsSubId);
+ if (!TextUtils.isEmpty(summary)) {
+ mSecondaryMobileSummaryText.setText(
+ Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY));
+ mSecondaryMobileSummaryText.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
+ mSecondaryMobileSummaryText.setTextAppearance(
+ R.style.TextAppearance_InternetDialog_Active);
+ }
+
+ ImageView mSecondarySignalIcon =
+ mDialogView.requireViewById(R.id.secondary_signal_icon);
+ mBackgroundExecutor.execute(() -> {
+ Drawable drawable = getSignalStrengthDrawable(autoSwitchNonDdsSubId);
+ mHandler.post(() -> {
+ mSecondarySignalIcon.setImageDrawable(drawable);
+ });
+ });
+
+ ImageView mSecondaryMobileSettingsIcon =
+ mDialogView.requireViewById(R.id.secondary_settings_icon);
+ mSecondaryMobileSettingsIcon.setColorFilter(
+ mContext.getColor(R.color.connected_network_primary_color));
+
+ // set secondary visual for default data sub
+ mMobileNetworkLayout.setBackground(mBackgroundOff);
+ mMobileTitleText.setTextAppearance(R.style.TextAppearance_InternetDialog);
+ mMobileSummaryText.setTextAppearance(
+ R.style.TextAppearance_InternetDialog_Secondary);
+ mSignalIcon.setColorFilter(
+ mContext.getColor(R.color.connected_network_secondary_color));
+ } else {
+ mMobileNetworkLayout.setBackground(
+ isNetworkConnected ? mBackgroundOn : mBackgroundOff);
+ mMobileTitleText.setTextAppearance(isNetworkConnected
+ ?
+ R.style.TextAppearance_InternetDialog_Active
+ : R.style.TextAppearance_InternetDialog);
+ mMobileSummaryText.setTextAppearance(secondaryRes);
+ }
+
+ if (mSecondaryMobileNetworkLayout != null) {
+ mSecondaryMobileNetworkLayout.setVisibility(nonDdsVisibility);
+ }
+
+ // Set airplane mode to the summary for carrier network
+ if (mInternetDialogController.isAirplaneModeEnabled()) {
+ mAirplaneModeSummaryText.setVisibility(View.VISIBLE);
+ mAirplaneModeSummaryText.setText(mContext.getText(R.string.airplane_mode));
+ mAirplaneModeSummaryText.setTextAppearance(secondaryRes);
+ } else {
+ mAirplaneModeSummaryText.setVisibility(View.GONE);
+ }
}
}
@@ -474,6 +552,10 @@ public class InternetDialog extends SystemUIDialog implements
mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry));
mWifiSettingsIcon.setColorFilter(
mContext.getColor(R.color.connected_network_primary_color));
+
+ if (mSecondaryMobileNetworkLayout != null) {
+ mSecondaryMobileNetworkLayout.setVisibility(View.GONE);
+ }
}
@MainThread
@@ -544,6 +626,11 @@ public class InternetDialog extends SystemUIDialog implements
mInternetDialogController.launchWifiDetailsSetting(mConnectedWifiEntry.getKey(), view);
}
+ /** For DSDS auto data switch **/
+ void onClickConnectedSecondarySub(View view) {
+ mInternetDialogController.launchMobileNetworkSettings(view);
+ }
+
void onClickSeeMoreButton(View view) {
mInternetDialogController.launchNetworkSetting(view);
}
@@ -558,16 +645,16 @@ public class InternetDialog extends SystemUIDialog implements
mIsProgressBarVisible && !mIsSearchingHidden);
}
- private Drawable getSignalStrengthDrawable() {
- return mInternetDialogController.getSignalStrengthDrawable();
+ private Drawable getSignalStrengthDrawable(int subId) {
+ return mInternetDialogController.getSignalStrengthDrawable(subId);
}
- CharSequence getMobileNetworkTitle() {
- return mInternetDialogController.getMobileNetworkTitle();
+ CharSequence getMobileNetworkTitle(int subId) {
+ return mInternetDialogController.getMobileNetworkTitle(subId);
}
- String getMobileNetworkSummary() {
- return mInternetDialogController.getMobileNetworkSummary();
+ String getMobileNetworkSummary(int subId) {
+ return mInternetDialogController.getMobileNetworkSummary(subId);
}
protected void showProgressBar() {
@@ -605,8 +692,8 @@ public class InternetDialog extends SystemUIDialog implements
}
private void showTurnOffMobileDialog() {
- CharSequence carrierName = getMobileNetworkTitle();
- boolean isInService = mInternetDialogController.isVoiceStateInService();
+ CharSequence carrierName = getMobileNetworkTitle(mDefaultDataSubId);
+ boolean isInService = mInternetDialogController.isVoiceStateInService(mDefaultDataSubId);
if (TextUtils.isEmpty(carrierName) || !isInService) {
carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
}
@@ -630,7 +717,33 @@ public class InternetDialog extends SystemUIDialog implements
SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
SystemUIDialog.registerDismissListener(mAlertDialog);
SystemUIDialog.setWindowOnTop(mAlertDialog, mKeyguard.isShowing());
- mAlertDialog.show();
+ mDialogLaunchAnimator.showFromDialog(mAlertDialog, this, null, false);
+ }
+
+ private void showTurnOffAutoDataSwitchDialog(int subId) {
+ CharSequence carrierName = getMobileNetworkTitle(mDefaultDataSubId);
+ if (TextUtils.isEmpty(carrierName)) {
+ carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
+ }
+ mAlertDialog = new Builder(mContext)
+ .setTitle(mContext.getString(R.string.auto_data_switch_disable_title, carrierName))
+ .setMessage(R.string.auto_data_switch_disable_message)
+ .setNegativeButton(R.string.auto_data_switch_dialog_negative_button,
+ (d, w) -> {})
+ .setPositiveButton(R.string.auto_data_switch_dialog_positive_button,
+ (d, w) -> {
+ mInternetDialogController
+ .setAutoDataSwitchMobileDataPolicy(subId, false);
+ if (mSecondaryMobileNetworkLayout != null) {
+ mSecondaryMobileNetworkLayout.setVisibility(View.GONE);
+ }
+ })
+ .create();
+ mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
+ SystemUIDialog.registerDismissListener(mAlertDialog);
+ SystemUIDialog.setWindowOnTop(mAlertDialog, mKeyguard.isShowing());
+ mDialogLaunchAnimator.showFromDialog(mAlertDialog, this, null, false);
}
@Override
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 9c0a087c01b8..4c7f10e2bc6b 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
@@ -37,6 +37,7 @@ import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.wifi.WifiManager;
+import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
@@ -78,6 +79,8 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -90,6 +93,7 @@ import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -113,6 +117,17 @@ public class InternetDialogController implements AccessPointController.AccessPoi
"android.settings.NETWORK_PROVIDER_SETTINGS";
private static final String ACTION_WIFI_SCANNING_SETTINGS =
"android.settings.WIFI_SCANNING_SETTINGS";
+ /**
+ * Fragment "key" argument passed thru {@link #SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS}
+ */
+ private static final String SETTINGS_EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+ /**
+ * When starting this activity, this extra can also be specified to supply a Bundle of arguments
+ * to pass to that fragment when it is instantiated during the initial creation of the activity.
+ */
+ private static final String SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS =
+ ":settings:show_fragment_args";
+ private static final String AUTO_DATA_SWITCH_SETTING_R_ID = "auto_data_switch";
public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
public static final int NO_CELL_DATA_TYPE_ICON = 0;
private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off;
@@ -130,9 +145,12 @@ public class InternetDialogController implements AccessPointController.AccessPoi
static final int MAX_WIFI_ENTRY_COUNT = 3;
+ private final FeatureFlags mFeatureFlags;
+
private WifiManager mWifiManager;
private Context mContext;
private SubscriptionManager mSubscriptionManager;
+ private Map<Integer, TelephonyManager> mSubIdTelephonyManagerMap = new HashMap<>();
private TelephonyManager mTelephonyManager;
private ConnectivityManager mConnectivityManager;
private CarrierConfigTracker mCarrierConfigTracker;
@@ -155,6 +173,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
private WindowManager mWindowManager;
private ToastFactory mToastFactory;
private SignalDrawable mSignalDrawable;
+ private SignalDrawable mSecondarySignalDrawable; // For the secondary mobile data sub in DSDS
private LocationController mLocationController;
private DialogLaunchAnimator mDialogLaunchAnimator;
private boolean mHasWifiEntries;
@@ -213,7 +232,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi
CarrierConfigTracker carrierConfigTracker,
LocationController locationController,
DialogLaunchAnimator dialogLaunchAnimator,
- WifiStateWorker wifiStateWorker
+ WifiStateWorker wifiStateWorker,
+ FeatureFlags featureFlags
) {
if (DEBUG) {
Log.d(TAG, "Init InternetDialogController");
@@ -242,10 +262,12 @@ public class InternetDialogController implements AccessPointController.AccessPoi
mWindowManager = windowManager;
mToastFactory = toastFactory;
mSignalDrawable = new SignalDrawable(mContext);
+ mSecondarySignalDrawable = new SignalDrawable(mContext);
mLocationController = locationController;
mDialogLaunchAnimator = dialogLaunchAnimator;
mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor();
mWifiStateWorker = wifiStateWorker;
+ mFeatureFlags = featureFlags;
}
void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
@@ -267,6 +289,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
}
mConfig = MobileMappings.Config.readConfig(mContext);
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+ mSubIdTelephonyManagerMap.put(mDefaultDataSubId, mTelephonyManager);
mInternetTelephonyCallback = new InternetTelephonyCallback();
mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
// Listen the connectivity changes
@@ -280,7 +303,9 @@ public class InternetDialogController implements AccessPointController.AccessPoi
Log.d(TAG, "onStop");
}
mBroadcastDispatcher.unregisterReceiver(mConnectionStateReceiver);
- mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
+ for (TelephonyManager tm : mSubIdTelephonyManagerMap.values()) {
+ tm.unregisterTelephonyCallback(mInternetTelephonyCallback);
+ }
mSubscriptionManager.removeOnSubscriptionsChangedListener(
mOnSubscriptionsChangedListener);
mAccessPointController.removeAccessPointCallback(this);
@@ -371,7 +396,10 @@ public class InternetDialogController implements AccessPointController.AccessPoi
if (DEBUG) {
Log.d(TAG, "No Wi-Fi item.");
}
- if (!hasActiveSubId() || (!isVoiceStateInService() && !isDataStateInService())) {
+ boolean isActiveOnNonDds = getActiveAutoSwitchNonDdsSubId() != SubscriptionManager
+ .INVALID_SUBSCRIPTION_ID;
+ if (!hasActiveSubId() || (!isVoiceStateInService(mDefaultDataSubId)
+ && !isDataStateInService(mDefaultDataSubId) && !isActiveOnNonDds)) {
if (DEBUG) {
Log.d(TAG, "No carrier or service is out of service.");
}
@@ -412,7 +440,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return drawable;
}
- Drawable getSignalStrengthDrawable() {
+ Drawable getSignalStrengthDrawable(int subId) {
Drawable drawable = mContext.getDrawable(
R.drawable.ic_signal_strength_zero_bar_no_internet);
try {
@@ -424,9 +452,10 @@ public class InternetDialogController implements AccessPointController.AccessPoi
}
boolean isCarrierNetworkActive = isCarrierNetworkActive();
- if (isDataStateInService() || isVoiceStateInService() || isCarrierNetworkActive) {
+ if (isDataStateInService(subId) || isVoiceStateInService(subId)
+ || isCarrierNetworkActive) {
AtomicReference<Drawable> shared = new AtomicReference<>();
- shared.set(getSignalStrengthDrawableWithLevel(isCarrierNetworkActive));
+ shared.set(getSignalStrengthDrawableWithLevel(isCarrierNetworkActive, subId));
drawable = shared.get();
}
@@ -447,24 +476,30 @@ public class InternetDialogController implements AccessPointController.AccessPoi
*
* @return The Drawable which is a signal bar icon with level.
*/
- Drawable getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive) {
- final SignalStrength strength = mTelephonyManager.getSignalStrength();
+ Drawable getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive, int subId) {
+ TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager);
+ final SignalStrength strength = tm.getSignalStrength();
int level = (strength == null) ? 0 : strength.getLevel();
int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
if (isCarrierNetworkActive) {
level = getCarrierNetworkLevel();
numLevels = WifiEntry.WIFI_LEVEL_MAX + 1;
- } else if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) {
+ } else if (mSubscriptionManager != null && shouldInflateSignalStrength(subId)) {
level += 1;
numLevels += 1;
}
- return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
+ return getSignalStrengthIcon(subId, mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
!isMobileDataEnabled());
}
- Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
+ Drawable getSignalStrengthIcon(int subId, Context context, int level, int numLevels,
int iconType, boolean cutOut) {
- mSignalDrawable.setLevel(SignalDrawable.getState(level, numLevels, cutOut));
+ boolean isForDds = subId == mDefaultDataSubId;
+ if (isForDds) {
+ mSignalDrawable.setLevel(SignalDrawable.getState(level, numLevels, cutOut));
+ } else {
+ mSecondarySignalDrawable.setLevel(SignalDrawable.getState(level, numLevels, cutOut));
+ }
// Make the network type drawable
final Drawable networkDrawable =
@@ -473,7 +508,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi
: context.getResources().getDrawable(iconType, context.getTheme());
// Overlay the two drawables
- final Drawable[] layers = {networkDrawable, mSignalDrawable};
+ final Drawable[] layers = {networkDrawable, isForDds
+ ? mSignalDrawable : mSecondarySignalDrawable};
final int iconSize =
context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
@@ -571,14 +607,39 @@ public class InternetDialogController implements AccessPointController.AccessPoi
info -> info.uniqueName));
}
- CharSequence getMobileNetworkTitle() {
- return getUniqueSubscriptionDisplayName(mDefaultDataSubId, mContext);
+ /**
+ * @return the subId of the visible non-DDS if it's actively being used for data, otherwise
+ * return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ */
+ int getActiveAutoSwitchNonDdsSubId() {
+ if (!mFeatureFlags.isEnabled(Flags.QS_SECONDARY_DATA_SUB_INFO)) {
+ // sets the non-DDS to be not found to hide its visual
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(
+ SubscriptionManager.getActiveDataSubscriptionId());
+ if (subInfo != null && subInfo.getSubscriptionId() != mDefaultDataSubId
+ && !subInfo.isOpportunistic()) {
+ int subId = subInfo.getSubscriptionId();
+ if (mSubIdTelephonyManagerMap.get(subId) == null) {
+ TelephonyManager secondaryTm = mTelephonyManager.createForSubscriptionId(subId);
+ secondaryTm.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
+ mSubIdTelephonyManagerMap.put(subId, secondaryTm);
+ }
+ return subId;
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ }
+
+ CharSequence getMobileNetworkTitle(int subId) {
+ return getUniqueSubscriptionDisplayName(subId, mContext);
}
- String getMobileNetworkSummary() {
+ String getMobileNetworkSummary(int subId) {
String description = getNetworkTypeDescription(mContext, mConfig,
- mTelephonyDisplayInfo, mDefaultDataSubId);
- return getMobileSummary(mContext, description);
+ mTelephonyDisplayInfo, subId);
+ return getMobileSummary(mContext, description, subId);
}
/**
@@ -606,22 +667,28 @@ public class InternetDialogController implements AccessPointController.AccessPoi
? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
}
- private String getMobileSummary(Context context, String networkTypeDescription) {
+ private String getMobileSummary(Context context, String networkTypeDescription, int subId) {
if (!isMobileDataEnabled()) {
return context.getString(R.string.mobile_data_off_summary);
}
String summary = networkTypeDescription;
+ boolean isForDds = subId == mDefaultDataSubId;
+ int activeSubId = getActiveAutoSwitchNonDdsSubId();
+ boolean isOnNonDds = activeSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID;
// Set network description for the carrier network when connecting to the carrier network
// under the airplane mode ON.
if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
summary = context.getString(R.string.preference_summary_default_combination,
- context.getString(R.string.mobile_data_connection_active),
+ context.getString(
+ isForDds // if nonDds is active, explains Dds status as poor connection
+ ? (isOnNonDds ? R.string.mobile_data_poor_connection
+ : R.string.mobile_data_connection_active)
+ : R.string.mobile_data_temp_connection_active),
networkTypeDescription);
- } else if (!isDataStateInService()) {
+ } else if (!isDataStateInService(subId)) {
summary = context.getString(R.string.mobile_data_no_connection);
}
-
return summary;
}
@@ -647,6 +714,26 @@ public class InternetDialogController implements AccessPointController.AccessPoi
}
}
+ void launchMobileNetworkSettings(View view) {
+ final int subId = getActiveAutoSwitchNonDdsSubId();
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ Log.w(TAG, "launchMobileNetworkSettings fail, invalid subId:" + subId);
+ return;
+ }
+ startActivity(getSubSettingIntent(subId), view);
+ }
+
+ Intent getSubSettingIntent(int subId) {
+ final Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+
+ final Bundle fragmentArgs = new Bundle();
+ // Special contract for Settings to highlight permission row
+ fragmentArgs.putString(SETTINGS_EXTRA_FRAGMENT_ARG_KEY, AUTO_DATA_SWITCH_SETTING_R_ID);
+ fragmentArgs.putInt(Settings.EXTRA_SUB_ID, subId);
+ intent.putExtra(SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs);
+ return intent;
+ }
+
void launchWifiScanningSetting(View view) {
final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -796,8 +883,20 @@ public class InternetDialogController implements AccessPointController.AccessPoi
mWorkerHandler.post(() -> setMergedCarrierWifiEnabledIfNeed(subId, enabled));
}
- boolean isDataStateInService() {
- final ServiceState serviceState = mTelephonyManager.getServiceState();
+ void setAutoDataSwitchMobileDataPolicy(int subId, boolean enable) {
+ TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager);
+ if (tm == null) {
+ if (DEBUG) {
+ Log.d(TAG, "TelephonyManager is null, can not set mobile data.");
+ }
+ return;
+ }
+ tm.setMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, enable);
+ }
+
+ boolean isDataStateInService(int subId) {
+ TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager);
+ final ServiceState serviceState = tm.getServiceState();
NetworkRegistrationInfo regInfo =
(serviceState == null) ? null : serviceState.getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS,
@@ -805,7 +904,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return (regInfo == null) ? false : regInfo.isRegistered();
}
- boolean isVoiceStateInService() {
+ boolean isVoiceStateInService(int subId) {
if (mTelephonyManager == null) {
if (DEBUG) {
Log.d(TAG, "TelephonyManager is null, can not detect voice state.");
@@ -813,7 +912,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return false;
}
- final ServiceState serviceState = mTelephonyManager.getServiceState();
+ TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager);
+ final ServiceState serviceState = tm.getServiceState();
return serviceState != null
&& serviceState.getState() == serviceState.STATE_IN_SERVICE;
}
@@ -1104,6 +1204,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
if (SubscriptionManager.isUsableSubscriptionId(mDefaultDataSubId)) {
mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+ mSubIdTelephonyManagerMap.put(mDefaultDataSubId, mTelephonyManager);
mTelephonyManager.registerTelephonyCallback(mHandler::post,
mInternetTelephonyCallback);
mCallback.onSubscriptionsChanged(mDefaultDataSubId);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
index 8566ca308738..796672dc0ead 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
@@ -66,7 +66,8 @@ class InternetDialogFactory @Inject constructor(
} else {
internetDialog = InternetDialog(
context, this, internetDialogController,
- canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler,
+ canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger,
+ dialogLaunchAnimator, handler,
executor, keyguardStateController
)
if (view != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
deleted file mode 100644
index 6de46483892b..000000000000
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.ripple
-
-/** A common utility functions that are used for computing [RippleShader]. */
-class RippleShaderUtilLibrary {
- //language=AGSL
- companion object {
- const val SHADER_LIB = """
- float triangleNoise(vec2 n) {
- n = fract(n * vec2(5.3987, 5.4421));
- n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
- float xy = n.x * n.y;
- return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
- }
- const float PI = 3.1415926535897932384626;
-
- float sparkles(vec2 uv, float t) {
- float n = triangleNoise(uv);
- float s = 0.0;
- for (float i = 0; i < 4; i += 1) {
- float l = i * 0.01;
- float h = l + 0.1;
- float o = smoothstep(n - l, h, n);
- o *= abs(sin(PI * o * (t + 0.55 * i)));
- s += o;
- }
- return s;
- }
-
- vec2 distort(vec2 p, float time, float distort_amount_radial,
- float distort_amount_xy) {
- float angle = atan(p.y, p.x);
- return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
- cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
- + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
- cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
- }"""
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
new file mode 100644
index 000000000000..f4d59a8dd6a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenrecord
+
+import android.content.Context
+import android.os.Bundle
+import android.view.Gravity
+import android.view.View
+import android.view.ViewStub
+import android.view.WindowManager
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Spinner
+import android.widget.TextView
+import androidx.annotation.LayoutRes
+import androidx.annotation.StringRes
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/** Base permission dialog for screen share and recording */
+open class BaseScreenSharePermissionDialog(
+ context: Context?,
+ private val screenShareOptions: List<ScreenShareOption>,
+ private val appName: String?
+) : SystemUIDialog(context), AdapterView.OnItemSelectedListener {
+ private lateinit var dialogTitle: TextView
+ private lateinit var startButton: TextView
+ private lateinit var warning: TextView
+ private lateinit var screenShareModeSpinner: Spinner
+ var selectedScreenShareOption: ScreenShareOption = screenShareOptions.first()
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ window.apply {
+ addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+ setGravity(Gravity.CENTER)
+ }
+ setContentView(R.layout.screen_share_dialog)
+ dialogTitle = findViewById(R.id.screen_share_dialog_title)
+ warning = findViewById(R.id.text_warning)
+ startButton = findViewById(R.id.button_start)
+ findViewById<TextView>(R.id.button_cancel).setOnClickListener { dismiss() }
+ initScreenShareOptions()
+ createOptionsView(getOptionsViewLayoutId())
+ }
+
+ protected fun initScreenShareOptions() {
+ selectedScreenShareOption = screenShareOptions.first()
+ warning.text = warningText
+ initScreenShareSpinner()
+ }
+
+ private val warningText: String
+ get() = context.getString(selectedScreenShareOption.warningText, appName)
+
+ private fun initScreenShareSpinner() {
+ val options = screenShareOptions.map { context.getString(it.spinnerText) }.toTypedArray()
+ val adapter =
+ ArrayAdapter(context.applicationContext, android.R.layout.simple_spinner_item, options)
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ screenShareModeSpinner = findViewById(R.id.screen_share_mode_spinner)
+ screenShareModeSpinner.adapter = adapter
+ screenShareModeSpinner.onItemSelectedListener = this
+ }
+
+ override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
+ selectedScreenShareOption = screenShareOptions[pos]
+ warning.text = warningText
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {}
+
+ /** Protected methods for the text updates & functionality */
+ protected fun setDialogTitle(@StringRes stringId: Int) {
+ val title = context.getString(stringId, appName)
+ dialogTitle.text = title
+ }
+
+ protected fun setStartButtonText(@StringRes stringId: Int) {
+ startButton.setText(stringId)
+ }
+
+ protected fun setStartButtonOnClickListener(listener: View.OnClickListener?) {
+ startButton.setOnClickListener(listener)
+ }
+
+ // Create additional options that is shown under the share mode spinner
+ // Eg. the audio and tap toggles in SysUI Recorder
+ @LayoutRes protected open fun getOptionsViewLayoutId(): Int? = null
+
+ private fun createOptionsView(@LayoutRes layoutId: Int?) {
+ if (layoutId == null) return
+ val stub = findViewById<View>(R.id.options_stub) as ViewStub
+ stub.layoutResource = layoutId
+ stub.inflate()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
new file mode 100644
index 000000000000..15b0bc4a356a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenrecord
+
+import android.content.Context
+import android.os.Bundle
+import com.android.systemui.R
+
+/** Dialog to select screen recording options */
+class MediaProjectionPermissionDialog(
+ context: Context?,
+ private val onStartRecordingClicked: Runnable,
+ appName: String?
+) : BaseScreenSharePermissionDialog(context, createOptionList(), appName) {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setDialogTitle(R.string.media_projection_permission_dialog_title)
+ setStartButtonText(R.string.media_projection_permission_dialog_continue)
+ setStartButtonOnClickListener {
+ // Note that it is important to run this callback before dismissing, so that the
+ // callback can disable the dialog exit animation if it wants to.
+ onStartRecordingClicked.run()
+ dismiss()
+ }
+ }
+
+ companion object {
+ private fun createOptionList(): List<ScreenShareOption> {
+ return listOf(
+ ScreenShareOption(
+ SINGLE_APP,
+ R.string.media_projection_permission_dialog_option_single_app,
+ R.string.media_projection_permission_dialog_warning_single_app
+ ),
+ ScreenShareOption(
+ ENTIRE_SCREEN,
+ R.string.media_projection_permission_dialog_option_entire_screen,
+ R.string.media_projection_permission_dialog_warning_entire_screen
+ )
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 1083f22164d9..ce4e0ecee914 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -16,6 +16,7 @@
package com.android.systemui.screenrecord;
+import android.app.Dialog;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -33,6 +34,7 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -97,11 +99,15 @@ public class RecordingController
}
/** Create a dialog to show screen recording options to the user. */
- public ScreenRecordDialog createScreenRecordDialog(Context context, FeatureFlags flags,
- DialogLaunchAnimator dialogLaunchAnimator, ActivityStarter activityStarter,
- @Nullable Runnable onStartRecordingClicked) {
- return new ScreenRecordDialog(context, this, activityStarter, mUserContextProvider,
- flags, dialogLaunchAnimator, onStartRecordingClicked);
+ public Dialog createScreenRecordDialog(Context context, FeatureFlags flags,
+ DialogLaunchAnimator dialogLaunchAnimator,
+ ActivityStarter activityStarter,
+ @Nullable Runnable onStartRecordingClicked) {
+ return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
+ ? new ScreenRecordPermissionDialog(context, this, activityStarter,
+ dialogLaunchAnimator, mUserContextProvider, onStartRecordingClicked)
+ : new ScreenRecordDialog(context, this, activityStarter,
+ mUserContextProvider, flags, dialogLaunchAnimator, onStartRecordingClicked);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
new file mode 100644
index 000000000000..19bb15a5c2d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenrecord
+
+import android.app.Activity
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.ResultReceiver
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Spinner
+import android.widget.Switch
+import androidx.annotation.LayoutRes
+import com.android.systemui.R
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.media.MediaProjectionAppSelectorActivity
+import com.android.systemui.media.MediaProjectionCaptureTarget
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.settings.UserContextProvider
+
+/** Dialog to select screen recording options */
+class ScreenRecordPermissionDialog(
+ context: Context?,
+ private val controller: RecordingController,
+ private val activityStarter: ActivityStarter,
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val userContextProvider: UserContextProvider,
+ private val onStartRecordingClicked: Runnable?
+) : BaseScreenSharePermissionDialog(context, createOptionList(), null) {
+ private lateinit var tapsSwitch: Switch
+ private lateinit var tapsView: View
+ private lateinit var audioSwitch: Switch
+ private lateinit var options: Spinner
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setDialogTitle(R.string.screenrecord_start_label)
+ setStartButtonText(R.string.screenrecord_start_recording)
+ setStartButtonOnClickListener { v: View? ->
+ onStartRecordingClicked?.run()
+ if (selectedScreenShareOption.mode == ENTIRE_SCREEN) {
+ requestScreenCapture(/* captureTarget= */ null)
+ }
+ if (selectedScreenShareOption.mode == SINGLE_APP) {
+ val intent = Intent(context, MediaProjectionAppSelectorActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ // We can't start activity for result here so we use result receiver to get
+ // the selected target to capture
+ intent.putExtra(
+ MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
+ CaptureTargetResultReceiver()
+ )
+ val animationController = dialogLaunchAnimator.createActivityLaunchController(v!!)
+ if (animationController == null) {
+ dismiss()
+ }
+ activityStarter.startActivity(intent, /* dismissShade= */ true, animationController)
+ }
+ dismiss()
+ }
+ initRecordOptionsView()
+ }
+
+ @LayoutRes override fun getOptionsViewLayoutId(): Int = R.layout.screen_record_options
+
+ private fun initRecordOptionsView() {
+ audioSwitch = findViewById(R.id.screenrecord_audio_switch)
+ tapsSwitch = findViewById(R.id.screenrecord_taps_switch)
+ tapsView = findViewById(R.id.show_taps)
+ updateTapsViewVisibility()
+ options = findViewById(R.id.screen_recording_options)
+ val a: ArrayAdapter<*> =
+ ScreenRecordingAdapter(context, android.R.layout.simple_spinner_dropdown_item, MODES)
+ a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ options.adapter = a
+ options.setOnItemClickListenerInt { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
+ audioSwitch.isChecked = true
+ }
+ }
+
+ override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
+ super.onItemSelected(adapterView, view, pos, id)
+ updateTapsViewVisibility()
+ }
+
+ private fun updateTapsViewVisibility() {
+ tapsView.visibility = if (selectedScreenShareOption.mode == SINGLE_APP) GONE else VISIBLE
+ }
+
+ /**
+ * Starts screen capture after some countdown
+ * @param captureTarget target to capture (could be e.g. a task) or null to record the whole
+ * screen
+ */
+ private fun requestScreenCapture(captureTarget: MediaProjectionCaptureTarget?) {
+ val userContext = userContextProvider.userContext
+ val showTaps = selectedScreenShareOption.mode != SINGLE_APP && tapsSwitch.isChecked
+ val audioMode =
+ if (audioSwitch.isChecked) options.selectedItem as ScreenRecordingAudioSource
+ else ScreenRecordingAudioSource.NONE
+ val startIntent =
+ PendingIntent.getForegroundService(
+ userContext,
+ RecordingService.REQUEST_CODE,
+ RecordingService.getStartIntent(
+ userContext,
+ Activity.RESULT_OK,
+ audioMode.ordinal,
+ showTaps,
+ captureTarget
+ ),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ val stopIntent =
+ PendingIntent.getService(
+ userContext,
+ RecordingService.REQUEST_CODE,
+ RecordingService.getStopIntent(userContext),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ controller.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent)
+ }
+
+ private inner class CaptureTargetResultReceiver() :
+ ResultReceiver(Handler(Looper.getMainLooper())) {
+ override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
+ if (resultCode == Activity.RESULT_OK) {
+ val captureTarget =
+ resultData.getParcelable(
+ MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET,
+ MediaProjectionCaptureTarget::class.java
+ )
+
+ // Start recording of the selected target
+ requestScreenCapture(captureTarget)
+ }
+ }
+ }
+
+ companion object {
+ private val MODES =
+ listOf(
+ ScreenRecordingAudioSource.INTERNAL,
+ ScreenRecordingAudioSource.MIC,
+ ScreenRecordingAudioSource.MIC_AND_INTERNAL
+ )
+ private const val DELAY_MS: Long = 3000
+ private const val INTERVAL_MS: Long = 1000
+ private fun createOptionList(): List<ScreenShareOption> {
+ return listOf(
+ ScreenShareOption(
+ SINGLE_APP,
+ R.string.screenrecord_option_single_app,
+ R.string.screenrecord_warning_single_app
+ ),
+ ScreenShareOption(
+ ENTIRE_SCREEN,
+ R.string.screenrecord_option_entire_screen,
+ R.string.screenrecord_warning_entire_screen
+ )
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
new file mode 100644
index 000000000000..914d29a52b53
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenrecord
+
+import androidx.annotation.IntDef
+import androidx.annotation.StringRes
+import kotlin.annotation.Retention
+
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(SINGLE_APP, ENTIRE_SCREEN)
+annotation class ScreenShareMode
+
+const val SINGLE_APP = 0
+const val ENTIRE_SCREEN = 1
+
+class ScreenShareOption(
+ @ScreenShareMode val mode: Int,
+ @StringRes val spinnerText: Int,
+ @StringRes val warningText: Int
+)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 5961635a0dba..01e32b7ada5f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -32,7 +32,7 @@ import android.view.WindowManagerGlobal
import com.android.internal.infra.ServiceConnector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
@@ -45,7 +45,7 @@ class ActionIntentExecutor
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- @Background private val bgDispatcher: CoroutineDispatcher,
+ @Main private val mainDispatcher: CoroutineDispatcher,
private val context: Context,
) {
/**
@@ -70,23 +70,21 @@ constructor(
userId: Int,
overrideTransition: Boolean,
) {
- withContext(bgDispatcher) {
- dismissKeyguard()
+ dismissKeyguard()
- if (userId == UserHandle.myUserId()) {
- context.startActivity(intent, bundle)
- } else {
- launchCrossProfileIntent(userId, intent, bundle)
- }
+ if (userId == UserHandle.myUserId()) {
+ withContext(mainDispatcher) { context.startActivity(intent, bundle) }
+ } else {
+ launchCrossProfileIntent(userId, intent, bundle)
+ }
- if (overrideTransition) {
- val runner = RemoteAnimationAdapter(SCREENSHOT_REMOTE_RUNNER, 0, 0)
- try {
- WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionRemote(runner, Display.DEFAULT_DISPLAY)
- } catch (e: Exception) {
- Log.e(TAG, "Error overriding screenshot app transition", e)
- }
+ if (overrideTransition) {
+ val runner = RemoteAnimationAdapter(SCREENSHOT_REMOTE_RUNNER, 0, 0)
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionRemote(runner, Display.DEFAULT_DISPLAY)
+ } catch (e: Exception) {
+ Log.e(TAG, "Error overriding screenshot app transition", e)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 8bf956b86683..5450db98af52 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -46,6 +46,8 @@ import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot;
import com.google.common.util.concurrent.ListenableFuture;
@@ -67,6 +69,7 @@ public class LongScreenshotActivity extends Activity {
private static final String TAG = LogConfig.logTag(LongScreenshotActivity.class);
public static final String EXTRA_CAPTURE_RESPONSE = "capture-response";
+ public static final String EXTRA_SCREENSHOT_USER_HANDLE = "screenshot-userhandle";
private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
private final UiEventLogger mUiEventLogger;
@@ -74,6 +77,8 @@ public class LongScreenshotActivity extends Activity {
private final Executor mBackgroundExecutor;
private final ImageExporter mImageExporter;
private final LongScreenshotData mLongScreenshotHolder;
+ private final ActionIntentExecutor mActionExecutor;
+ private final FeatureFlags mFeatureFlags;
private ImageView mPreview;
private ImageView mTransitionView;
@@ -85,6 +90,7 @@ public class LongScreenshotActivity extends Activity {
private CropView mCropView;
private MagnifierView mMagnifierView;
private ScrollCaptureResponse mScrollCaptureResponse;
+ private UserHandle mScreenshotUserHandle;
private File mSavedImagePath;
private ListenableFuture<File> mCacheSaveFuture;
@@ -103,12 +109,15 @@ public class LongScreenshotActivity extends Activity {
@Inject
public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
@Main Executor mainExecutor, @Background Executor bgExecutor,
- LongScreenshotData longScreenshotHolder) {
+ LongScreenshotData longScreenshotHolder, ActionIntentExecutor actionExecutor,
+ FeatureFlags featureFlags) {
mUiEventLogger = uiEventLogger;
mUiExecutor = mainExecutor;
mBackgroundExecutor = bgExecutor;
mImageExporter = imageExporter;
mLongScreenshotHolder = longScreenshotHolder;
+ mActionExecutor = actionExecutor;
+ mFeatureFlags = featureFlags;
}
@@ -139,6 +148,11 @@ public class LongScreenshotActivity extends Activity {
Intent intent = getIntent();
mScrollCaptureResponse = intent.getParcelableExtra(EXTRA_CAPTURE_RESPONSE);
+ mScreenshotUserHandle = intent.getParcelableExtra(EXTRA_SCREENSHOT_USER_HANDLE,
+ UserHandle.class);
+ if (mScreenshotUserHandle == null) {
+ mScreenshotUserHandle = Process.myUserHandle();
+ }
if (savedInstanceState != null) {
String savedImagePath = savedInstanceState.getString(KEY_SAVED_IMAGE_PATH);
@@ -318,36 +332,51 @@ public class LongScreenshotActivity extends Activity {
}
private void doEdit(Uri uri) {
- String editorPackage = getString(R.string.config_screenshotEditor);
- Intent intent = new Intent(Intent.ACTION_EDIT);
- if (!TextUtils.isEmpty(editorPackage)) {
- intent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY) && mScreenshotUserHandle
+ != Process.myUserHandle()) {
+ // TODO: Fix transition for work profile. Omitting it in the meantime.
+ mActionExecutor.launchIntentAsync(
+ ActionIntentCreator.INSTANCE.createEditIntent(uri, this),
+ null,
+ mScreenshotUserHandle.getIdentifier(), false);
+ } else {
+ String editorPackage = getString(R.string.config_screenshotEditor);
+ Intent intent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ intent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ }
+ intent.setDataAndType(uri, "image/png");
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ mTransitionView.setImageBitmap(mOutputBitmap);
+ mTransitionView.setVisibility(View.VISIBLE);
+ mTransitionView.setTransitionName(
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
+ // TODO: listen for transition completing instead of finishing onStop
+ mTransitionStarted = true;
+ startActivity(intent,
+ ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
}
- intent.setDataAndType(uri, "image/png");
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- mTransitionView.setImageBitmap(mOutputBitmap);
- mTransitionView.setVisibility(View.VISIBLE);
- mTransitionView.setTransitionName(
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
- // TODO: listen for transition completing instead of finishing onStop
- mTransitionStarted = true;
- startActivity(intent,
- ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
}
private void doShare(Uri uri) {
- Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType("image/png");
- intent.putExtra(Intent.EXTRA_STREAM, uri);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_GRANT_READ_URI_PERMISSION);
- Intent sharingChooserIntent = Intent.createChooser(intent, null)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
+ if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
+ Intent shareIntent = ActionIntentCreator.INSTANCE.createShareIntent(uri, null);
+ mActionExecutor.launchIntentAsync(shareIntent, null,
+ mScreenshotUserHandle.getIdentifier(), false);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("image/png");
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ Intent sharingChooserIntent = Intent.createChooser(intent, null)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
+ }
}
private void onClicked(View v) {
@@ -389,8 +418,8 @@ public class LongScreenshotActivity extends Activity {
mOutputBitmap = renderBitmap(drawable, bounds);
ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now(),
- // TODO: Owner must match the owner of the captured window.
- Process.myUserHandle());
+ mFeatureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)
+ ? mScreenshotUserHandle : Process.myUserHandle());
exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 9b5295d1bb3d..d94c8277b82c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -63,6 +63,7 @@ import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -276,6 +277,7 @@ public class ScreenshotController {
mScreenshotNotificationSmartActionsProvider;
private final TimeoutHandler mScreenshotHandler;
private final ActionIntentExecutor mActionExecutor;
+ private final UserManager mUserManager;
private ScreenshotView mScreenshotView;
private Bitmap mScreenBitmap;
@@ -314,7 +316,8 @@ public class ScreenshotController {
TimeoutHandler timeoutHandler,
BroadcastSender broadcastSender,
ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider,
- ActionIntentExecutor actionExecutor
+ ActionIntentExecutor actionExecutor,
+ UserManager userManager
) {
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
@@ -345,6 +348,7 @@ public class ScreenshotController {
mWindowManager = mContext.getSystemService(WindowManager.class);
mFlags = flags;
mActionExecutor = actionExecutor;
+ mUserManager = userManager;
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
@@ -587,7 +591,7 @@ public class ScreenshotController {
// Wait until this window is attached to request because it is
// the reference used to locate the target window (below).
withWindowAttached(() -> {
- requestScrollCapture();
+ requestScrollCapture(owner);
mWindow.peekDecorView().getViewRootImpl().setActivityConfigCallback(
new ViewRootImpl.ActivityConfigCallback() {
@Override
@@ -599,11 +603,11 @@ public class ScreenshotController {
mScreenshotView.hideScrollChip();
// Delay scroll capture eval a bit to allow the underlying activity
// to set up in the new orientation.
- mScreenshotHandler.postDelayed(
- ScreenshotController.this::requestScrollCapture, 150);
+ mScreenshotHandler.postDelayed(() -> {
+ requestScrollCapture(owner);
+ }, 150);
mScreenshotView.updateInsets(
- mWindowManager.getCurrentWindowMetrics()
- .getWindowInsets());
+ mWindowManager.getCurrentWindowMetrics().getWindowInsets());
// Screenshot animation calculations won't be valid anymore,
// so just end
if (mScreenshotAnimation != null
@@ -651,7 +655,7 @@ public class ScreenshotController {
mScreenshotHandler.cancelTimeout(); // restarted after animation
}
- private void requestScrollCapture() {
+ private void requestScrollCapture(UserHandle owner) {
if (!allowLongScreenshots()) {
Log.d(TAG, "Long screenshots not supported on this device");
return;
@@ -664,10 +668,11 @@ public class ScreenshotController {
mScrollCaptureClient.request(DEFAULT_DISPLAY);
mLastScrollCaptureRequest = future;
mLastScrollCaptureRequest.addListener(() ->
- onScrollCaptureResponseReady(future), mMainExecutor);
+ onScrollCaptureResponseReady(future, owner), mMainExecutor);
}
- private void onScrollCaptureResponseReady(Future<ScrollCaptureResponse> responseFuture) {
+ private void onScrollCaptureResponseReady(Future<ScrollCaptureResponse> responseFuture,
+ UserHandle owner) {
try {
if (mLastScrollCaptureResponse != null) {
mLastScrollCaptureResponse.close();
@@ -697,7 +702,7 @@ public class ScreenshotController {
mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
mScreenshotTakenInPortrait);
// delay starting scroll capture to make sure the scrim is up before the app moves
- mScreenshotView.post(() -> runBatchScrollCapture(response));
+ mScreenshotView.post(() -> runBatchScrollCapture(response, owner));
});
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "requestScrollCapture failed", e);
@@ -706,7 +711,7 @@ public class ScreenshotController {
ListenableFuture<ScrollCaptureController.LongScreenshot> mLongScreenshotFuture;
- private void runBatchScrollCapture(ScrollCaptureResponse response) {
+ private void runBatchScrollCapture(ScrollCaptureResponse response, UserHandle owner) {
// Clear the reference to prevent close() in dismissScreenshot
mLastScrollCaptureResponse = null;
@@ -740,6 +745,8 @@ public class ScreenshotController {
longScreenshot));
final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
+ intent.putExtra(LongScreenshotActivity.EXTRA_SCREENSHOT_USER_HANDLE,
+ owner);
intent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -975,16 +982,25 @@ public class ScreenshotController {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mScreenshotView.setChipIntents(imageData);
+ doPostAnimation(imageData);
}
});
} else {
- mScreenshotView.setChipIntents(imageData);
+ doPostAnimation(imageData);
}
});
}
}
+ private void doPostAnimation(ScreenshotController.SavedImageData imageData) {
+ mScreenshotView.setChipIntents(imageData);
+ if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+ && mUserManager.isManagedProfile(imageData.owner.getIdentifier())) {
+ // TODO: Read app from configuration
+ mScreenshotView.showWorkProfileMessage("Files");
+ }
+ }
+
/**
* Sets up the action shade and its entrance animation, once we get the Quick Share action data.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 8b5a24c0e2ff..c891686ada8f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -89,7 +89,9 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "User has saved a long screenshot to a file")
SCREENSHOT_LONG_SCREENSHOT_SAVED(910),
@UiEvent(doc = "User has discarded the result of a long screenshot")
- SCREENSHOT_LONG_SCREENSHOT_EXIT(911);
+ SCREENSHOT_LONG_SCREENSHOT_EXIT(911),
+ @UiEvent(doc = "A screenshot has been taken and saved to work profile")
+ SCREENSHOT_SAVED_TO_WORK_PROFILE(1240);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index c41e2bc14afc..4cb91e134003 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -15,12 +15,17 @@
*/
package com.android.systemui.screenshot
-import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
+import androidx.lifecycle.LifecycleService
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.phone.CentralSurfaces
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import java.util.Optional
import javax.inject.Inject
@@ -30,7 +35,8 @@ import javax.inject.Inject
internal class ScreenshotProxyService @Inject constructor(
private val mExpansionMgr: ShadeExpansionStateManager,
private val mCentralSurfacesOptional: Optional<CentralSurfaces>,
-) : Service() {
+ @Main private val mMainDispatcher: CoroutineDispatcher,
+) : LifecycleService() {
private val mBinder: IBinder = object : IScreenshotProxy.Stub() {
/**
@@ -43,20 +49,28 @@ internal class ScreenshotProxyService @Inject constructor(
}
override fun dismissKeyguard(callback: IOnDoneCallback) {
- if (mCentralSurfacesOptional.isPresent) {
- mCentralSurfacesOptional.get().executeRunnableDismissingKeyguard(
- Runnable {
- callback.onDone(true)
- }, null,
- true /* dismissShade */, true /* afterKeyguardGone */,
- true /* deferred */
- )
- } else {
- callback.onDone(false)
+ lifecycleScope.launch {
+ executeAfterDismissing(callback)
}
}
}
+ private suspend fun executeAfterDismissing(callback: IOnDoneCallback) =
+ withContext(mMainDispatcher) {
+ mCentralSurfacesOptional.ifPresentOrElse(
+ {
+ it.executeRunnableDismissingKeyguard(
+ Runnable {
+ callback.onDone(true)
+ }, null,
+ true /* dismissShade */, true /* afterKeyguardGone */,
+ true /* deferred */
+ )
+ },
+ { callback.onDone(false) }
+ )
+ }
+
override fun onBind(intent: Intent): IBinder? {
Log.d(TAG, "onBind: $intent")
return mBinder
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 27331ae7a389..0a4b550882c9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -80,6 +80,7 @@ import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -137,6 +138,8 @@ public class ScreenshotView extends FrameLayout implements
private ImageView mScrollingScrim;
private DraggableConstraintLayout mScreenshotStatic;
+ private ViewGroup mMessageContainer;
+ private TextView mMessageContent;
private ImageView mScreenshotPreview;
private ImageView mScreenshotBadge;
private View mScreenshotPreviewBorder;
@@ -340,10 +343,26 @@ public class ScreenshotView extends FrameLayout implements
}
}
+ /**
+ * Show a notification under the screenshot view indicating that a work profile screenshot has
+ * been taken and which app can be used to view it.
+ *
+ * @param appName The name of the app to use to view screenshots
+ */
+ void showWorkProfileMessage(String appName) {
+ mMessageContent.setText(
+ mContext.getString(R.string.screenshot_work_profile_notification, appName));
+ mMessageContainer.setVisibility(VISIBLE);
+ }
+
@Override // View
protected void onFinishInflate() {
mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim));
mScreenshotStatic = requireNonNull(findViewById(R.id.screenshot_static));
+ mMessageContainer =
+ requireNonNull(mScreenshotStatic.findViewById(R.id.screenshot_message_container));
+ mMessageContent =
+ requireNonNull(mMessageContainer.findViewById(R.id.screenshot_message_content));
mScreenshotPreview = requireNonNull(findViewById(R.id.screenshot_preview));
mScreenshotPreviewBorder = requireNonNull(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/CameraLauncher.java b/packages/SystemUI/src/com/android/systemui/shade/CameraLauncher.java
new file mode 100644
index 000000000000..fc61e90ab8f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/CameraLauncher.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade;
+
+import com.android.systemui.camera.CameraGestureHelper;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+
+import javax.inject.Inject;
+
+/** Handles launching camera from Shade. */
+@SysUISingleton
+public class CameraLauncher {
+ private final CameraGestureHelper mCameraGestureHelper;
+ private final KeyguardBypassController mKeyguardBypassController;
+
+ private boolean mLaunchingAffordance;
+
+ @Inject
+ public CameraLauncher(
+ CameraGestureHelper cameraGestureHelper,
+ KeyguardBypassController keyguardBypassController
+ ) {
+ mCameraGestureHelper = cameraGestureHelper;
+ mKeyguardBypassController = keyguardBypassController;
+ }
+
+ /** Launches the camera. */
+ public void launchCamera(int source, boolean isShadeFullyCollapsed) {
+ if (!isShadeFullyCollapsed) {
+ setLaunchingAffordance(true);
+ }
+
+ mCameraGestureHelper.launchCamera(source);
+ }
+
+ /**
+ * Set whether we are currently launching an affordance. This is currently only set when
+ * launched via a camera gesture.
+ */
+ public void setLaunchingAffordance(boolean launchingAffordance) {
+ mLaunchingAffordance = launchingAffordance;
+ mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
+ }
+
+ /**
+ * Return true when a bottom affordance is launching an occluded activity with a splash screen.
+ */
+ public boolean isLaunchingAffordance() {
+ return mLaunchingAffordance;
+ }
+
+ /**
+ * Whether the camera application can be launched for the camera launch gesture.
+ */
+ public boolean canCameraGestureBeLaunched(int barState) {
+ return mCameraGestureHelper.canCameraGestureBeLaunched(barState);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
index 4063af3cbc36..5011227ad2cc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
@@ -51,6 +51,8 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain
connect(R.id.statusIcons, ConstraintSet.START, R.id.date, ConstraintSet.END)
connect(R.id.privacy_container, ConstraintSet.START, R.id.date, ConstraintSet.END)
constrainWidth(R.id.statusIcons, ViewGroup.LayoutParams.WRAP_CONTENT)
+ constrainedWidth(R.id.date, true)
+ constrainedWidth(R.id.statusIcons, true)
}
)
}
@@ -92,7 +94,8 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain
centerEnd,
ConstraintSet.END
)
- constrainWidth(R.id.statusIcons, 0)
+ constrainedWidth(R.id.date, true)
+ constrainedWidth(R.id.statusIcons, true)
},
qsConstraintsChanges = {
setGuidelineBegin(centerStart, offsetFromEdge)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index b8c4b3e36218..32c8f3bba6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -118,17 +118,18 @@ import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.DejankUtils;
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.camera.CameraGestureHelper;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -232,7 +233,7 @@ import javax.inject.Inject;
import javax.inject.Provider;
@CentralSurfacesComponent.CentralSurfacesScope
-public final class NotificationPanelViewController {
+public final class NotificationPanelViewController implements Dumpable {
public static final String TAG = NotificationPanelView.class.getSimpleName();
public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
@@ -462,7 +463,6 @@ public final class NotificationPanelViewController {
private boolean mCollapsedOnDown;
private boolean mClosingWithAlphaFadeOut;
private boolean mHeadsUpAnimatingAway;
- private boolean mLaunchingAffordance;
private final FalsingManager mFalsingManager;
private final FalsingCollector mFalsingCollector;
@@ -573,7 +573,7 @@ public final class NotificationPanelViewController {
/** Whether the current animator is resetting the pulse expansion after a drag down. */
private boolean mIsPulseExpansionResetAnimator;
- private final Rect mKeyguardStatusAreaClipBounds = new Rect();
+ private final Rect mLastQsClipBounds = new Rect();
private final Region mQsInterceptRegion = new Region();
/** Alpha of the views which only show on the keyguard but not in shade / shade locked. */
private float mKeyguardOnlyContentAlpha = 1.0f;
@@ -613,7 +613,6 @@ public final class NotificationPanelViewController {
private final NotificationListContainer mNotificationListContainer;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private final NPVCDownEventState.Buffer mLastDownEvents;
- private final CameraGestureHelper mCameraGestureHelper;
private final KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
private final KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
private float mMinExpandHeight;
@@ -741,9 +740,9 @@ public final class NotificationPanelViewController {
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
ShadeTransitionController shadeTransitionController,
SystemClock systemClock,
- CameraGestureHelper cameraGestureHelper,
KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
- KeyguardBottomAreaInteractor keyguardBottomAreaInteractor) {
+ KeyguardBottomAreaInteractor keyguardBottomAreaInteractor,
+ DumpManager dumpManager) {
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
@@ -921,8 +920,8 @@ public final class NotificationPanelViewController {
unlockAnimationStarted(playingCannedAnimation, isWakeAndUnlock, startDelay);
}
});
- mCameraGestureHelper = cameraGestureHelper;
mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor;
+ dumpManager.registerDumpable(this);
}
private void unlockAnimationFinished() {
@@ -1309,7 +1308,11 @@ public final class NotificationPanelViewController {
}
private void initBottomArea() {
- mKeyguardBottomArea.init(mKeyguardBottomAreaViewModel, mFalsingManager);
+ mKeyguardBottomArea.init(
+ mKeyguardBottomAreaViewModel,
+ mFalsingManager,
+ mLockIconViewController
+ );
}
@VisibleForTesting
@@ -2806,7 +2809,7 @@ public final class NotificationPanelViewController {
*/
private void applyQSClippingBounds(int left, int top, int right, int bottom,
boolean qsVisible) {
- if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
+ if (!mAnimateNextNotificationBounds || mLastQsClipBounds.isEmpty()) {
if (mQsClippingAnimation != null) {
// update the end position of the animator
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
@@ -2815,10 +2818,10 @@ public final class NotificationPanelViewController {
}
} else {
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
- final int startLeft = mKeyguardStatusAreaClipBounds.left;
- final int startTop = mKeyguardStatusAreaClipBounds.top;
- final int startRight = mKeyguardStatusAreaClipBounds.right;
- final int startBottom = mKeyguardStatusAreaClipBounds.bottom;
+ final int startLeft = mLastQsClipBounds.left;
+ final int startTop = mLastQsClipBounds.top;
+ final int startRight = mLastQsClipBounds.right;
+ final int startBottom = mLastQsClipBounds.bottom;
if (mQsClippingAnimation != null) {
mQsClippingAnimation.cancel();
}
@@ -2855,12 +2858,10 @@ public final class NotificationPanelViewController {
private void applyQSClippingImmediately(int left, int top, int right, int bottom,
boolean qsVisible) {
- // Fancy clipping for quick settings
int radius = mScrimCornerRadius;
boolean clipStatusView = false;
+ mLastQsClipBounds.set(left, top, right, bottom);
if (mIsFullWidth) {
- // The padding on this area is large enough that we can use a cheaper clipping strategy
- mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
clipStatusView = qsVisible;
float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius;
radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
@@ -2895,8 +2896,8 @@ public final class NotificationPanelViewController {
radius,
qsVisible && !mSplitShadeEnabled);
}
- mKeyguardStatusViewController.setClipBounds(
- clipStatusView ? mKeyguardStatusAreaClipBounds : null);
+ // The padding on this area is large enough that we can use a cheaper clipping strategy
+ mKeyguardStatusViewController.setClipBounds(clipStatusView ? mLastQsClipBounds : null);
if (!qsVisible && mSplitShadeEnabled) {
// On the lockscreen when qs isn't visible, we don't want the bounds of the shade to
// be visible, otherwise you can see the bounds once swiping up to see bouncer
@@ -3386,11 +3387,7 @@ public final class NotificationPanelViewController {
boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
if (mPanelExpanded != isExpanded) {
mPanelExpanded = isExpanded;
-
- mHeadsUpManager.setIsPanelExpanded(isExpanded);
- mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded);
- mCentralSurfaces.setPanelExpanded(isExpanded);
-
+ mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
if (!isExpanded && mQs != null && mQs.isCustomizing()) {
mQs.closeCustomizer();
}
@@ -3944,6 +3941,10 @@ public final class NotificationPanelViewController {
}
}
+ public int getBarState() {
+ return mBarState;
+ }
+
private boolean isOnKeyguard() {
return mBarState == KEYGUARD;
}
@@ -3989,35 +3990,6 @@ public final class NotificationPanelViewController {
&& mBarState == StatusBarState.SHADE;
}
- /** Launches the camera. */
- public void launchCamera(int source) {
- if (!isFullyCollapsed()) {
- setLaunchingAffordance(true);
- }
-
- mCameraGestureHelper.launchCamera(source);
- }
-
- public void onAffordanceLaunchEnded() {
- setLaunchingAffordance(false);
- }
-
- /** Set whether we are currently launching an affordance (i.e. camera gesture). */
- private void setLaunchingAffordance(boolean launchingAffordance) {
- mLaunchingAffordance = launchingAffordance;
- mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
- }
-
- /** Returns whether a bottom affordance is launching an occluded activity with splash screen. */
- public boolean isLaunchingAffordanceWithPreview() {
- return mLaunchingAffordance;
- }
-
- /** Whether the camera application can be launched by the camera launch gesture. */
- public boolean canCameraGestureBeLaunched() {
- return mCameraGestureHelper.canCameraGestureBeLaunched(mBarState);
- }
-
public boolean hideStatusBarIconsWhenExpanded() {
if (mIsLaunchAnimationRunning) {
return mHideIconsDuringLaunchAnimation;
@@ -4283,29 +4255,184 @@ public final class NotificationPanelViewController {
mBlockingExpansionForCurrentTouch = mTracking;
}
+ @Override
public void dump(PrintWriter pw, String[] args) {
- pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
- + " tracking=%s timeAnim=%s%s "
- + "touchDisabled=%s" + "]",
- this.getClass().getSimpleName(), getExpandedHeight(), getMaxPanelHeight(),
- mClosing ? "T" : "f", mTracking ? "T" : "f", mHeightAnimator,
- ((mHeightAnimator != null && mHeightAnimator.isStarted()) ? " (started)" : ""),
- mTouchDisabled ? "T" : "f"));
+ pw.println(TAG + ":");
IndentingPrintWriter ipw = asIndenting(pw);
ipw.increaseIndent();
+
+ ipw.print("mDownTime="); ipw.println(mDownTime);
+ ipw.print("mTouchSlopExceededBeforeDown="); ipw.println(mTouchSlopExceededBeforeDown);
+ ipw.print("mIsLaunchAnimationRunning="); ipw.println(mIsLaunchAnimationRunning);
+ ipw.print("mOverExpansion="); ipw.println(mOverExpansion);
+ ipw.print("mExpandedHeight="); ipw.println(mExpandedHeight);
+ ipw.print("mTracking="); ipw.println(mTracking);
+ ipw.print("mHintAnimationRunning="); ipw.println(mHintAnimationRunning);
+ ipw.print("mExpanding="); ipw.println(mExpanding);
+ ipw.print("mSplitShadeEnabled="); ipw.println(mSplitShadeEnabled);
+ ipw.print("mKeyguardNotificationBottomPadding=");
+ ipw.println(mKeyguardNotificationBottomPadding);
+ ipw.print("mKeyguardNotificationTopPadding="); ipw.println(mKeyguardNotificationTopPadding);
+ ipw.print("mMaxAllowedKeyguardNotifications=");
+ ipw.println(mMaxAllowedKeyguardNotifications);
+ ipw.print("mAnimateNextPositionUpdate="); ipw.println(mAnimateNextPositionUpdate);
+ ipw.print("mQuickQsHeaderHeight="); ipw.println(mQuickQsHeaderHeight);
+ ipw.print("mQsTrackingPointer="); ipw.println(mQsTrackingPointer);
+ ipw.print("mQsTracking="); ipw.println(mQsTracking);
+ ipw.print("mConflictingQsExpansionGesture="); ipw.println(mConflictingQsExpansionGesture);
+ ipw.print("mPanelExpanded="); ipw.println(mPanelExpanded);
+ ipw.print("mQsExpanded="); ipw.println(mQsExpanded);
+ ipw.print("mQsExpandedWhenExpandingStarted="); ipw.println(mQsExpandedWhenExpandingStarted);
+ ipw.print("mQsFullyExpanded="); ipw.println(mQsFullyExpanded);
+ ipw.print("mKeyguardShowing="); ipw.println(mKeyguardShowing);
+ ipw.print("mKeyguardQsUserSwitchEnabled="); ipw.println(mKeyguardQsUserSwitchEnabled);
+ ipw.print("mKeyguardUserSwitcherEnabled="); ipw.println(mKeyguardUserSwitcherEnabled);
+ ipw.print("mDozing="); ipw.println(mDozing);
+ ipw.print("mDozingOnDown="); ipw.println(mDozingOnDown);
+ ipw.print("mBouncerShowing="); ipw.println(mBouncerShowing);
+ ipw.print("mBarState="); ipw.println(mBarState);
+ ipw.print("mInitialHeightOnTouch="); ipw.println(mInitialHeightOnTouch);
+ ipw.print("mInitialTouchX="); ipw.println(mInitialTouchX);
+ ipw.print("mInitialTouchY="); ipw.println(mInitialTouchY);
+ ipw.print("mQsExpansionHeight="); ipw.println(mQsExpansionHeight);
+ ipw.print("mQsMinExpansionHeight="); ipw.println(mQsMinExpansionHeight);
+ ipw.print("mQsMaxExpansionHeight="); ipw.println(mQsMaxExpansionHeight);
+ ipw.print("mQsPeekHeight="); ipw.println(mQsPeekHeight);
+ ipw.print("mStackScrollerOverscrolling="); ipw.println(mStackScrollerOverscrolling);
+ ipw.print("mQsExpansionFromOverscroll="); ipw.println(mQsExpansionFromOverscroll);
+ ipw.print("mLastOverscroll="); ipw.println(mLastOverscroll);
+ ipw.print("mQsExpansionEnabledPolicy="); ipw.println(mQsExpansionEnabledPolicy);
+ ipw.print("mQsExpansionEnabledAmbient="); ipw.println(mQsExpansionEnabledAmbient);
+ ipw.print("mStatusBarMinHeight="); ipw.println(mStatusBarMinHeight);
+ ipw.print("mStatusBarHeaderHeightKeyguard="); ipw.println(mStatusBarHeaderHeightKeyguard);
+ ipw.print("mOverStretchAmount="); ipw.println(mOverStretchAmount);
+ ipw.print("mDownX="); ipw.println(mDownX);
+ ipw.print("mDownY="); ipw.println(mDownY);
+ ipw.print("mDisplayTopInset="); ipw.println(mDisplayTopInset);
+ ipw.print("mDisplayRightInset="); ipw.println(mDisplayRightInset);
+ ipw.print("mLargeScreenShadeHeaderHeight="); ipw.println(mLargeScreenShadeHeaderHeight);
+ ipw.print("mSplitShadeNotificationsScrimMarginBottom=");
+ ipw.println(mSplitShadeNotificationsScrimMarginBottom);
+ ipw.print("mIsExpanding="); ipw.println(mIsExpanding);
+ ipw.print("mQsExpandImmediate="); ipw.println(mQsExpandImmediate);
+ ipw.print("mTwoFingerQsExpandPossible="); ipw.println(mTwoFingerQsExpandPossible);
+ ipw.print("mHeaderDebugInfo="); ipw.println(mHeaderDebugInfo);
+ ipw.print("mQsAnimatorExpand="); ipw.println(mQsAnimatorExpand);
+ ipw.print("mQsScrimEnabled="); ipw.println(mQsScrimEnabled);
+ ipw.print("mQsTouchAboveFalsingThreshold="); ipw.println(mQsTouchAboveFalsingThreshold);
+ ipw.print("mQsFalsingThreshold="); ipw.println(mQsFalsingThreshold);
+ ipw.print("mHeadsUpStartHeight="); ipw.println(mHeadsUpStartHeight);
+ ipw.print("mListenForHeadsUp="); ipw.println(mListenForHeadsUp);
+ ipw.print("mNavigationBarBottomHeight="); ipw.println(mNavigationBarBottomHeight);
+ ipw.print("mExpandingFromHeadsUp="); ipw.println(mExpandingFromHeadsUp);
+ ipw.print("mCollapsedOnDown="); ipw.println(mCollapsedOnDown);
+ ipw.print("mClosingWithAlphaFadeOut="); ipw.println(mClosingWithAlphaFadeOut);
+ ipw.print("mHeadsUpAnimatingAway="); ipw.println(mHeadsUpAnimatingAway);
+ ipw.print("mShowIconsWhenExpanded="); ipw.println(mShowIconsWhenExpanded);
+ ipw.print("mIndicationBottomPadding="); ipw.println(mIndicationBottomPadding);
+ ipw.print("mAmbientIndicationBottomPadding="); ipw.println(mAmbientIndicationBottomPadding);
+ ipw.print("mIsFullWidth="); ipw.println(mIsFullWidth);
+ ipw.print("mBlockingExpansionForCurrentTouch=");
+ ipw.println(mBlockingExpansionForCurrentTouch);
+ ipw.print("mExpectingSynthesizedDown="); ipw.println(mExpectingSynthesizedDown);
+ ipw.print("mLastEventSynthesizedDown="); ipw.println(mLastEventSynthesizedDown);
+ ipw.print("mInterpolatedDarkAmount="); ipw.println(mInterpolatedDarkAmount);
+ ipw.print("mLinearDarkAmount="); ipw.println(mLinearDarkAmount);
+ ipw.print("mPulsing="); ipw.println(mPulsing);
+ ipw.print("mHideIconsDuringLaunchAnimation="); ipw.println(mHideIconsDuringLaunchAnimation);
+ ipw.print("mStackScrollerMeasuringPass="); ipw.println(mStackScrollerMeasuringPass);
+ ipw.print("mPanelAlpha="); ipw.println(mPanelAlpha);
+ ipw.print("mBottomAreaShadeAlpha="); ipw.println(mBottomAreaShadeAlpha);
+ ipw.print("mHeadsUpInset="); ipw.println(mHeadsUpInset);
+ ipw.print("mHeadsUpPinnedMode="); ipw.println(mHeadsUpPinnedMode);
+ ipw.print("mAllowExpandForSmallExpansion="); ipw.println(mAllowExpandForSmallExpansion);
+ ipw.print("mLockscreenNotificationQSPadding=");
+ ipw.println(mLockscreenNotificationQSPadding);
+ ipw.print("mTransitioningToFullShadeProgress=");
+ ipw.println(mTransitioningToFullShadeProgress);
+ ipw.print("mTransitionToFullShadeQSPosition=");
+ ipw.println(mTransitionToFullShadeQSPosition);
+ ipw.print("mDistanceForQSFullShadeTransition=");
+ ipw.println(mDistanceForQSFullShadeTransition);
+ ipw.print("mQsTranslationForFullShadeTransition=");
+ ipw.println(mQsTranslationForFullShadeTransition);
+ ipw.print("mMaxOverscrollAmountForPulse="); ipw.println(mMaxOverscrollAmountForPulse);
+ ipw.print("mAnimateNextNotificationBounds="); ipw.println(mAnimateNextNotificationBounds);
+ ipw.print("mNotificationBoundsAnimationDelay=");
+ ipw.println(mNotificationBoundsAnimationDelay);
+ ipw.print("mNotificationBoundsAnimationDuration=");
+ ipw.println(mNotificationBoundsAnimationDuration);
+ ipw.print("mIsPanelCollapseOnQQS="); ipw.println(mIsPanelCollapseOnQQS);
+ ipw.print("mAnimatingQS="); ipw.println(mAnimatingQS);
+ ipw.print("mIsQsTranslationResetAnimator="); ipw.println(mIsQsTranslationResetAnimator);
+ ipw.print("mIsPulseExpansionResetAnimator="); ipw.println(mIsPulseExpansionResetAnimator);
+ ipw.print("mKeyguardOnlyContentAlpha="); ipw.println(mKeyguardOnlyContentAlpha);
+ ipw.print("mKeyguardOnlyTransitionTranslationY=");
+ ipw.println(mKeyguardOnlyTransitionTranslationY);
+ ipw.print("mUdfpsMaxYBurnInOffset="); ipw.println(mUdfpsMaxYBurnInOffset);
+ ipw.print("mIsGestureNavigation="); ipw.println(mIsGestureNavigation);
+ ipw.print("mOldLayoutDirection="); ipw.println(mOldLayoutDirection);
+ ipw.print("mScrimCornerRadius="); ipw.println(mScrimCornerRadius);
+ ipw.print("mScreenCornerRadius="); ipw.println(mScreenCornerRadius);
+ ipw.print("mQSAnimatingHiddenFromCollapsed="); ipw.println(mQSAnimatingHiddenFromCollapsed);
+ ipw.print("mUseLargeScreenShadeHeader="); ipw.println(mUseLargeScreenShadeHeader);
+ ipw.print("mEnableQsClipping="); ipw.println(mEnableQsClipping);
+ ipw.print("mQsClipTop="); ipw.println(mQsClipTop);
+ ipw.print("mQsClipBottom="); ipw.println(mQsClipBottom);
+ ipw.print("mQsVisible="); ipw.println(mQsVisible);
+ ipw.print("mMinFraction="); ipw.println(mMinFraction);
+ ipw.print("mStatusViewCentered="); ipw.println(mStatusViewCentered);
+ ipw.print("mSplitShadeFullTransitionDistance=");
+ ipw.println(mSplitShadeFullTransitionDistance);
+ ipw.print("mSplitShadeScrimTransitionDistance=");
+ ipw.println(mSplitShadeScrimTransitionDistance);
+ ipw.print("mMinExpandHeight="); ipw.println(mMinExpandHeight);
+ ipw.print("mPanelUpdateWhenAnimatorEnds="); ipw.println(mPanelUpdateWhenAnimatorEnds);
+ ipw.print("mHasVibratedOnOpen="); ipw.println(mHasVibratedOnOpen);
+ ipw.print("mFixedDuration="); ipw.println(mFixedDuration);
+ ipw.print("mPanelFlingOvershootAmount="); ipw.println(mPanelFlingOvershootAmount);
+ ipw.print("mLastGesturedOverExpansion="); ipw.println(mLastGesturedOverExpansion);
+ ipw.print("mIsSpringBackAnimation="); ipw.println(mIsSpringBackAnimation);
+ ipw.print("mInSplitShade="); ipw.println(mInSplitShade);
+ ipw.print("mHintDistance="); ipw.println(mHintDistance);
+ ipw.print("mInitialOffsetOnTouch="); ipw.println(mInitialOffsetOnTouch);
+ ipw.print("mCollapsedAndHeadsUpOnDown="); ipw.println(mCollapsedAndHeadsUpOnDown);
+ ipw.print("mExpandedFraction="); ipw.println(mExpandedFraction);
+ ipw.print("mExpansionDragDownAmountPx="); ipw.println(mExpansionDragDownAmountPx);
+ ipw.print("mPanelClosedOnDown="); ipw.println(mPanelClosedOnDown);
+ ipw.print("mHasLayoutedSinceDown="); ipw.println(mHasLayoutedSinceDown);
+ ipw.print("mUpdateFlingVelocity="); ipw.println(mUpdateFlingVelocity);
+ ipw.print("mUpdateFlingOnLayout="); ipw.println(mUpdateFlingOnLayout);
+ ipw.print("mClosing="); ipw.println(mClosing);
+ ipw.print("mTouchSlopExceeded="); ipw.println(mTouchSlopExceeded);
+ ipw.print("mTrackingPointer="); ipw.println(mTrackingPointer);
+ ipw.print("mTouchSlop="); ipw.println(mTouchSlop);
+ ipw.print("mSlopMultiplier="); ipw.println(mSlopMultiplier);
+ ipw.print("mTouchAboveFalsingThreshold="); ipw.println(mTouchAboveFalsingThreshold);
+ ipw.print("mTouchStartedInEmptyArea="); ipw.println(mTouchStartedInEmptyArea);
+ ipw.print("mMotionAborted="); ipw.println(mMotionAborted);
+ ipw.print("mUpwardsWhenThresholdReached="); ipw.println(mUpwardsWhenThresholdReached);
+ ipw.print("mAnimatingOnDown="); ipw.println(mAnimatingOnDown);
+ ipw.print("mHandlingPointerUp="); ipw.println(mHandlingPointerUp);
+ ipw.print("mInstantExpanding="); ipw.println(mInstantExpanding);
+ ipw.print("mAnimateAfterExpanding="); ipw.println(mAnimateAfterExpanding);
+ ipw.print("mIsFlinging="); ipw.println(mIsFlinging);
+ ipw.print("mViewName="); ipw.println(mViewName);
+ ipw.print("mInitialExpandY="); ipw.println(mInitialExpandY);
+ ipw.print("mInitialExpandX="); ipw.println(mInitialExpandX);
+ ipw.print("mTouchDisabled="); ipw.println(mTouchDisabled);
+ ipw.print("mInitialTouchFromKeyguard="); ipw.println(mInitialTouchFromKeyguard);
+ ipw.print("mNextCollapseSpeedUpFactor="); ipw.println(mNextCollapseSpeedUpFactor);
+ ipw.print("mGestureWaitForTouchSlop="); ipw.println(mGestureWaitForTouchSlop);
+ ipw.print("mIgnoreXTouchSlop="); ipw.println(mIgnoreXTouchSlop);
+ ipw.print("mExpandLatencyTracking="); ipw.println(mExpandLatencyTracking);
+ ipw.print("mExpandLatencyTracking="); ipw.println(mExpandLatencyTracking);
ipw.println("gestureExclusionRect:" + calculateGestureExclusionRect());
- ipw.println("applyQSClippingImmediately: top(" + mQsClipTop + ") bottom(" + mQsClipBottom
- + ")");
- ipw.println("qsVisible:" + mQsVisible);
new DumpsysTableLogger(
TAG,
NPVCDownEventState.TABLE_HEADERS,
mLastDownEvents.toList()
).printTableData(ipw);
- ipw.decreaseIndent();
- if (mKeyguardStatusBarViewController != null) {
- mKeyguardStatusBarViewController.dump(pw, args);
- }
}
@@ -4657,7 +4784,7 @@ public final class NotificationPanelViewController {
mUpdateFlingVelocity = vel;
}
} else if (!mCentralSurfaces.isBouncerShowing()
- && !mStatusBarKeyguardViewManager.isShowingAlternateAuth()
+ && !mStatusBarKeyguardViewManager.isShowingAlternateBouncer()
&& !mKeyguardStateController.isKeyguardGoingAway()) {
onEmptySpaceClick();
onTrackingStopped(true);
@@ -6041,7 +6168,7 @@ public final class NotificationPanelViewController {
== AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId()
|| action
== AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) {
- mStatusBarKeyguardViewManager.showBouncer(true);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true);
return true;
}
return super.performAccessibilityAction(host, action, args);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 66a22f4ddc0d..b719177f702a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -44,6 +44,7 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -158,6 +159,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
configurationController.addCallback(this);
shadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
+ shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
float desiredPreferredRefreshRate = context.getResources()
.getInteger(R.integer.config_keyguardRefreshRate);
@@ -204,6 +206,14 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
+ @VisibleForTesting
+ void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ if (mCurrentState.mPanelExpanded != isExpanded) {
+ mCurrentState.mPanelExpanded = isExpanded;
+ apply(mCurrentState);
+ }
+ }
+
/**
* Register a listener to monitor scrims visibility
* @param listener A listener to monitor scrims visibility
@@ -699,15 +709,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
@Override
- public void setPanelExpanded(boolean isExpanded) {
- if (mCurrentState.mPanelExpanded == isExpanded) {
- return;
- }
- mCurrentState.mPanelExpanded = isExpanded;
- apply(mCurrentState);
- }
-
- @Override
public void onRemoteInputActive(boolean remoteInputActive) {
mCurrentState.mRemoteInputActive = remoteInputActive;
apply(mCurrentState);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index e52170e13292..400b0baea01b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -16,6 +16,7 @@
package com.android.systemui.shade;
+import static android.os.Trace.TRACE_TAG_ALWAYS;
import static android.view.WindowInsets.Type.systemBars;
import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -33,6 +34,7 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Trace;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.DisplayCutout;
@@ -299,6 +301,19 @@ public class NotificationShadeWindowView extends FrameLayout {
return mode;
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("NotificationShadeWindowView#onMeasure");
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ Trace.endSection();
+ }
+
+ @Override
+ public void requestLayout() {
+ Trace.instant(TRACE_TAG_ALWAYS, "NotificationShadeWindowView#requestLayout");
+ super.requestLayout();
+ }
+
private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
private final ActionMode.Callback mWrapped;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 1e63b2dd134f..bb67280c07b8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -284,7 +284,7 @@ public class NotificationShadeWindowViewController {
return true;
}
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) {
// capture all touches if the alt auth bouncer is showing
return true;
}
@@ -322,7 +322,7 @@ public class NotificationShadeWindowViewController {
handled = !mService.isPulsing();
}
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) {
// eat the touch
handled = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 667392c9796e..a1767cc5888d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -34,6 +34,7 @@ import javax.inject.Inject
class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents {
private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>()
+ private val fullExpansionListeners = CopyOnWriteArrayList<ShadeFullExpansionListener>()
private val qsExpansionListeners = CopyOnWriteArrayList<ShadeQsExpansionListener>()
private val stateListeners = CopyOnWriteArrayList<ShadeStateListener>()
private val shadeStateEventsListeners = CopyOnWriteArrayList<ShadeStateEventsListener>()
@@ -62,6 +63,15 @@ class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents {
expansionListeners.remove(listener)
}
+ fun addFullExpansionListener(listener: ShadeFullExpansionListener) {
+ fullExpansionListeners.add(listener)
+ listener.onShadeExpansionFullyChanged(qsExpanded)
+ }
+
+ fun removeFullExpansionListener(listener: ShadeFullExpansionListener) {
+ fullExpansionListeners.remove(listener)
+ }
+
fun addQsExpansionListener(listener: ShadeQsExpansionListener) {
qsExpansionListeners.add(listener)
listener.onQsExpansionChanged(qsExpanded)
@@ -156,6 +166,13 @@ class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents {
qsExpansionListeners.forEach { it.onQsExpansionChanged(qsExpanded) }
}
+ fun onShadeExpansionFullyChanged(isExpanded: Boolean) {
+ this.expanded = isExpanded
+
+ debugLog("expanded=$isExpanded")
+ fullExpansionListeners.forEach { it.onShadeExpansionFullyChanged(isExpanded) }
+ }
+
/** Updates the panel state if necessary. */
fun updateState(@PanelState state: Int) {
debugLog(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt
new file mode 100644
index 000000000000..6d13e1972255
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+/** A listener interface to be notified of expansion events for the notification shade. */
+fun interface ShadeFullExpansionListener {
+ /** Invoked whenever the shade expansion changes, when it is fully collapsed or expanded */
+ fun onShadeExpansionFullyChanged(isExpanded: Boolean)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index f2b86035200b..2101efb61443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -55,7 +55,6 @@ import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -64,7 +63,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.text.format.Formatter;
-import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
@@ -76,6 +74,7 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.logging.KeyguardLogger;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
@@ -123,7 +122,6 @@ public class KeyguardIndicationController {
private static final String TAG = "KeyguardIndication";
private static final boolean DEBUG_CHARGING_SPEED = false;
- private static final boolean DEBUG = Build.IS_DEBUGGABLE;
private static final int MSG_HIDE_TRANSIENT = 1;
private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2;
@@ -139,6 +137,7 @@ public class KeyguardIndicationController {
protected final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AuthController mAuthController;
+ private final KeyguardLogger mKeyguardLogger;
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTopIndicationView;
private KeyguardIndicationTextView mLockScreenIndicationView;
@@ -229,7 +228,8 @@ public class KeyguardIndicationController {
ScreenLifecycle screenLifecycle,
KeyguardBypassController keyguardBypassController,
AccessibilityManager accessibilityManager,
- FaceHelpMessageDeferral faceHelpMessageDeferral) {
+ FaceHelpMessageDeferral faceHelpMessageDeferral,
+ KeyguardLogger keyguardLogger) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mDevicePolicyManager = devicePolicyManager;
@@ -249,6 +249,7 @@ public class KeyguardIndicationController {
mKeyguardBypassController = keyguardBypassController;
mAccessibilityManager = accessibilityManager;
mScreenLifecycle = screenLifecycle;
+ mKeyguardLogger = keyguardLogger;
mScreenLifecycle.addObserver(mScreenObserver);
mFaceAcquiredMessageDeferral = faceHelpMessageDeferral;
@@ -925,7 +926,7 @@ public class KeyguardIndicationController {
}
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) {
return; // udfps affordance is highlighted, no need to show action to unlock
} else if (mKeyguardUpdateMonitor.isFaceEnrolled()) {
String message = mContext.getString(R.string.keyguard_retry);
@@ -1024,7 +1025,7 @@ public class KeyguardIndicationController {
mChargingTimeRemaining = mPowerPluggedIn
? mBatteryInfo.computeChargeTimeRemaining() : -1;
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IBatteryStats: ", e);
+ mKeyguardLogger.logException(e, "Error calling IBatteryStats");
mChargingTimeRemaining = -1;
}
updateDeviceEntryIndication(!wasPluggedIn && mPowerPluggedInWired);
@@ -1072,8 +1073,10 @@ public class KeyguardIndicationController {
final boolean isCoExFaceAcquisitionMessage =
faceAuthSoftError && isUnlockWithFingerprintPossible;
if (isCoExFaceAcquisitionMessage && !mCoExFaceAcquisitionMsgIdsToShow.contains(msgId)) {
- debugLog("skip showing msgId=" + msgId + " helpString=" + helpString
- + ", due to co-ex logic");
+ mKeyguardLogger.logBiometricMessage(
+ "skipped showing help message due to co-ex logic",
+ msgId,
+ helpString);
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
mInitialTextColorState);
@@ -1131,7 +1134,7 @@ public class KeyguardIndicationController {
CharSequence deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
mFaceAcquiredMessageDeferral.reset();
if (shouldSuppressFaceError(msgId, mKeyguardUpdateMonitor)) {
- debugLog("suppressingFaceError msgId=" + msgId + " errString= " + errString);
+ mKeyguardLogger.logBiometricMessage("suppressingFaceError", msgId, errString);
return;
}
if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
@@ -1145,8 +1148,9 @@ public class KeyguardIndicationController {
private void onFingerprintAuthError(int msgId, String errString) {
if (shouldSuppressFingerprintError(msgId, mKeyguardUpdateMonitor)) {
- debugLog("suppressingFingerprintError msgId=" + msgId
- + " errString= " + errString);
+ mKeyguardLogger.logBiometricMessage("suppressingFingerprintError",
+ msgId,
+ errString);
} else {
showErrorMessageNowOrLater(errString, null);
}
@@ -1179,16 +1183,14 @@ public class KeyguardIndicationController {
@Override
public void onTrustChanged(int userId) {
- if (getCurrentUser() != userId) {
- return;
- }
+ if (!isCurrentUser(userId)) return;
updateDeviceEntryIndication(false);
}
@Override
- public void showTrustGrantedMessage(CharSequence message) {
- mTrustGrantedIndication = message;
- updateDeviceEntryIndication(false);
+ public void onTrustGrantedWithFlags(int flags, int userId, @Nullable String message) {
+ if (!isCurrentUser(userId)) return;
+ showTrustGrantedMessage(flags, message);
}
@Override
@@ -1248,6 +1250,15 @@ public class KeyguardIndicationController {
}
}
+ private boolean isCurrentUser(int userId) {
+ return getCurrentUser() == userId;
+ }
+
+ void showTrustGrantedMessage(int flags, @Nullable CharSequence message) {
+ mTrustGrantedIndication = message;
+ updateDeviceEntryIndication(false);
+ }
+
private void handleFaceLockoutError(String errString) {
int followupMsgId = canUnlockWithFingerprint() ? R.string.keyguard_suggest_fingerprint
: R.string.keyguard_unlock;
@@ -1263,7 +1274,8 @@ public class KeyguardIndicationController {
showErrorMessageNowOrLater(errString, followupMessage);
} else if (!mAuthController.isUdfpsFingerDown()) {
// On subsequent lockouts, we show a more generic locked out message.
- showBiometricMessage(mContext.getString(R.string.keyguard_face_unlock_unavailable),
+ showErrorMessageNowOrLater(
+ mContext.getString(R.string.keyguard_face_unlock_unavailable),
followupMessage);
}
}
@@ -1274,7 +1286,8 @@ public class KeyguardIndicationController {
}
private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) {
- debugLog("showDeferredFaceMessage msgId=" + deferredFaceMessage);
+ mKeyguardLogger.logBiometricMessage("deferred message after face auth timeout",
+ null, String.valueOf(deferredFaceMessage));
if (canUnlockWithFingerprint()) {
// Co-ex: show deferred message OR nothing
// if we're on the lock screen (bouncer isn't showing), show the deferred msg
@@ -1286,7 +1299,8 @@ public class KeyguardIndicationController {
);
} else {
// otherwise, don't show any message
- debugLog("skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
+ mKeyguardLogger.logBiometricMessage(
+ "skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
}
} else if (deferredFaceMessage != null) {
// Face-only: The face timeout message is not very actionable, let's ask the
@@ -1307,12 +1321,6 @@ public class KeyguardIndicationController {
KeyguardUpdateMonitor.getCurrentUser());
}
- private void debugLog(String logMsg) {
- if (DEBUG) {
- Log.d(TAG, logMsg);
- }
- }
-
private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 9d2750fa7b5f..bc456d5d4613 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -18,6 +18,8 @@ import android.view.View
import com.android.systemui.animation.Interpolators
import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
import com.android.systemui.util.getColorWithAlpha
+import com.android.systemui.util.leak.RotationUtils
+import com.android.systemui.util.leak.RotationUtils.Rotation
import java.util.function.Consumer
/**
@@ -67,22 +69,19 @@ object LiftReveal : LightRevealEffect {
override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
val ovalWidthIncreaseAmount =
- getPercentPastThreshold(interpolatedAmount, WIDEN_OVAL_THRESHOLD)
+ getPercentPastThreshold(interpolatedAmount, WIDEN_OVAL_THRESHOLD)
val initialWidthMultiplier = (1f - OVAL_INITIAL_WIDTH_PERCENT) / 2f
with(scrim) {
- revealGradientEndColorAlpha = 1f - getPercentPastThreshold(
- amount, FADE_END_COLOR_OUT_THRESHOLD)
+ revealGradientEndColorAlpha =
+ 1f - getPercentPastThreshold(amount, FADE_END_COLOR_OUT_THRESHOLD)
setRevealGradientBounds(
- scrim.width * initialWidthMultiplier +
- -scrim.width * ovalWidthIncreaseAmount,
- scrim.height * OVAL_INITIAL_TOP_PERCENT -
- scrim.height * interpolatedAmount,
- scrim.width * (1f - initialWidthMultiplier) +
- scrim.width * ovalWidthIncreaseAmount,
- scrim.height * OVAL_INITIAL_BOTTOM_PERCENT +
- scrim.height * interpolatedAmount)
+ scrim.width * initialWidthMultiplier + -scrim.width * ovalWidthIncreaseAmount,
+ scrim.height * OVAL_INITIAL_TOP_PERCENT - scrim.height * interpolatedAmount,
+ scrim.width * (1f - initialWidthMultiplier) + scrim.width * ovalWidthIncreaseAmount,
+ scrim.height * OVAL_INITIAL_BOTTOM_PERCENT + scrim.height * interpolatedAmount
+ )
}
}
}
@@ -97,12 +96,17 @@ class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffe
scrim.interpolatedRevealAmount = interpolatedAmount
scrim.startColorAlpha =
- getPercentPastThreshold(1 - interpolatedAmount,
- threshold = 1 - START_COLOR_REVEAL_PERCENTAGE)
+ getPercentPastThreshold(
+ 1 - interpolatedAmount,
+ threshold = 1 - START_COLOR_REVEAL_PERCENTAGE
+ )
scrim.revealGradientEndColorAlpha =
- 1f - getPercentPastThreshold(interpolatedAmount,
- threshold = REVEAL_GRADIENT_END_COLOR_ALPHA_START_PERCENTAGE)
+ 1f -
+ getPercentPastThreshold(
+ interpolatedAmount,
+ threshold = REVEAL_GRADIENT_END_COLOR_ALPHA_START_PERCENTAGE
+ )
// Start changing gradient bounds later to avoid harsh gradient in the beginning
val gradientBoundsAmount = lerp(GRADIENT_START_BOUNDS_PERCENTAGE, 1.0f, interpolatedAmount)
@@ -179,7 +183,7 @@ class PowerButtonReveal(
*/
private val OFF_SCREEN_START_AMOUNT = 0.05f
- private val WIDTH_INCREASE_MULTIPLIER = 1.25f
+ private val INCREASE_MULTIPLIER = 1.25f
override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN_REVERSE.getInterpolation(amount)
@@ -188,15 +192,36 @@ class PowerButtonReveal(
with(scrim) {
revealGradientEndColorAlpha = 1f - fadeAmount
interpolatedRevealAmount = interpolatedAmount
- setRevealGradientBounds(
+ @Rotation val rotation = RotationUtils.getRotation(scrim.getContext())
+ if (rotation == RotationUtils.ROTATION_NONE) {
+ setRevealGradientBounds(
width * (1f + OFF_SCREEN_START_AMOUNT) -
- width * WIDTH_INCREASE_MULTIPLIER * interpolatedAmount,
- powerButtonY -
- height * interpolatedAmount,
+ width * INCREASE_MULTIPLIER * interpolatedAmount,
+ powerButtonY - height * interpolatedAmount,
width * (1f + OFF_SCREEN_START_AMOUNT) +
- width * WIDTH_INCREASE_MULTIPLIER * interpolatedAmount,
- powerButtonY +
- height * interpolatedAmount)
+ width * INCREASE_MULTIPLIER * interpolatedAmount,
+ powerButtonY + height * interpolatedAmount
+ )
+ } else if (rotation == RotationUtils.ROTATION_LANDSCAPE) {
+ setRevealGradientBounds(
+ powerButtonY - width * interpolatedAmount,
+ (-height * OFF_SCREEN_START_AMOUNT) -
+ height * INCREASE_MULTIPLIER * interpolatedAmount,
+ powerButtonY + width * interpolatedAmount,
+ (-height * OFF_SCREEN_START_AMOUNT) +
+ height * INCREASE_MULTIPLIER * interpolatedAmount
+ )
+ } else {
+ // RotationUtils.ROTATION_SEASCAPE
+ setRevealGradientBounds(
+ (width - powerButtonY) - width * interpolatedAmount,
+ height * (1f + OFF_SCREEN_START_AMOUNT) -
+ height * INCREASE_MULTIPLIER * interpolatedAmount,
+ (width - powerButtonY) + width * interpolatedAmount,
+ height * (1f + OFF_SCREEN_START_AMOUNT) +
+ height * INCREASE_MULTIPLIER * interpolatedAmount
+ )
+ }
}
}
}
@@ -208,9 +233,7 @@ class PowerButtonReveal(
*/
class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
- /**
- * Listener that is called if the scrim's opaqueness changes
- */
+ /** Listener that is called if the scrim's opaqueness changes */
lateinit var isScrimOpaqueChangedListener: Consumer<Boolean>
/**
@@ -224,8 +247,11 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
revealEffect.setRevealAmountOnScrim(value, this)
updateScrimOpaque()
- Trace.traceCounter(Trace.TRACE_TAG_APP, "light_reveal_amount",
- (field * 100).toInt())
+ Trace.traceCounter(
+ Trace.TRACE_TAG_APP,
+ "light_reveal_amount",
+ (field * 100).toInt()
+ )
invalidate()
}
}
@@ -250,10 +276,10 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
/**
* Alpha of the fill that can be used in the beginning of the animation to hide the content.
- * Normally the gradient bounds are animated from small size so the content is not visible,
- * but if the start gradient bounds allow to see some content this could be used to make the
- * reveal smoother. It can help to add fade in effect in the beginning of the animation.
- * The color of the fill is determined by [revealGradientEndColor].
+ * Normally the gradient bounds are animated from small size so the content is not visible, but
+ * if the start gradient bounds allow to see some content this could be used to make the reveal
+ * smoother. It can help to add fade in effect in the beginning of the animation. The color of
+ * the fill is determined by [revealGradientEndColor].
*
* 0 - no fill and content is visible, 1 - the content is covered with the start color
*/
@@ -281,9 +307,7 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
}
}
- /**
- * Is the scrim currently fully opaque
- */
+ /** Is the scrim currently fully opaque */
var isScrimOpaque = false
private set(value) {
if (field != value) {
@@ -318,16 +342,22 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
* Paint used to draw a transparent-to-white radial gradient. This will be scaled and translated
* via local matrix in [onDraw] so we never need to construct a new shader.
*/
- private val gradientPaint = Paint().apply {
- shader = RadialGradient(
- 0f, 0f, 1f,
- intArrayOf(Color.TRANSPARENT, Color.WHITE), floatArrayOf(0f, 1f),
- Shader.TileMode.CLAMP)
-
- // SRC_OVER ensures that we draw the semitransparent pixels over other views in the same
- // window, rather than outright replacing them.
- xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)
- }
+ private val gradientPaint =
+ Paint().apply {
+ shader =
+ RadialGradient(
+ 0f,
+ 0f,
+ 1f,
+ intArrayOf(Color.TRANSPARENT, Color.WHITE),
+ floatArrayOf(0f, 1f),
+ Shader.TileMode.CLAMP
+ )
+
+ // SRC_OVER ensures that we draw the semitransparent pixels over other views in the same
+ // window, rather than outright replacing them.
+ xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)
+ }
/**
* Matrix applied to [gradientPaint]'s RadialGradient shader to move the gradient to
@@ -347,8 +377,8 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
* simply a helper method that sets [revealGradientCenter], [revealGradientWidth], and
* [revealGradientHeight] for you.
*
- * This method does not call [invalidate] - you should do so once you're done changing
- * properties.
+ * This method does not call [invalidate]
+ * - you should do so once you're done changing properties.
*/
fun setRevealGradientBounds(left: Float, top: Float, right: Float, bottom: Float) {
revealGradientWidth = right - left
@@ -359,8 +389,12 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
}
override fun onDraw(canvas: Canvas?) {
- if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0 ||
- revealAmount == 0f) {
+ if (
+ canvas == null ||
+ revealGradientWidth <= 0 ||
+ revealGradientHeight <= 0 ||
+ revealAmount == 0f
+ ) {
if (revealAmount < 1f) {
canvas?.drawColor(revealGradientEndColor)
}
@@ -383,8 +417,10 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
}
private fun setPaintColorFilter() {
- gradientPaint.colorFilter = PorterDuffColorFilter(
- getColorWithAlpha(revealGradientEndColor, revealGradientEndColorAlpha),
- PorterDuff.Mode.MULTIPLY)
+ gradientPaint.colorFilter =
+ PorterDuffColorFilter(
+ getColorWithAlpha(revealGradientEndColor, revealGradientEndColorAlpha),
+ PorterDuff.Mode.MULTIPLY
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index e21acb7e0f68..0b1807dd2d70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -123,9 +123,6 @@ public interface NotificationShadeWindowController extends RemoteInputController
/** Sets whether the window was collapsed by force or not. */
default void setForceWindowCollapsed(boolean force) {}
- /** Sets whether panel is expanded or not. */
- default void setPanelExpanded(boolean isExpanded) {}
-
/** Gets whether the panel is expanded or not. */
default boolean getPanelExpanded() {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index c04bc8289f81..186e6dc92f93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -54,6 +54,7 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -153,13 +154,18 @@ public class StatusBarStateControllerImpl implements
private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN;
@Inject
- public StatusBarStateControllerImpl(UiEventLogger uiEventLogger, DumpManager dumpManager,
- InteractionJankMonitor interactionJankMonitor) {
+ public StatusBarStateControllerImpl(
+ UiEventLogger uiEventLogger,
+ DumpManager dumpManager,
+ InteractionJankMonitor interactionJankMonitor,
+ ShadeExpansionStateManager shadeExpansionStateManager
+ ) {
mUiEventLogger = uiEventLogger;
mInteractionJankMonitor = interactionJankMonitor;
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistoricalRecords[i] = new HistoricalState();
}
+ shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
dumpManager.registerDumpable(this);
}
@@ -263,21 +269,6 @@ public class StatusBarStateControllerImpl implements
}
@Override
- public boolean setPanelExpanded(boolean expanded) {
- if (mIsExpanded == expanded) {
- return false;
- }
- mIsExpanded = expanded;
- String tag = getClass().getSimpleName() + "#setIsExpanded";
- DejankUtils.startDetectingBlockingIpcs(tag);
- for (RankedListener rl : new ArrayList<>(mListeners)) {
- rl.mListener.onExpandedChanged(mIsExpanded);
- }
- DejankUtils.stopDetectingBlockingIpcs(tag);
- return true;
- }
-
- @Override
public float getInterpolatedDozeAmount() {
return mDozeInterpolator.getInterpolation(mDozeAmount);
}
@@ -325,6 +316,18 @@ public class StatusBarStateControllerImpl implements
}
}
+ private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ if (mIsExpanded != isExpanded) {
+ mIsExpanded = isExpanded;
+ String tag = getClass().getSimpleName() + "#setIsExpanded";
+ DejankUtils.startDetectingBlockingIpcs(tag);
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.mListener.onExpandedChanged(mIsExpanded);
+ }
+ DejankUtils.stopDetectingBlockingIpcs(tag);
+ }
+ }
+
private void startDozeAnimation() {
if (mDozeAmount == 0f || mDozeAmount == 1f) {
mDozeInterpolator = mIsDozing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 2cc77384fb2a..e0cf812a11e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -109,13 +109,6 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated);
/**
- * Update the expanded state from {@link CentralSurfaces}'s perspective
- * @param expanded are we expanded?
- * @return {@code true} if the state changed, else {@code false}
- */
- boolean setPanelExpanded(boolean expanded);
-
- /**
* Sets whether to leave status bar open when hiding keyguard
*/
void setLeaveOpenOnKeyguardHide(boolean leaveOpen);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index 5cf1abc18274..035b90faf7da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -15,9 +15,7 @@
*/
package com.android.systemui.statusbar.connectivity;
-import static com.android.settingslib.mobile.MobileMappings.getDefaultIcons;
-import static com.android.settingslib.mobile.MobileMappings.getIconKey;
-import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
import android.content.Context;
import android.content.Intent;
@@ -46,6 +44,7 @@ import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
+import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy;
import com.android.systemui.util.CarrierConfigTracker;
import java.io.PrintWriter;
@@ -63,6 +62,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
private final TelephonyManager mPhone;
private final CarrierConfigTracker mCarrierConfigTracker;
private final SubscriptionDefaults mDefaults;
+ private final MobileMappingsProxy mMobileMappingsProxy;
private final String mNetworkNameDefault;
private final String mNetworkNameSeparator;
private final ContentObserver mObserver;
@@ -121,6 +121,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
TelephonyManager phone,
CallbackHandler callbackHandler,
NetworkControllerImpl networkController,
+ MobileMappingsProxy mobileMappingsProxy,
SubscriptionInfo info,
SubscriptionDefaults defaults,
Looper receiverLooper,
@@ -135,13 +136,14 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
mPhone = phone;
mDefaults = defaults;
mSubscriptionInfo = info;
+ mMobileMappingsProxy = mobileMappingsProxy;
mNetworkNameSeparator = getTextIfExists(
R.string.status_bar_network_name_separator).toString();
mNetworkNameDefault = getTextIfExists(
com.android.internal.R.string.lockscreen_carrier_default).toString();
- mNetworkToIconLookup = mapIconSets(mConfig);
- mDefaultIcons = getDefaultIcons(mConfig);
+ mNetworkToIconLookup = mMobileMappingsProxy.mapIconSets(mConfig);
+ mDefaultIcons = mMobileMappingsProxy.getDefaultIcons(mConfig);
String networkName = info.getCarrierName() != null ? info.getCarrierName().toString()
: mNetworkNameDefault;
@@ -161,8 +163,8 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
void setConfiguration(Config config) {
mConfig = config;
updateInflateSignalStrength();
- mNetworkToIconLookup = mapIconSets(mConfig);
- mDefaultIcons = getDefaultIcons(mConfig);
+ mNetworkToIconLookup = mMobileMappingsProxy.mapIconSets(mConfig);
+ mDefaultIcons = mMobileMappingsProxy.getDefaultIcons(mConfig);
updateTelephony();
}
@@ -271,8 +273,9 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
}
- final QsInfo qsInfo = getQsInfo(contentDescription, icons.dataType);
- final SbInfo sbInfo = getSbInfo(contentDescription, icons.dataType);
+ int iconId = mCurrentState.getNetworkTypeIcon(mContext);
+ final QsInfo qsInfo = getQsInfo(contentDescription, iconId);
+ final SbInfo sbInfo = getSbInfo(contentDescription, iconId);
MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
sbInfo.icon,
@@ -373,6 +376,10 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
} else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
updateDataSim();
notifyListenersIfNecessary();
+ } else if (action.equals(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) {
+ int carrierId = intent.getIntExtra(
+ TelephonyManager.EXTRA_CARRIER_ID, UNKNOWN_CARRIER_ID);
+ mCurrentState.setCarrierId(carrierId);
}
}
@@ -477,7 +484,8 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
mCurrentState.level = getSignalLevel(mCurrentState.signalStrength);
}
- String iconKey = getIconKey(mCurrentState.telephonyDisplayInfo);
+ mCurrentState.setCarrierId(mPhone.getSimCarrierId());
+ String iconKey = mMobileMappingsProxy.getIconKey(mCurrentState.telephonyDisplayInfo);
if (mNetworkToIconLookup.get(iconKey) != null) {
mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
index 793817948803..a323454a7ed8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
@@ -22,6 +22,7 @@ import android.telephony.TelephonyManager
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.MobileStatusTracker
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.util.CarrierConfigTracker
import javax.inject.Inject
@@ -33,6 +34,7 @@ internal class MobileSignalControllerFactory @Inject constructor(
val context: Context,
val callbackHandler: CallbackHandler,
val carrierConfigTracker: CarrierConfigTracker,
+ val mobileMappings: MobileMappingsProxy,
) {
fun createMobileSignalController(
config: MobileMappings.Config,
@@ -56,6 +58,7 @@ internal class MobileSignalControllerFactory @Inject constructor(
phone,
callbackHandler,
networkController,
+ mobileMappings,
subscriptionInfo,
subscriptionDefaults,
receiverLooper,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
index f20d20631c95..1fb6a982fdf3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
@@ -16,10 +16,14 @@
package com.android.systemui.statusbar.connectivity
+import android.annotation.DrawableRes
+import android.content.Context
import android.telephony.ServiceState
import android.telephony.SignalStrength
import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyManager
+import com.android.internal.annotations.VisibleForTesting
+import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.Utils
import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus
import com.android.settingslib.mobile.TelephonyIcons
@@ -41,7 +45,7 @@ internal class MobileState(
@JvmField var roaming: Boolean = false,
@JvmField var dataState: Int = TelephonyManager.DATA_DISCONNECTED,
// Tracks the on/off state of the defaultDataSubscription
- @JvmField var defaultDataOff: Boolean = false
+ @JvmField var defaultDataOff: Boolean = false,
) : ConnectivityState() {
@JvmField var telephonyDisplayInfo = TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
@@ -49,6 +53,11 @@ internal class MobileState(
@JvmField var serviceState: ServiceState? = null
@JvmField var signalStrength: SignalStrength? = null
+ var carrierId = TelephonyManager.UNKNOWN_CARRIER_ID
+
+ @VisibleForTesting
+ var networkTypeResIdCache: NetworkTypeResIdCache = NetworkTypeResIdCache()
+
/** @return true if this state is disabled or not default data */
val isDataDisabledOrNotDefault: Boolean
get() = (iconGroup === TelephonyIcons.DATA_DISABLED ||
@@ -125,6 +134,21 @@ internal class MobileState(
return serviceState != null && serviceState!!.roaming
}
+ /**
+ *
+ * Load the (potentially customized) icon resource id for the current network type. Note that
+ * this operation caches the result. Note that reading the [MobileIconGroup.dataType] field
+ * directly will not yield correct results in cases where the carrierId has an associated
+ * override. This is the preferred method for getting the network type indicator.
+ *
+ * @return a drawable res id appropriate for the current (carrierId, networkType) pair
+ */
+ @DrawableRes
+ fun getNetworkTypeIcon(context: Context): Int {
+ val icon = (iconGroup as MobileIconGroup)
+ return networkTypeResIdCache.get(icon, carrierId, context)
+ }
+
fun setFromMobileStatus(mobileStatus: MobileStatus) {
activityIn = mobileStatus.activityIn
activityOut = mobileStatus.activityOut
@@ -140,6 +164,7 @@ internal class MobileState(
super.toString(builder)
builder.append(',')
builder.append("dataSim=$dataSim,")
+ builder.append("carrierId=$carrierId")
builder.append("networkName=$networkName,")
builder.append("networkNameData=$networkNameData,")
builder.append("dataConnected=$dataConnected,")
@@ -157,6 +182,8 @@ internal class MobileState(
builder.append("voiceServiceState=${getVoiceServiceState()},")
builder.append("isInService=${isInService()},")
+ builder.append("networkTypeIconCache=$networkTypeResIdCache")
+
builder.append("serviceState=${serviceState?.minLog() ?: "(null)"},")
builder.append("signalStrength=${signalStrength?.minLog() ?: "(null)"},")
builder.append("displayInfo=$telephonyDisplayInfo")
@@ -164,6 +191,7 @@ internal class MobileState(
override fun tableColumns(): List<String> {
val columns = listOf("dataSim",
+ "carrierId",
"networkName",
"networkNameData",
"dataConnected",
@@ -178,6 +206,7 @@ internal class MobileState(
"showQuickSettingsRatIcon",
"voiceServiceState",
"isInService",
+ "networkTypeIconCache",
"serviceState",
"signalStrength",
"displayInfo")
@@ -187,6 +216,7 @@ internal class MobileState(
override fun tableData(): List<String> {
val columns = listOf(dataSim,
+ carrierId,
networkName,
networkNameData,
dataConnected,
@@ -201,6 +231,7 @@ internal class MobileState(
showQuickSettingsRatIcon(),
getVoiceServiceState(),
isInService(),
+ networkTypeResIdCache,
serviceState?.minLog() ?: "(null)",
signalStrength?.minLog() ?: "(null)",
telephonyDisplayInfo).map { it.toString() }
@@ -217,6 +248,7 @@ internal class MobileState(
if (networkName != other.networkName) return false
if (networkNameData != other.networkNameData) return false
+ if (carrierId != other.carrierId) return false
if (dataSim != other.dataSim) return false
if (dataConnected != other.dataConnected) return false
if (isEmergency != other.isEmergency) return false
@@ -238,6 +270,7 @@ internal class MobileState(
var result = super.hashCode()
result = 31 * result + (networkName?.hashCode() ?: 0)
result = 31 * result + (networkNameData?.hashCode() ?: 0)
+ result = 31 * result + (carrierId.hashCode())
result = 31 * result + dataSim.hashCode()
result = 31 * result + dataConnected.hashCode()
result = 31 * result + isEmergency.hashCode()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 402217dac185..3cc53c16f28b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -22,6 +22,7 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -139,7 +140,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
private final MobileSignalControllerFactory mMobileFactory;
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
- private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private int mActiveMobileDataSubscription = INVALID_SUBSCRIPTION_ID;
// Subcontrollers.
@VisibleForTesting
@@ -503,6 +504,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+ filter.addAction(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
@@ -793,6 +795,20 @@ public class NetworkControllerImpl extends BroadcastReceiver
mConfig = Config.readConfig(mContext);
mReceiverHandler.post(this::handleConfigurationChanged);
break;
+
+ case TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED: {
+ // Notify the relevant MobileSignalController of the change
+ int subId = intent.getIntExtra(
+ TelephonyManager.EXTRA_SUBSCRIPTION_ID,
+ INVALID_SUBSCRIPTION_ID
+ );
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
+ mMobileSignalControllers.get(subId).handleBroadcast(intent);
+ }
+ }
+ }
+ break;
case Intent.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
@@ -820,7 +836,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
break;
default:
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ INVALID_SUBSCRIPTION_ID);
if (SubscriptionManager.isValidSubscriptionId(subId)) {
if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
mMobileSignalControllers.get(subId).handleBroadcast(intent);
@@ -1336,6 +1352,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
String slotString = args.getString("slot");
int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString);
slot = MathUtils.constrain(slot, 0, 8);
+ String carrierIdString = args.getString("carrierid");
+ int carrierId = TextUtils.isEmpty(carrierIdString) ? 0
+ : Integer.parseInt(carrierIdString);
// Ensure we have enough sim slots
List<SubscriptionInfo> subs = new ArrayList<>();
while (mMobileSignalControllers.size() <= slot) {
@@ -1347,6 +1366,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
}
// Hack to index linearly for easy use.
MobileSignalController controller = mMobileSignalControllers.valueAt(slot);
+ if (carrierId != 0) {
+ controller.getState().setCarrierId(carrierId);
+ }
controller.getState().dataSim = datatype != null;
controller.getState().isDefault = datatype != null;
controller.getState().dataConnected = datatype != null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCache.kt
new file mode 100644
index 000000000000..9be7ee99cf02
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCache.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.connectivity
+
+import android.annotation.DrawableRes
+import android.content.Context
+import com.android.settingslib.SignalIcon.MobileIconGroup
+import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
+import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
+
+/**
+ * Cache for network type resource IDs.
+ *
+ * The default framework behavior is to have a statically defined icon per network type. See
+ * [MobileIconGroup] for the standard mapping.
+ *
+ * For the case of carrierId-defined overrides, we want to check [MobileIconCarrierIdOverrides] for
+ * an existing icon override, and cache the result of the operation
+ */
+class NetworkTypeResIdCache(
+ private val overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
+) {
+ @DrawableRes private var cachedResId: Int = 0
+ private var lastCarrierId: Int? = null
+ private var lastIconGroup: MobileIconGroup? = null
+ private var isOverridden: Boolean = false
+
+ @DrawableRes
+ fun get(iconGroup: MobileIconGroup, carrierId: Int, context: Context): Int {
+ if (lastCarrierId != carrierId || lastIconGroup != iconGroup) {
+ lastCarrierId = carrierId
+ lastIconGroup = iconGroup
+
+ val maybeOverride = calculateOverriddenIcon(iconGroup, carrierId, context)
+ if (maybeOverride > 0) {
+ cachedResId = maybeOverride
+ isOverridden = true
+ } else {
+ cachedResId = iconGroup.dataType
+ isOverridden = false
+ }
+ }
+
+ return cachedResId
+ }
+
+ override fun toString(): String {
+ return "networkTypeResIdCache={id=$cachedResId, isOverridden=$isOverridden}"
+ }
+
+ @DrawableRes
+ private fun calculateOverriddenIcon(
+ iconGroup: MobileIconGroup,
+ carrierId: Int,
+ context: Context,
+ ): Int {
+ val name = iconGroup.name
+ if (!overrides.carrierIdEntryExists(carrierId)) {
+ return 0
+ }
+
+ return overrides.getOverrideFor(carrierId, name, context.resources)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt
index a02dd3490341..42b874fd7156 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt
@@ -37,6 +37,13 @@ import javax.inject.Inject
* own [Configuration] and track resources based on the full set of available mcc-mnc combinations.
*
* (for future reference: b/240555502 is the initiating bug for this)
+ *
+ * NOTE: MCC/MNC qualifiers are not sufficient to fully describe a network type icon qualified by
+ * network type + carrier ID. This class exists to keep the legacy behavior of using the MCC/MNC
+ * resource qualifiers working, but if a carrier-specific icon is requested, then the override
+ * provided by [MobileIconCarrierIdOverrides] will take precedence.
+ *
+ * TODO(b/258503704): consider removing this class in favor of the `carrierId` overrides
*/
@SysUISingleton
class MobileContextProvider
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index eacb18e3c50c..14d0d7e032d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -300,7 +300,7 @@ public interface CentralSurfacesDependenciesModule {
@Override
public boolean isShowingAlternateAuthOnUnlock() {
- return statusBarKeyguardViewManager.get().shouldShowAltAuth();
+ return statusBarKeyguardViewManager.get().canShowAlternateBouncer();
}
};
return new DialogLaunchAnimator(callback, interactionJankMonitor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index ff6389141575..df2de560b5b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -34,6 +34,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeEventsModule;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
@@ -160,6 +161,7 @@ public interface NotificationsModule {
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
+ ShadeExpansionStateManager shadeExpansionStateManager,
NotificationLogger.ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
return new NotificationLogger(
@@ -169,6 +171,7 @@ public interface NotificationsModule {
visibilityProvider,
notifPipeline,
statusBarStateController,
+ shadeExpansionStateManager,
expansionStateLogger,
notificationPanelLogger);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 639187790ae0..58f59be1db6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -36,6 +36,7 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
@@ -92,25 +93,6 @@ public class NotificationLogger implements StateListener {
private Boolean mPanelExpanded = null; // Use null to indicate state is not yet known
private boolean mLogging = false;
- protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
- new OnChildLocationsChangedListener() {
- @Override
- public void onChildLocationsChanged() {
- if (mHandler.hasCallbacks(mVisibilityReporter)) {
- // Visibilities will be reported when the existing
- // callback is executed.
- return;
- }
- // Calculate when we're allowed to run the visibility
- // reporter. Note that this timestamp might already have
- // passed. That's OK, the callback will just be executed
- // ASAP.
- long nextReportUptimeMs =
- mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
- mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
- }
- };
-
// Tracks notifications currently visible in mNotificationStackScroller and
// emits visibility events via NoMan on changes.
protected Runnable mVisibilityReporter = new Runnable() {
@@ -219,6 +201,7 @@ public class NotificationLogger implements StateListener {
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
+ ShadeExpansionStateManager shadeExpansionStateManager,
ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
mNotificationListener = notificationListener;
@@ -232,6 +215,7 @@ public class NotificationLogger implements StateListener {
mNotificationPanelLogger = notificationPanelLogger;
// Not expected to be destroyed, don't need to unsubscribe
statusBarStateController.addCallback(this);
+ shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
registerNewPipelineListener();
}
@@ -278,14 +262,14 @@ public class NotificationLogger implements StateListener {
if (DEBUG) {
Log.i(TAG, "startNotificationLogging");
}
- mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
+ mListContainer.setChildLocationsChangedListener(this::onChildLocationsChanged);
// Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
// cause the scroller to emit child location events. Hence generate
// one ourselves to guarantee that we're reporting visible
// notifications.
// (Note that in cases where the scroller does emit events, this
// additional event doesn't break anything.)
- mNotificationLocationsChangedListener.onChildLocationsChanged();
+ onChildLocationsChanged();
}
}
@@ -411,21 +395,6 @@ public class NotificationLogger implements StateListener {
}
/**
- * Called by CentralSurfaces to notify the logger that the panel expansion has changed.
- * The panel may be showing any of the normal notification panel, the AOD, or the bouncer.
- * @param isExpanded True if the panel is expanded.
- */
- public void onPanelExpandedChanged(boolean isExpanded) {
- if (DEBUG) {
- Log.i(TAG, "onPanelExpandedChanged: new=" + isExpanded);
- }
- mPanelExpanded = isExpanded;
- synchronized (mDozingLock) {
- maybeUpdateLoggingStatus();
- }
- }
-
- /**
* Called when the notification is expanded / collapsed.
*/
public void onExpansionChanged(String key, boolean isUserAction, boolean isExpanded) {
@@ -434,6 +403,36 @@ public class NotificationLogger implements StateListener {
}
@VisibleForTesting
+ void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ // mPanelExpanded is initialized as null
+ if (mPanelExpanded == null || !mPanelExpanded.equals(isExpanded)) {
+ if (DEBUG) {
+ Log.i(TAG, "onPanelExpandedChanged: new=" + isExpanded);
+ }
+ mPanelExpanded = isExpanded;
+ synchronized (mDozingLock) {
+ maybeUpdateLoggingStatus();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void onChildLocationsChanged() {
+ if (mHandler.hasCallbacks(mVisibilityReporter)) {
+ // Visibilities will be reported when the existing
+ // callback is executed.
+ return;
+ }
+ // Calculate when we're allowed to run the visibility
+ // reporter. Note that this timestamp might already have
+ // passed. That's OK, the callback will just be executed
+ // ASAP.
+ long nextReportUptimeMs =
+ mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
+ mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
+ }
+
+ @VisibleForTesting
public void setVisibilityReporter(Runnable visibilityReporter) {
mVisibilityReporter = visibilityReporter;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index de158c4d017e..b93e1500e570 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -135,6 +135,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private static final String TAG = "ExpandableNotifRow";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG_ONMEASURE =
+ Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
private static final int COLORED_DIVIDER_ALPHA = 0x7B;
private static final int MENU_VIEW_INDEX = 0;
@@ -1511,7 +1513,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
l.setAlpha(alpha);
}
if (mChildrenContainer != null) {
- mChildrenContainer.setAlpha(alpha);
+ mChildrenContainer.setContentAlpha(alpha);
}
}
@@ -1724,6 +1726,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Trace.beginSection(appendTraceStyleTag("ExpNotRow#onMeasure"));
+ if (DEBUG_ONMEASURE) {
+ Log.d(TAG, "onMeasure("
+ + "widthMeasureSpec=" + MeasureSpec.toString(widthMeasureSpec) + ", "
+ + "heightMeasureSpec=" + MeasureSpec.toString(heightMeasureSpec) + ")");
+ }
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index b2628e40e77e..6f4d6d944033 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -719,7 +719,7 @@ public class AmbientState implements Dumpable {
*/
public boolean isBouncerInTransit() {
return mStatusBarKeyguardViewManager != null
- && mStatusBarKeyguardViewManager.isBouncerInTransit();
+ && mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 0554fb5b3689..d43ca823089f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -25,6 +25,7 @@ import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.drawable.ColorDrawable;
+import android.os.Trace;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.Log;
@@ -219,6 +220,7 @@ public class NotificationChildrenContainer extends ViewGroup
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("NotificationChildrenContainer#onMeasure");
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
@@ -267,6 +269,7 @@ public class NotificationChildrenContainer extends ViewGroup
}
setMeasuredDimension(width, height);
+ Trace.endSection();
}
@Override
@@ -495,6 +498,20 @@ public class NotificationChildrenContainer extends ViewGroup
}
/**
+ * Sets the alpha on the content, while leaving the background of the container itself as is.
+ *
+ * @param alpha alpha value to apply to the content
+ */
+ public void setContentAlpha(float alpha) {
+ for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
+ mNotificationHeader.getChildAt(i).setAlpha(alpha);
+ }
+ for (ExpandableNotificationRow child : getAttachedChildren()) {
+ child.setContentAlpha(alpha);
+ }
+ }
+
+ /**
* To be called any time the rows have been updated
*/
public void updateExpansionStates() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 2c3330e12229..41dbf1d6dc60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
+import static android.os.Trace.TRACE_TAG_ALWAYS;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
@@ -44,6 +46,7 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Trace;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
@@ -1074,6 +1077,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("NotificationStackScrollLayout#onMeasure");
+ if (SPEW) {
+ Log.d(TAG, "onMeasure("
+ + "widthMeasureSpec=" + MeasureSpec.toString(widthMeasureSpec) + ", "
+ + "heightMeasureSpec=" + MeasureSpec.toString(heightMeasureSpec) + ")");
+ }
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
@@ -1090,6 +1099,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
for (int i = 0; i < size; i++) {
measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
}
+ Trace.endSection();
+ }
+
+ @Override
+ public void requestLayout() {
+ Trace.instant(TRACE_TAG_ALWAYS, "NotificationStackScrollLayout#requestLayout");
+ super.requestLayout();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 6e68079706f4..f72f1bcdcb1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -480,7 +480,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
break;
case MODE_SHOW_BOUNCER:
Trace.beginSection("MODE_SHOW_BOUNCER");
- mKeyguardViewController.showBouncer(true);
+ mKeyguardViewController.showPrimaryBouncer(true);
Trace.endSection();
break;
case MODE_WAKE_AND_UNLOCK_FROM_DREAM:
@@ -560,7 +560,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
}
if (mKeyguardStateController.isShowing()) {
- if (mKeyguardViewController.bouncerIsOrWillBeShowing() && unlockingAllowed) {
+ if (mKeyguardViewController.primaryBouncerIsOrWillBeShowing() && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed) {
return MODE_UNLOCK_COLLAPSING;
@@ -603,7 +603,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
return MODE_UNLOCK_COLLAPSING;
}
if (mKeyguardStateController.isShowing()) {
- if ((mKeyguardViewController.bouncerIsOrWillBeShowing()
+ if ((mKeyguardViewController.primaryBouncerIsOrWillBeShowing()
|| mKeyguardBypassController.getAltBouncerShowing()) && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed && (bypass || mAuthController.isUdfpsFingerDown())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index dc370821dc95..bd0678fb5988 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -258,8 +258,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void onKeyguardViewManagerStatesUpdated();
- void setPanelExpanded(boolean isExpanded);
-
ViewGroup getNotificationScrollLayout();
boolean isPulsing();
@@ -456,7 +454,11 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void setTransitionToFullShadeProgress(float transitionToFullShadeProgress);
- void setBouncerHiddenFraction(float expansion);
+ /**
+ * Sets the amount of progress to the bouncer being fully hidden/visible. 1 means the bouncer
+ * is fully hidden, while 0 means the bouncer is visible.
+ */
+ void setPrimaryBouncerHiddenFraction(float expansion);
@VisibleForTesting
void updateScrimController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 1e95dad6b115..9e5a66f1e306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -55,6 +55,7 @@ import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
@@ -71,6 +72,8 @@ import java.util.Optional;
import javax.inject.Inject;
+import dagger.Lazy;
+
/** */
@CentralSurfacesComponent.CentralSurfacesScope
public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callbacks {
@@ -99,6 +102,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
private final boolean mVibrateOnOpening;
private final VibrationEffect mCameraLaunchGestureVibrationEffect;
private final SystemBarAttributesListener mSystemBarAttributesListener;
+ private final Lazy<CameraLauncher> mCameraLauncherLazy;
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@@ -128,8 +132,8 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
Optional<Vibrator> vibratorOptional,
DisableFlagsLogger disableFlagsLogger,
@DisplayId int displayId,
- SystemBarAttributesListener systemBarAttributesListener) {
-
+ SystemBarAttributesListener systemBarAttributesListener,
+ Lazy<CameraLauncher> cameraLauncherLazy) {
mCentralSurfaces = centralSurfaces;
mContext = context;
mShadeController = shadeController;
@@ -152,6 +156,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
mVibratorOptional = vibratorOptional;
mDisableFlagsLogger = disableFlagsLogger;
mDisplayId = displayId;
+ mCameraLauncherLazy = cameraLauncherLazy;
mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
@@ -346,7 +351,8 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
mCentralSurfaces.setLaunchCameraOnFinishedGoingToSleep(true);
return;
}
- if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
+ if (!mCameraLauncherLazy.get().canCameraGestureBeLaunched(
+ mNotificationPanelViewController.getBarState())) {
if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
Slog.d(CentralSurfaces.TAG, "Can't launch camera right now");
}
@@ -383,7 +389,8 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.reset(true /* hide */);
}
- mNotificationPanelViewController.launchCamera(source);
+ mCameraLauncherLazy.get().launchCamera(source,
+ mNotificationPanelViewController.isFullyCollapsed());
mCentralSurfaces.updateScrimController();
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 0ad72ab77bf9..ab76c65e98de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -174,9 +174,9 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.ripple.RippleShader.RippleShape;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.brightness.BrightnessSliderController;
+import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
@@ -233,6 +233,7 @@ import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
+import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -485,6 +486,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final PluginManager mPluginManager;
private final ShadeController mShadeController;
private final InitController mInitController;
+ private final Lazy<CameraLauncher> mCameraLauncherLazy;
private final PluginDependencyProvider mPluginDependencyProvider;
private final KeyguardDismissUtil mKeyguardDismissUtil;
@@ -617,6 +619,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private Runnable mLaunchTransitionEndRunnable;
private Runnable mLaunchTransitionCancelRunnable;
+ private boolean mLaunchingAffordance;
private boolean mLaunchCameraWhenFinishedWaking;
private boolean mLaunchCameraOnFinishedGoingToSleep;
private boolean mLaunchEmergencyActionWhenFinishedWaking;
@@ -761,7 +764,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
InteractionJankMonitor jankMonitor,
DeviceStateManager deviceStateManager,
WiredChargingRippleController wiredChargingRippleController,
- IDreamManager dreamManager) {
+ IDreamManager dreamManager,
+ Lazy<CameraLauncher> cameraLauncherLazy) {
mContext = context;
mNotificationsController = notificationsController;
mFragmentService = fragmentService;
@@ -838,6 +842,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mMessageRouter = messageRouter;
mWallpaperManager = wallpaperManager;
mJankMonitor = jankMonitor;
+ mCameraLauncherLazy = cameraLauncherLazy;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -848,6 +853,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mScreenOffAnimationController = screenOffAnimationController;
mShadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged);
+ mShadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
mBubbleExpandListener = (isExpanding, key) ->
mContext.getMainExecutor().execute(this::updateScrimController);
@@ -1376,6 +1382,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private void onPanelExpansionChanged(ShadeExpansionChangeEvent event) {
float fraction = event.getFraction();
boolean tracking = event.getTracking();
+ boolean isExpanded = event.getExpanded();
dispatchPanelExpansionForKeyguardDismiss(fraction, tracking);
if (fraction == 0 || fraction == 1) {
@@ -1388,6 +1395,23 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
}
+ @VisibleForTesting
+ void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ if (mPanelExpanded != isExpanded) {
+ mPanelExpanded = isExpanded;
+ if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
+ if (DEBUG) {
+ Log.v(TAG, "clearing notification effects from Height");
+ }
+ clearNotificationEffects();
+ }
+
+ if (!isExpanded) {
+ mRemoteInputManager.onPanelCollapsed();
+ }
+ }
+ }
+
@NonNull
@Override
public Lifecycle getLifecycle() {
@@ -1793,27 +1817,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
@Override
- public void setPanelExpanded(boolean isExpanded) {
- if (mPanelExpanded != isExpanded) {
- mNotificationLogger.onPanelExpandedChanged(isExpanded);
- }
- mPanelExpanded = isExpanded;
- mStatusBarHideIconsForBouncerManager.setPanelExpandedAndTriggerUpdate(isExpanded);
- mNotificationShadeWindowController.setPanelExpanded(isExpanded);
- mStatusBarStateController.setPanelExpanded(isExpanded);
- if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
- if (DEBUG) {
- Log.v(TAG, "clearing notification effects from Height");
- }
- clearNotificationEffects();
- }
-
- if (!isExpanded) {
- mRemoteInputManager.onPanelCollapsed();
- }
- }
-
- @Override
public ViewGroup getNotificationScrollLayout() {
return mStackScroller;
}
@@ -2267,13 +2270,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
pw.println(" Panels: ");
- if (mNotificationPanelViewController != null) {
- pw.println(" mNotificationPanel="
- + mNotificationPanelViewController.getView() + " params="
- + mNotificationPanelViewController.getView().getLayoutParams().debug(""));
- pw.print (" ");
- mNotificationPanelViewController.dump(pw, args);
- }
pw.println(" mStackScroller: " + mStackScroller + " (dump moved)");
pw.println(" Theme:");
String nightMode = mUiModeManager == null ? "null" : mUiModeManager.getNightMode() + "";
@@ -2978,7 +2974,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private void onLaunchTransitionFadingEnded() {
mNotificationPanelViewController.resetAlpha();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mCameraLauncherLazy.get().setLaunchingAffordance(false);
releaseGestureWakeLock();
runLaunchTransitionEndRunnable();
mKeyguardStateController.setLaunchTransitionFadingAway(false);
@@ -3048,7 +3044,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private void onLaunchTransitionTimeout() {
Log.w(TAG, "Launch transition: Timeout!");
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mCameraLauncherLazy.get().setLaunchingAffordance(false);
releaseGestureWakeLock();
mNotificationPanelViewController.resetViews(false /* animate */);
}
@@ -3101,7 +3097,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
releaseGestureWakeLock();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mCameraLauncherLazy.get().setLaunchingAffordance(false);
mNotificationPanelViewController.resetAlpha();
mNotificationPanelViewController.resetTranslation();
mNotificationPanelViewController.resetViewGroupFade();
@@ -3259,7 +3255,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void endAffordanceLaunch() {
releaseGestureWakeLock();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mCameraLauncherLazy.get().setLaunchingAffordance(false);
}
/**
@@ -3322,8 +3318,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// lock screen where users can use the UDFPS affordance to enter the device
mStatusBarKeyguardViewManager.reset(true);
} else if (mState == StatusBarState.KEYGUARD
- && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()) {
- mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
+ && !mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()) {
+ mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
}
}
}
@@ -3531,7 +3527,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedGoingToSleep() {
- mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mCameraLauncherLazy.get().setLaunchingAffordance(false);
releaseGestureWakeLock();
mLaunchCameraWhenFinishedWaking = false;
mDeviceInteractive = false;
@@ -3632,7 +3628,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
.updateSensitivenessForOccludedWakeup();
}
if (mLaunchCameraWhenFinishedWaking) {
- mNotificationPanelViewController.launchCamera(mLastCameraLaunchSource);
+ mCameraLauncherLazy.get().launchCamera(mLastCameraLaunchSource,
+ mNotificationPanelViewController.isFullyCollapsed());
mLaunchCameraWhenFinishedWaking = false;
}
if (mLaunchEmergencyActionWhenFinishedWaking) {
@@ -3805,7 +3802,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
* is fully hidden, while 0 means the bouncer is visible.
*/
@Override
- public void setBouncerHiddenFraction(float expansion) {
+ public void setPrimaryBouncerHiddenFraction(float expansion) {
mScrimController.setBouncerHiddenFraction(expansion);
}
@@ -3823,11 +3820,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mScrimController.setExpansionAffectsAlpha(!unlocking);
- boolean launchingAffordanceWithPreview =
- mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
+ boolean launchingAffordanceWithPreview = mLaunchingAffordance;
mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateBouncer()) {
if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
|| mTransitionToFullShadeProgress > 0f) {
mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
@@ -3838,7 +3834,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
// Bouncer needs the front scrim when it's on top of an activity,
// tapping on a notification, editing QS or being dismissed by
// FLAG_DISMISS_KEYGUARD_ACTIVITY.
- ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
+ ScrimState state = mStatusBarKeyguardViewManager.primaryBouncerNeedsScrimming()
? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
mScrimController.transitionTo(state);
} else if (launchingAffordanceWithPreview) {
@@ -4147,7 +4143,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
*/
@Override
public boolean isBouncerShowingScrimmed() {
- return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming();
+ return isBouncerShowing() && mStatusBarKeyguardViewManager.primaryBouncerNeedsScrimming();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 38c3f93dc6ad..dcf53271db11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -34,6 +34,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
@@ -111,7 +112,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
ConfigurationController configurationController,
@Main Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ ShadeExpansionStateManager shadeExpansionStateManager) {
super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
Resources resources = mContext.getResources();
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
@@ -132,6 +134,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
updateResources();
}
});
+
+ shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
}
public void setAnimationStateHandler(AnimationStateHandler handler) {
@@ -220,13 +224,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
mTrackingHeadsUp = trackingHeadsUp;
}
- /**
- * Notify that the status bar panel gets expanded or collapsed.
- *
- * @param isExpanded True to notify expanded, false to notify collapsed.
- * TODO(b/237811427) replace with a listener
- */
- public void setIsPanelExpanded(boolean isExpanded) {
+ private void onShadeExpansionFullyChanged(Boolean isExpanded) {
if (isExpanded != mIsExpanded) {
mIsExpanded = isExpanded;
if (isExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
index 4897c529dd51..78b28d203629 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -23,6 +23,8 @@ import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import android.widget.FrameLayout
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.LockIconViewController
import com.android.systemui.R
import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind
@@ -51,13 +53,20 @@ constructor(
private var ambientIndicationArea: View? = null
private lateinit var binding: KeyguardBottomAreaViewBinder.Binding
+ private lateinit var lockIconViewController: LockIconViewController
/** Initializes the view. */
fun init(
viewModel: KeyguardBottomAreaViewModel,
falsingManager: FalsingManager,
+ lockIconViewController: LockIconViewController,
) {
- binding = bind(this, viewModel, falsingManager)
+ binding = bind(
+ this,
+ viewModel,
+ falsingManager,
+ )
+ this.lockIconViewController = lockIconViewController
}
/**
@@ -114,4 +123,29 @@ constructor(
}
return insets
}
+
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ super.onLayout(changed, left, top, right, bottom)
+ findViewById<View>(R.id.ambient_indication_container)?.let {
+ val (ambientLeft, ambientTop) = it.locationOnScreen
+ if (binding.shouldConstrainToTopOfLockIcon()) {
+ //make top of ambient indication view the bottom of the lock icon
+ it.layout(
+ ambientLeft,
+ lockIconViewController.bottom.toInt(),
+ right - ambientLeft,
+ ambientTop + it.measuredHeight
+ )
+ } else {
+ //make bottom of ambient indication view the top of the lock icon
+ val lockLocationTop = lockIconViewController.top
+ it.layout(
+ ambientLeft,
+ lockLocationTop.toInt() - it.measuredHeight,
+ right - ambientLeft,
+ lockLocationTop.toInt()
+ )
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index b2a9509a03b7..5f8da4161092 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -55,13 +55,13 @@ import java.util.List;
import javax.inject.Inject;
/**
- * A class which manages the bouncer on the lockscreen.
+ * A class which manages the primary (pin/pattern/password) bouncer on the lockscreen.
* @deprecated Use KeyguardBouncerRepository
*/
@Deprecated
public class KeyguardBouncer {
- private static final String TAG = "KeyguardBouncer";
+ private static final String TAG = "PrimaryKeyguardBouncer";
static final long BOUNCER_FACE_DELAY = 1200;
public static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
/**
@@ -78,7 +78,7 @@ public class KeyguardBouncer {
private final FalsingCollector mFalsingCollector;
private final DismissCallbackRegistry mDismissCallbackRegistry;
private final Handler mHandler;
- private final List<BouncerExpansionCallback> mExpansionCallbacks = new ArrayList<>();
+ private final List<PrimaryBouncerExpansionCallback> mExpansionCallbacks = new ArrayList<>();
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardStateController mKeyguardStateController;
private final KeyguardSecurityModel mKeyguardSecurityModel;
@@ -126,7 +126,7 @@ public class KeyguardBouncer {
private KeyguardBouncer(Context context, ViewMediatorCallback callback,
ViewGroup container,
DismissCallbackRegistry dismissCallbackRegistry, FalsingCollector falsingCollector,
- BouncerExpansionCallback expansionCallback,
+ PrimaryBouncerExpansionCallback expansionCallback,
KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController keyguardBypassController, @Main Handler handler,
@@ -279,10 +279,7 @@ public class KeyguardBouncer {
* @see #onFullyShown()
*/
private void onFullyHidden() {
- cancelShowRunnable();
- setVisibility(View.INVISIBLE);
- mFalsingCollector.onBouncerHidden();
- DejankUtils.postAfterTraversal(mResetRunnable);
+
}
private void setVisibility(@View.Visibility int visibility) {
@@ -459,7 +456,13 @@ public class KeyguardBouncer {
onFullyShown();
dispatchFullyShown();
} else if (fraction == EXPANSION_HIDDEN && oldExpansion != EXPANSION_HIDDEN) {
- onFullyHidden();
+ DejankUtils.postAfterTraversal(mResetRunnable);
+ /*
+ * There are cases where #hide() was not invoked, such as when
+ * NotificationPanelViewController controls the hide animation. Make sure the state gets
+ * updated by calling #hide() directly.
+ */
+ hide(false /* destroyView */);
dispatchFullyHidden();
} else if (fraction != EXPANSION_VISIBLE && oldExpansion == EXPANSION_VISIBLE) {
dispatchStartingToHide();
@@ -571,37 +574,37 @@ public class KeyguardBouncer {
}
private void dispatchFullyShown() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onFullyShown();
}
}
private void dispatchStartingToHide() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onStartingToHide();
}
}
private void dispatchStartingToShow() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onStartingToShow();
}
}
private void dispatchFullyHidden() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onFullyHidden();
}
}
private void dispatchExpansionChanged() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onExpansionChanged(mExpansion);
}
}
private void dispatchVisibilityChanged() {
- for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ for (PrimaryBouncerExpansionCallback callback : mExpansionCallbacks) {
callback.onVisibilityChanged(mContainer.getVisibility() == View.VISIBLE);
}
}
@@ -647,7 +650,7 @@ public class KeyguardBouncer {
/**
* Adds a callback to listen to bouncer expansion updates.
*/
- public void addBouncerExpansionCallback(BouncerExpansionCallback callback) {
+ public void addBouncerExpansionCallback(PrimaryBouncerExpansionCallback callback) {
if (!mExpansionCallbacks.contains(callback)) {
mExpansionCallbacks.add(callback);
}
@@ -657,11 +660,14 @@ public class KeyguardBouncer {
* Removes a previously added callback. If the callback was never added, this methood
* does nothing.
*/
- public void removeBouncerExpansionCallback(BouncerExpansionCallback callback) {
+ public void removeBouncerExpansionCallback(PrimaryBouncerExpansionCallback callback) {
mExpansionCallbacks.remove(callback);
}
- public interface BouncerExpansionCallback {
+ /**
+ * Callback updated when the primary bouncer's show and hide states change.
+ */
+ public interface PrimaryBouncerExpansionCallback {
/**
* Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_VISIBLE}.
* This is NOT called each time the bouncer is shown, but rather only when the fully
@@ -745,7 +751,7 @@ public class KeyguardBouncer {
* Construct a KeyguardBouncer that will exist in the given container.
*/
public KeyguardBouncer create(ViewGroup container,
- BouncerExpansionCallback expansionCallback) {
+ PrimaryBouncerExpansionCallback expansionCallback) {
return new KeyguardBouncer(mContext, mCallback, container,
mDismissCallbackRegistry, mFalsingCollector, expansionCallback,
mKeyguardStateController, mKeyguardUpdateMonitor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 18877f9fb437..7a49a495155b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -26,6 +26,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.TypedValue;
@@ -527,4 +528,11 @@ public class KeyguardStatusBarView extends RelativeLayout {
mClipRect.set(0, mTopClipping, getWidth(), getHeight());
setClipBounds(mClipRect);
}
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Trace.beginSection("KeyguardStatusBarView#onMeasure");
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ Trace.endSection();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e744c7932c47..c527f30c424c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -53,7 +53,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -205,7 +204,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private final ScreenOffAnimationController mScreenOffAnimationController;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private KeyguardViewMediator mKeyguardViewMediator;
private GradientColors mColors;
private boolean mNeedsDrawableColorUpdate;
@@ -275,8 +273,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
@Main Executor mainExecutor,
ScreenOffAnimationController screenOffAnimationController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- KeyguardViewMediator keyguardViewMediator) {
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
mScrimStateListener = lightBarController::setScrimState;
mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
@@ -315,8 +312,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
});
mColors = new GradientColors();
-
- mKeyguardViewMediator = keyguardViewMediator;
}
/**
@@ -812,13 +807,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mBehindTint,
interpolatedFraction);
}
-
- // If we're unlocked but still playing the occlude animation, remain at the keyguard
- // alpha temporarily.
- if (mKeyguardViewMediator.isOccludeAnimationPlaying()
- || mState.mLaunchingAffordanceWithPreview) {
- mNotificationsAlpha = KEYGUARD_SCRIM_ALPHA;
- }
} else if (mState == ScrimState.AUTH_SCRIMMED_SHADE) {
mNotificationsAlpha = (float) Math.pow(getInterpolatedFraction(), 0.8f);
} else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED
@@ -895,7 +883,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha();
float behindAlpha;
- int behindTint;
+ int behindTint = state.getBehindTint();
if (mDarkenWhileDragging) {
behindAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind,
interpolatedFract);
@@ -903,17 +891,19 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
behindAlpha = MathUtils.lerp(0 /* start */, stateBehind,
interpolatedFract);
}
- if (mClipsQsScrim) {
- behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(),
+ if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
+ if (mClipsQsScrim) {
+ behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(),
state.getNotifTint(), interpolatedFract);
- } else {
- behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
+ } else {
+ behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
state.getBehindTint(), interpolatedFract);
+ }
}
if (mQsExpansion > 0) {
behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion);
float tintProgress = mQsExpansion;
- if (mStatusBarKeyguardViewManager.isBouncerInTransit()) {
+ if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
// this is case of - on lockscreen - going from expanded QS to bouncer.
// Because mQsExpansion is already interpolated and transition between tints
// is too slow, we want to speed it up and make it more aligned to bouncer
@@ -1096,7 +1086,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
private float getInterpolatedFraction() {
- if (mStatusBarKeyguardViewManager.isBouncerInTransit()) {
+ if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
return BouncerPanelExpansionCalculator
.aboutToShowBouncerProgress(mPanelExpansionFraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
index 51131914363d..4d9de09fded4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
@@ -5,6 +5,7 @@ import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -24,10 +25,11 @@ import javax.inject.Inject
*/
@SysUISingleton
class StatusBarHideIconsForBouncerManager @Inject constructor(
- private val commandQueue: CommandQueue,
- @Main private val mainExecutor: DelayableExecutor,
- statusBarWindowStateController: StatusBarWindowStateController,
- dumpManager: DumpManager
+ private val commandQueue: CommandQueue,
+ @Main private val mainExecutor: DelayableExecutor,
+ statusBarWindowStateController: StatusBarWindowStateController,
+ shadeExpansionStateManager: ShadeExpansionStateManager,
+ dumpManager: DumpManager
) : Dumpable {
// State variables set by external classes.
private var panelExpanded: Boolean = false
@@ -47,6 +49,12 @@ class StatusBarHideIconsForBouncerManager @Inject constructor(
statusBarWindowStateController.addListener {
state -> setStatusBarStateAndTriggerUpdate(state)
}
+ shadeExpansionStateManager.addFullExpansionListener { isExpanded ->
+ if (panelExpanded != isExpanded) {
+ panelExpanded = isExpanded
+ updateHideIconsForBouncer(animate = false)
+ }
+ }
}
/** Returns true if the status bar icons should be hidden in the bouncer. */
@@ -63,11 +71,6 @@ class StatusBarHideIconsForBouncerManager @Inject constructor(
this.displayId = displayId
}
- fun setPanelExpandedAndTriggerUpdate(panelExpanded: Boolean) {
- this.panelExpanded = panelExpanded
- updateHideIconsForBouncer(animate = false)
- }
-
fun setIsOccludedAndTriggerUpdate(isOccluded: Boolean) {
this.isOccluded = isOccluded
updateHideIconsForBouncer(animate = false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 9e2821806693..01a1ebe7fd68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -60,8 +60,8 @@ import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.BouncerView;
-import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -78,7 +78,7 @@ import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
-import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
+import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.FoldAodAnimationController;
@@ -135,62 +135,64 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Nullable
private final FoldAodAnimationController mFoldAodAnimationController;
private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
- private final BouncerCallbackInteractor mBouncerCallbackInteractor;
- private final BouncerInteractor mBouncerInteractor;
- private final BouncerView mBouncerView;
+ private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
+ private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+ private final BouncerView mPrimaryBouncerView;
private final Lazy<com.android.systemui.shade.ShadeController> mShadeController;
- private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
- private boolean mBouncerAnimating;
+ private final PrimaryBouncerExpansionCallback mExpansionCallback =
+ new PrimaryBouncerExpansionCallback() {
+ private boolean mPrimaryBouncerAnimating;
- @Override
- public void onFullyShown() {
- mBouncerAnimating = false;
- updateStates();
- }
+ @Override
+ public void onFullyShown() {
+ mPrimaryBouncerAnimating = false;
+ updateStates();
+ }
- @Override
- public void onStartingToHide() {
- mBouncerAnimating = true;
- updateStates();
- }
+ @Override
+ public void onStartingToHide() {
+ mPrimaryBouncerAnimating = true;
+ updateStates();
+ }
- @Override
- public void onStartingToShow() {
- mBouncerAnimating = true;
- updateStates();
- }
+ @Override
+ public void onStartingToShow() {
+ mPrimaryBouncerAnimating = true;
+ updateStates();
+ }
- @Override
- public void onFullyHidden() {
- mBouncerAnimating = false;
- }
+ @Override
+ public void onFullyHidden() {
+ mPrimaryBouncerAnimating = false;
+ updateStates();
+ }
- @Override
- public void onExpansionChanged(float expansion) {
- if (mBouncerAnimating) {
- mCentralSurfaces.setBouncerHiddenFraction(expansion);
+ @Override
+ public void onExpansionChanged(float expansion) {
+ if (mPrimaryBouncerAnimating) {
+ mCentralSurfaces.setPrimaryBouncerHiddenFraction(expansion);
+ }
}
- }
- @Override
- public void onVisibilityChanged(boolean isVisible) {
- mCentralSurfaces
- .setBouncerShowingOverDream(
- isVisible && mDreamOverlayStateController.isOverlayActive());
+ @Override
+ public void onVisibilityChanged(boolean isVisible) {
+ mCentralSurfaces.setBouncerShowingOverDream(
+ isVisible && mDreamOverlayStateController.isOverlayActive());
- if (!isVisible) {
- mCentralSurfaces.setBouncerHiddenFraction(KeyguardBouncer.EXPANSION_HIDDEN);
- }
+ if (!isVisible) {
+ mCentralSurfaces.setPrimaryBouncerHiddenFraction(
+ KeyguardBouncer.EXPANSION_HIDDEN);
+ }
- /* Register predictive back callback when keyguard becomes visible, and unregister
- when it's hidden. */
- if (isVisible) {
- registerBackCallback();
- } else {
- unregisterBackCallback();
+ /* Register predictive back callback when keyguard becomes visible, and unregister
+ when it's hidden. */
+ if (isVisible) {
+ registerBackCallback();
+ } else {
+ unregisterBackCallback();
+ }
}
- }
};
private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
@@ -223,7 +225,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private View mNotificationContainer;
- @Nullable protected KeyguardBouncer mBouncer;
+ @Nullable protected KeyguardBouncer mPrimaryBouncer;
protected boolean mRemoteInputActive;
private boolean mGlobalActionsVisible = false;
private boolean mLastGlobalActionsVisible = false;
@@ -236,8 +238,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
protected boolean mFirstUpdate = true;
protected boolean mLastShowing;
protected boolean mLastOccluded;
- private boolean mLastBouncerShowing;
- private boolean mLastBouncerIsOrWillBeShowing;
+ private boolean mLastPrimaryBouncerShowing;
+ private boolean mLastPrimaryBouncerIsOrWillBeShowing;
private boolean mLastBouncerDismissible;
protected boolean mLastRemoteInputActive;
private boolean mLastDozing;
@@ -265,7 +267,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final LatencyTracker mLatencyTracker;
private final KeyguardSecurityModel mKeyguardSecurityModel;
private KeyguardBypassController mBypassController;
- @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor;
+ @Nullable private AlternateBouncer mAlternateBouncer;
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@@ -300,9 +302,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
LatencyTracker latencyTracker,
KeyguardSecurityModel keyguardSecurityModel,
FeatureFlags featureFlags,
- BouncerCallbackInteractor bouncerCallbackInteractor,
- BouncerInteractor bouncerInteractor,
- BouncerView bouncerView) {
+ PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor,
+ PrimaryBouncerInteractor primaryBouncerInteractor,
+ BouncerView primaryBouncerView) {
mContext = context;
mViewMediatorCallback = callback;
mLockPatternUtils = lockPatternUtils;
@@ -320,9 +322,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mShadeController = shadeController;
mLatencyTracker = latencyTracker;
mKeyguardSecurityModel = keyguardSecurityModel;
- mBouncerCallbackInteractor = bouncerCallbackInteractor;
- mBouncerInteractor = bouncerInteractor;
- mBouncerView = bouncerView;
+ mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor;
+ mPrimaryBouncerInteractor = primaryBouncerInteractor;
+ mPrimaryBouncerView = primaryBouncerView;
mFoldAodAnimationController = sysUIUnfoldComponent
.map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER);
@@ -340,9 +342,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
ViewGroup container = mCentralSurfaces.getBouncerContainer();
if (mIsModernBouncerEnabled) {
- mBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
+ mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
} else {
- mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
+ mPrimaryBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
}
mNotificationPanelViewController = notificationPanelViewController;
if (shadeExpansionStateManager != null) {
@@ -361,20 +363,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* Sets the given alt auth interceptor to null if it's the current auth interceptor. Else,
* does nothing.
*/
- public void removeAlternateAuthInterceptor(@NonNull AlternateAuthInterceptor authInterceptor) {
- if (Objects.equals(mAlternateAuthInterceptor, authInterceptor)) {
- mAlternateAuthInterceptor = null;
- resetAlternateAuth(true);
+ public void removeAlternateAuthInterceptor(@NonNull AlternateBouncer authInterceptor) {
+ if (Objects.equals(mAlternateBouncer, authInterceptor)) {
+ mAlternateBouncer = null;
+ hideAlternateBouncer(true);
}
}
/**
* Sets a new alt auth interceptor.
*/
- public void setAlternateAuthInterceptor(@NonNull AlternateAuthInterceptor authInterceptor) {
- if (!Objects.equals(mAlternateAuthInterceptor, authInterceptor)) {
- mAlternateAuthInterceptor = authInterceptor;
- resetAlternateAuth(false);
+ public void setAlternateBouncer(@NonNull AlternateBouncer authInterceptor) {
+ if (!Objects.equals(mAlternateBouncer, authInterceptor)) {
+ mAlternateBouncer = authInterceptor;
+ hideAlternateBouncer(false);
}
}
@@ -458,48 +460,48 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mDozing && !mPulsing) {
return;
} else if (mNotificationPanelViewController.isUnlockHintRunning()) {
- if (mBouncer != null) {
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else {
- mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ mPrimaryBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
}
} else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
// Don't expand to the bouncer. Instead transition back to the lock screen (see
// CentralSurfaces#showBouncerOrLockScreenIfKeyguard)
return;
- } else if (bouncerNeedsScrimming()) {
- if (mBouncer != null) {
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ } else if (needsFullscreenBouncer()) {
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else {
- mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ mPrimaryBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
}
} else if (mKeyguardStateController.isShowing() && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
&& !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
&& !isUnlockCollapsing()) {
- if (mBouncer != null) {
- mBouncer.setExpansion(fraction);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setExpansion(fraction);
} else {
- mBouncerInteractor.setPanelExpansion(fraction);
+ mPrimaryBouncerInteractor.setPanelExpansion(fraction);
}
}
if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking
&& !mKeyguardStateController.canDismissLockScreen()
- && !bouncerIsShowing()
+ && !primaryBouncerIsShowing()
&& !bouncerIsAnimatingAway()) {
- if (mBouncer != null) {
- mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
} else {
- mBouncerInteractor.show(/* isScrimmed= */false);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */false);
}
}
- } else if (!mKeyguardStateController.isShowing() && isBouncerInTransit()) {
+ } else if (!mKeyguardStateController.isShowing() && isPrimaryBouncerInTransit()) {
// Keyguard is not visible anymore, but expansion animation was still running.
// We need to hide the bouncer, otherwise it will be stuck in transit.
- if (mBouncer != null) {
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else {
- mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ mPrimaryBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
}
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
@@ -543,17 +545,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (needsFullscreenBouncer() && !mDozing) {
// The keyguard might be showing (already). So we need to hide it.
mCentralSurfaces.hideKeyguard();
- if (mBouncer != null) {
- mBouncer.show(true /* resetSecuritySelection */);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.show(true /* resetSecuritySelection */);
} else {
- mBouncerInteractor.show(true);
+ mPrimaryBouncerInteractor.show(true);
}
} else {
mCentralSurfaces.showKeyguard();
if (hideBouncerWhenShowing) {
hideBouncer(false /* destroyView */);
- if (mBouncer != null) {
- mBouncer.prepare();
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.prepare();
}
}
}
@@ -561,23 +563,25 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
/**
- * If applicable, shows the alternate authentication bouncer. Else, shows the input
- * (pin/password/pattern) bouncer.
- * @param scrimmed true when the input bouncer should show scrimmed, false when the user will be
- * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
+ *
+ * If possible, shows the alternate bouncer. Else, shows the primary (pin/pattern/password)
+ * bouncer.
+ * @param scrimmed true when the primary bouncer should show scrimmed,
+ * false when the user will be dragging it and translation should be deferred
+ * {@see KeyguardBouncer#show(boolean, boolean)}
*/
- public void showGenericBouncer(boolean scrimmed) {
- if (shouldShowAltAuth()) {
- updateAlternateAuthShowing(mAlternateAuthInterceptor.showAlternateAuthBouncer());
+ public void showBouncer(boolean scrimmed) {
+ if (canShowAlternateBouncer()) {
+ updateAlternateBouncerShowing(mAlternateBouncer.showAlternateBouncer());
return;
}
- showBouncer(scrimmed);
+ showPrimaryBouncer(scrimmed);
}
- /** Whether we should show the alternate authentication instead of the traditional bouncer. */
- public boolean shouldShowAltAuth() {
- return mAlternateAuthInterceptor != null
+ /** Whether we can show the alternate bouncer instead of the primary bouncer. */
+ public boolean canShowAlternateBouncer() {
+ return mAlternateBouncer != null
&& mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true);
}
@@ -586,10 +590,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
@VisibleForTesting
void hideBouncer(boolean destroyView) {
- if (mBouncer != null) {
- mBouncer.hide(destroyView);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.hide(destroyView);
} else {
- mBouncerInteractor.hide();
+ mPrimaryBouncerInteractor.hide();
}
if (mKeyguardStateController.isShowing()) {
// If we were showing the bouncer and then aborting, we need to also clear out any
@@ -600,19 +604,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
/**
- * Shows the keyguard input bouncer - the password challenge on the lock screen
+ * Shows the primary bouncer - the pin/pattern/password challenge on the lock screen.
*
* @param scrimmed true when the bouncer should show scrimmed, false when the user will be
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
*/
- public void showBouncer(boolean scrimmed) {
- resetAlternateAuth(false);
+ public void showPrimaryBouncer(boolean scrimmed) {
+ hideAlternateBouncer(false);
if (mKeyguardStateController.isShowing() && !isBouncerShowing()) {
- if (mBouncer != null) {
- mBouncer.show(false /* resetSecuritySelection */, scrimmed);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.show(false /* resetSecuritySelection */, scrimmed);
} else {
- mBouncerInteractor.show(scrimmed);
+ mPrimaryBouncerInteractor.show(scrimmed);
}
}
updateStates();
@@ -644,42 +648,41 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// If there is an an alternate auth interceptor (like the UDFPS), show that one
// instead of the bouncer.
- if (shouldShowAltAuth()) {
+ if (canShowAlternateBouncer()) {
if (!afterKeyguardGone) {
- if (mBouncer != null) {
- mBouncer.setDismissAction(mAfterKeyguardGoneAction,
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setDismissAction(mAfterKeyguardGoneAction,
mKeyguardGoneCancelAction);
} else {
- mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
+ mPrimaryBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
mKeyguardGoneCancelAction);
}
mAfterKeyguardGoneAction = null;
mKeyguardGoneCancelAction = null;
}
- updateAlternateAuthShowing(
- mAlternateAuthInterceptor.showAlternateAuthBouncer());
+ updateAlternateBouncerShowing(mAlternateBouncer.showAlternateBouncer());
return;
}
if (afterKeyguardGone) {
// we'll handle the dismiss action after keyguard is gone, so just show the
// bouncer
- if (mBouncer != null) {
- mBouncer.show(false /* resetSecuritySelection */);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.show(false /* resetSecuritySelection */);
} else {
- mBouncerInteractor.show(/* isScrimmed= */true);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
}
} else {
// after authentication success, run dismiss action with the option to defer
// hiding the keyguard based on the return value of the OnDismissAction
- if (mBouncer != null) {
- mBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
mKeyguardGoneCancelAction);
} else {
- mBouncerInteractor.setDismissAction(
+ mPrimaryBouncerInteractor.setDismissAction(
mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
- mBouncerInteractor.show(/* isScrimmed= */true);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */true);
}
// bouncer will handle the dismiss action, so we no longer need to track it here
mAfterKeyguardGoneAction = null;
@@ -724,28 +727,28 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
} else {
showBouncerOrKeyguard(hideBouncerWhenShowing);
}
- resetAlternateAuth(false);
+ hideAlternateBouncer(false);
mKeyguardUpdateManager.sendKeyguardReset();
updateStates();
}
}
@Override
- public void resetAlternateAuth(boolean forceUpdateScrim) {
- final boolean updateScrim = (mAlternateAuthInterceptor != null
- && mAlternateAuthInterceptor.hideAlternateAuthBouncer())
+ public void hideAlternateBouncer(boolean forceUpdateScrim) {
+ final boolean updateScrim = (mAlternateBouncer != null
+ && mAlternateBouncer.hideAlternateBouncer())
|| forceUpdateScrim;
- updateAlternateAuthShowing(updateScrim);
+ updateAlternateBouncerShowing(updateScrim);
}
- private void updateAlternateAuthShowing(boolean updateScrim) {
- final boolean isShowingAltAuth = isShowingAlternateAuth();
+ private void updateAlternateBouncerShowing(boolean updateScrim) {
+ final boolean isShowingAlternateBouncer = isShowingAlternateBouncer();
if (mKeyguardMessageAreaController != null) {
- mKeyguardMessageAreaController.setIsVisible(isShowingAltAuth);
+ mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer);
mKeyguardMessageAreaController.setMessage("");
}
- mBypassController.setAltBouncerShowing(isShowingAltAuth);
- mKeyguardUpdateManager.setUdfpsBouncerShowing(isShowingAltAuth);
+ mBypassController.setAltBouncerShowing(isShowingAlternateBouncer);
+ mKeyguardUpdateManager.setUdfpsBouncerShowing(isShowingAlternateBouncer);
if (updateScrim) {
mCentralSurfaces.updateScrimController();
@@ -782,10 +785,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void onFinishedGoingToSleep() {
- if (mBouncer != null) {
- mBouncer.onScreenTurnedOff();
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.onScreenTurnedOff();
} else {
- mBouncerInteractor.onScreenTurnedOff();
+ mPrimaryBouncerInteractor.onScreenTurnedOff();
}
}
@@ -870,18 +873,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(isOccluding /* hideBouncerWhenShowing*/);
}
- if (animate && !isOccluded && isShowing && !bouncerIsShowing()) {
+ if (animate && !isOccluded && isShowing && !primaryBouncerIsShowing()) {
mCentralSurfaces.animateKeyguardUnoccluding();
}
}
@Override
public void startPreHideAnimation(Runnable finishRunnable) {
- if (bouncerIsShowing()) {
- if (mBouncer != null) {
- mBouncer.startPreHideAnimation(finishRunnable);
+ if (primaryBouncerIsShowing()) {
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.startPreHideAnimation(finishRunnable);
} else {
- mBouncerInteractor.startDisappearAnimation(finishRunnable);
+ mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
}
mNotificationPanelViewController.startBouncerPreHideAnimation();
@@ -994,13 +997,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
updateResources();
return;
}
- boolean wasShowing = bouncerIsShowing();
- boolean wasScrimmed = bouncerIsScrimmed();
+ boolean wasShowing = primaryBouncerIsShowing();
+ boolean wasScrimmed = primaryBouncerIsScrimmed();
hideBouncer(true /* destroyView */);
- mBouncer.prepare();
+ mPrimaryBouncer.prepare();
- if (wasShowing) showBouncer(wasScrimmed);
+ if (wasShowing) showPrimaryBouncer(wasScrimmed);
}
public void onKeyguardFadedAway() {
@@ -1045,8 +1048,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* WARNING: This method might cause Binder calls.
*/
public boolean isSecure() {
- if (mBouncer != null) {
- return mBouncer.isSecure();
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.isSecure();
}
return mKeyguardSecurityModel.getSecurityMode(
@@ -1060,7 +1063,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* @return whether a back press can be handled right now.
*/
public boolean canHandleBackPressed() {
- return bouncerIsShowing();
+ return primaryBouncerIsShowing();
}
/**
@@ -1073,7 +1076,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mCentralSurfaces.endAffordanceLaunch();
// The second condition is for SIM card locked bouncer
- if (bouncerIsScrimmed() && !needsFullscreenBouncer()) {
+ if (primaryBouncerIsScrimmed() && !needsFullscreenBouncer()) {
hideBouncer(false);
updateStates();
} else {
@@ -1094,27 +1097,27 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public boolean isBouncerShowing() {
- return bouncerIsShowing() || isShowingAlternateAuth();
+ return primaryBouncerIsShowing() || isShowingAlternateBouncer();
}
@Override
- public boolean bouncerIsOrWillBeShowing() {
- return isBouncerShowing() || isBouncerInTransit();
+ public boolean primaryBouncerIsOrWillBeShowing() {
+ return isBouncerShowing() || isPrimaryBouncerInTransit();
}
public boolean isFullscreenBouncer() {
- if (mBouncerView.getDelegate() != null) {
- return mBouncerView.getDelegate().isFullScreenBouncer();
+ if (mPrimaryBouncerView.getDelegate() != null) {
+ return mPrimaryBouncerView.getDelegate().isFullScreenBouncer();
}
- return mBouncer != null && mBouncer.isFullscreenBouncer();
+ return mPrimaryBouncer != null && mPrimaryBouncer.isFullscreenBouncer();
}
/**
* Clear out any potential actions that were saved to run when the device is unlocked
*/
public void cancelPostAuthActions() {
- if (bouncerIsOrWillBeShowing()) {
- return; // allow bouncer to trigger saved actions
+ if (primaryBouncerIsOrWillBeShowing()) {
+ return; // allow the primary bouncer to trigger saved actions
}
mAfterKeyguardGoneAction = null;
mDismissActionWillAnimateOnKeyguard = false;
@@ -1153,25 +1156,25 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
boolean showing = mKeyguardStateController.isShowing();
boolean occluded = mKeyguardStateController.isOccluded();
- boolean bouncerShowing = bouncerIsShowing();
- boolean bouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing();
- boolean bouncerDismissible = !isFullscreenBouncer();
+ boolean primaryBouncerShowing = primaryBouncerIsShowing();
+ boolean primaryBouncerIsOrWillBeShowing = primaryBouncerIsOrWillBeShowing();
+ boolean primaryBouncerDismissible = !isFullscreenBouncer();
boolean remoteInputActive = mRemoteInputActive;
- if ((bouncerDismissible || !showing || remoteInputActive) !=
- (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
+ if ((primaryBouncerDismissible || !showing || remoteInputActive)
+ != (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
|| mFirstUpdate) {
- if (bouncerDismissible || !showing || remoteInputActive) {
- if (mBouncer != null) {
- mBouncer.setBackButtonEnabled(true);
+ if (primaryBouncerDismissible || !showing || remoteInputActive) {
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setBackButtonEnabled(true);
} else {
- mBouncerInteractor.setBackButtonEnabled(true);
+ mPrimaryBouncerInteractor.setBackButtonEnabled(true);
}
} else {
- if (mBouncer != null) {
- mBouncer.setBackButtonEnabled(false);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.setBackButtonEnabled(false);
} else {
- mBouncerInteractor.setBackButtonEnabled(false);
+ mPrimaryBouncerInteractor.setBackButtonEnabled(false);
}
}
}
@@ -1182,23 +1185,26 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
updateNavigationBarVisibility(navBarVisible);
}
- if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
- mNotificationShadeWindowController.setBouncerShowing(bouncerShowing);
- mCentralSurfaces.setBouncerShowing(bouncerShowing);
+ boolean isPrimaryBouncerShowingChanged =
+ primaryBouncerShowing != mLastPrimaryBouncerShowing;
+ mLastPrimaryBouncerShowing = primaryBouncerShowing;
+
+ if (isPrimaryBouncerShowingChanged || mFirstUpdate) {
+ mNotificationShadeWindowController.setBouncerShowing(primaryBouncerShowing);
+ mCentralSurfaces.setBouncerShowing(primaryBouncerShowing);
}
- if (bouncerIsOrWillBeShowing != mLastBouncerIsOrWillBeShowing || mFirstUpdate
- || bouncerShowing != mLastBouncerShowing) {
- mKeyguardUpdateManager.sendKeyguardBouncerChanged(bouncerIsOrWillBeShowing,
- bouncerShowing);
+ if (primaryBouncerIsOrWillBeShowing != mLastPrimaryBouncerIsOrWillBeShowing || mFirstUpdate
+ || isPrimaryBouncerShowingChanged) {
+ mKeyguardUpdateManager.sendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
+ primaryBouncerShowing);
}
mFirstUpdate = false;
mLastShowing = showing;
mLastGlobalActionsVisible = mGlobalActionsVisible;
mLastOccluded = occluded;
- mLastBouncerShowing = bouncerShowing;
- mLastBouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing;
- mLastBouncerDismissible = bouncerDismissible;
+ mLastPrimaryBouncerIsOrWillBeShowing = primaryBouncerIsOrWillBeShowing;
+ mLastBouncerDismissible = primaryBouncerDismissible;
mLastRemoteInputActive = remoteInputActive;
mLastDozing = mDozing;
mLastPulsing = mPulsing;
@@ -1242,7 +1248,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|| mPulsing && !mIsDocked)
&& mGesturalNav;
return (!keyguardVisible && !hideWhileDozing && !mScreenOffAnimationPlaying
- || bouncerIsShowing()
+ || primaryBouncerIsShowing()
|| mRemoteInputActive
|| keyguardWithGestureNav
|| mGlobalActionsVisible);
@@ -1258,32 +1264,32 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
&& !mLastScreenOffAnimationPlaying || mLastPulsing && !mLastIsDocked)
&& mLastGesturalNav;
return (!keyguardShowing && !hideWhileDozing && !mLastScreenOffAnimationPlaying
- || mLastBouncerShowing || mLastRemoteInputActive || keyguardWithGestureNav
+ || mLastPrimaryBouncerShowing || mLastRemoteInputActive || keyguardWithGestureNav
|| mLastGlobalActionsVisible);
}
public boolean shouldDismissOnMenuPressed() {
- if (mBouncerView.getDelegate() != null) {
- return mBouncerView.getDelegate().shouldDismissOnMenuPressed();
+ if (mPrimaryBouncerView.getDelegate() != null) {
+ return mPrimaryBouncerView.getDelegate().shouldDismissOnMenuPressed();
}
- return mBouncer != null && mBouncer.shouldDismissOnMenuPressed();
+ return mPrimaryBouncer != null && mPrimaryBouncer.shouldDismissOnMenuPressed();
}
public boolean interceptMediaKey(KeyEvent event) {
- if (mBouncerView.getDelegate() != null) {
- return mBouncerView.getDelegate().interceptMediaKey(event);
+ if (mPrimaryBouncerView.getDelegate() != null) {
+ return mPrimaryBouncerView.getDelegate().interceptMediaKey(event);
}
- return mBouncer != null && mBouncer.interceptMediaKey(event);
+ return mPrimaryBouncer != null && mPrimaryBouncer.interceptMediaKey(event);
}
/**
* @return true if the pre IME back event should be handled
*/
public boolean dispatchBackKeyEventPreIme() {
- if (mBouncerView.getDelegate() != null) {
- return mBouncerView.getDelegate().dispatchBackKeyEventPreIme();
+ if (mPrimaryBouncerView.getDelegate() != null) {
+ return mPrimaryBouncerView.getDelegate().dispatchBackKeyEventPreIme();
}
- return mBouncer != null && mBouncer.dispatchBackKeyEventPreIme();
+ return mPrimaryBouncer != null && mPrimaryBouncer.dispatchBackKeyEventPreIme();
}
public void readyForKeyguardDone() {
@@ -1329,29 +1335,29 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* fingerprint.
*/
public void notifyKeyguardAuthenticated(boolean strongAuth) {
- if (mBouncer != null) {
- mBouncer.notifyKeyguardAuthenticated(strongAuth);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.notifyKeyguardAuthenticated(strongAuth);
} else {
- mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
+ mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
}
- if (mAlternateAuthInterceptor != null && isShowingAlternateAuth()) {
- resetAlternateAuth(false);
+ if (mAlternateBouncer != null && isShowingAlternateBouncer()) {
+ hideAlternateBouncer(false);
executeAfterKeyguardGoneAction();
}
}
/** Display security message to relevant KeyguardMessageArea. */
public void setKeyguardMessage(String message, ColorStateList colorState) {
- if (isShowingAlternateAuth()) {
+ if (isShowingAlternateBouncer()) {
if (mKeyguardMessageAreaController != null) {
mKeyguardMessageAreaController.setMessage(message);
}
} else {
- if (mBouncer != null) {
- mBouncer.showMessage(message, colorState);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.showMessage(message, colorState);
} else {
- mBouncerInteractor.showMessage(message, colorState);
+ mPrimaryBouncerInteractor.showMessage(message, colorState);
}
}
}
@@ -1390,12 +1396,15 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
- public boolean bouncerNeedsScrimming() {
+ /**
+ * Whether the primary bouncer requires scrimming.
+ */
+ public boolean primaryBouncerNeedsScrimming() {
// When a dream overlay is active, scrimming will cause any expansion to immediately expand.
return (mKeyguardStateController.isOccluded()
&& !mDreamOverlayStateController.isOverlayActive())
- || bouncerWillDismissWithAction()
- || (bouncerIsShowing() && bouncerIsScrimmed())
+ || primaryBouncerWillDismissWithAction()
+ || (primaryBouncerIsShowing() && primaryBouncerIsScrimmed())
|| isFullscreenBouncer();
}
@@ -1405,10 +1414,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* configuration.
*/
public void updateResources() {
- if (mBouncer != null) {
- mBouncer.updateResources();
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.updateResources();
} else {
- mBouncerInteractor.updateResources();
+ mPrimaryBouncerInteractor.updateResources();
}
}
@@ -1420,19 +1429,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
pw.println(" mAfterKeyguardGoneRunnables: " + mAfterKeyguardGoneRunnables);
pw.println(" mPendingWakeupAction: " + mPendingWakeupAction);
pw.println(" isBouncerShowing(): " + isBouncerShowing());
- pw.println(" bouncerIsOrWillBeShowing(): " + bouncerIsOrWillBeShowing());
+ pw.println(" bouncerIsOrWillBeShowing(): " + primaryBouncerIsOrWillBeShowing());
pw.println(" Registered KeyguardViewManagerCallbacks:");
for (KeyguardViewManagerCallback callback : mCallbacks) {
pw.println(" " + callback);
}
- if (mBouncer != null) {
- mBouncer.dump(pw);
+ if (mPrimaryBouncer != null) {
+ pw.println("PrimaryBouncer:");
+ mPrimaryBouncer.dump(pw);
}
- if (mAlternateAuthInterceptor != null) {
- pw.println("AltAuthInterceptor: ");
- mAlternateAuthInterceptor.dump(pw);
+ if (mAlternateBouncer != null) {
+ pw.println("AlternateBouncer:");
+ mAlternateBouncer.dump(pw);
}
}
@@ -1480,13 +1490,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
@Nullable
- public KeyguardBouncer getBouncer() {
- return mBouncer;
+ public KeyguardBouncer getPrimaryBouncer() {
+ return mPrimaryBouncer;
}
- public boolean isShowingAlternateAuth() {
- return mAlternateAuthInterceptor != null
- && mAlternateAuthInterceptor.isShowingAlternateAuthBouncer();
+ public boolean isShowingAlternateBouncer() {
+ return mAlternateBouncer != null && mAlternateBouncer.isShowingAlternateBouncer();
}
/**
@@ -1500,10 +1509,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
/** Update keyguard position based on a tapped X coordinate. */
public void updateKeyguardPosition(float x) {
- if (mBouncer != null) {
- mBouncer.updateKeyguardPosition(x);
+ if (mPrimaryBouncer != null) {
+ mPrimaryBouncer.updateKeyguardPosition(x);
} else {
- mBouncerInteractor.setKeyguardPosition(x);
+ mPrimaryBouncerInteractor.setKeyguardPosition(x);
}
}
@@ -1535,41 +1544,41 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
public void requestFp(boolean request, int udfpsColor) {
mKeyguardUpdateManager.requestFingerprintAuthOnOccludingApp(request);
- if (mAlternateAuthInterceptor != null) {
- mAlternateAuthInterceptor.requestUdfps(request, udfpsColor);
+ if (mAlternateBouncer != null) {
+ mAlternateBouncer.requestUdfps(request, udfpsColor);
}
}
/**
* Returns if bouncer expansion is between 0 and 1 non-inclusive.
*/
- public boolean isBouncerInTransit() {
- if (mBouncer != null) {
- return mBouncer.inTransit();
+ public boolean isPrimaryBouncerInTransit() {
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.inTransit();
} else {
- return mBouncerInteractor.isInTransit();
+ return mPrimaryBouncerInteractor.isInTransit();
}
}
/**
* Returns if bouncer is showing
*/
- public boolean bouncerIsShowing() {
- if (mBouncer != null) {
- return mBouncer.isShowing();
+ public boolean primaryBouncerIsShowing() {
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.isShowing();
} else {
- return mBouncerInteractor.isFullyShowing();
+ return mPrimaryBouncerInteractor.isFullyShowing();
}
}
/**
* Returns if bouncer is scrimmed
*/
- public boolean bouncerIsScrimmed() {
- if (mBouncer != null) {
- return mBouncer.isScrimmed();
+ public boolean primaryBouncerIsScrimmed() {
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.isScrimmed();
} else {
- return mBouncerInteractor.isScrimmed();
+ return mPrimaryBouncerInteractor.isScrimmed();
}
}
@@ -1577,10 +1586,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* Returns if bouncer is animating away
*/
public boolean bouncerIsAnimatingAway() {
- if (mBouncer != null) {
- return mBouncer.isAnimatingAway();
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.isAnimatingAway();
} else {
- return mBouncerInteractor.isAnimatingAway();
+ return mPrimaryBouncerInteractor.isAnimatingAway();
}
}
@@ -1588,11 +1597,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
/**
* Returns if bouncer will dismiss with action
*/
- public boolean bouncerWillDismissWithAction() {
- if (mBouncer != null) {
- return mBouncer.willDismissWithAction();
+ public boolean primaryBouncerWillDismissWithAction() {
+ if (mPrimaryBouncer != null) {
+ return mPrimaryBouncer.willDismissWithAction();
} else {
- return mBouncerInteractor.willDismissWithAction();
+ return mPrimaryBouncerInteractor.willDismissWithAction();
}
}
@@ -1607,26 +1616,26 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
/**
- * Delegate used to send show/reset events to an alternate authentication method instead of the
- * regular pin/pattern/password bouncer.
+ * Delegate used to send show and hide events to an alternate authentication method instead of
+ * the regular pin/pattern/password bouncer.
*/
- public interface AlternateAuthInterceptor {
+ public interface AlternateBouncer {
/**
* Show alternate authentication bouncer.
* @return whether alternate auth method was newly shown
*/
- boolean showAlternateAuthBouncer();
+ boolean showAlternateBouncer();
/**
* Hide alternate authentication bouncer
* @return whether the alternate auth method was newly hidden
*/
- boolean hideAlternateAuthBouncer();
+ boolean hideAlternateBouncer();
/**
* @return true if the alternate auth bouncer is showing
*/
- boolean isShowingAlternateAuthBouncer();
+ boolean isShowingAlternateBouncer();
/**
* Use when an app occluding the keyguard would like to give the user ability to
@@ -1638,7 +1647,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
void requestUdfps(boolean requestUdfps, int color);
/**
- * print information for the alternate auth interceptor registered
+ * print information for the alternate bouncer registered
*/
void dump(PrintWriter pw);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 70af77e1eb36..8a49850b1822 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -133,7 +133,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
if (!row.isPinned()) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
}
- mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
+ mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
mPendingRemoteInputView = clicked;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index d9c0293fe13d..2a039dade059 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -34,6 +34,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -68,12 +69,15 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
private int mDisplayCutoutTouchableRegionSize;
private int mStatusBarHeight;
+ private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
+
@Inject
public StatusBarTouchableRegionManager(
Context context,
NotificationShadeWindowController notificationShadeWindowController,
ConfigurationController configurationController,
HeadsUpManagerPhone headsUpManager,
+ ShadeExpansionStateManager shadeExpansionStateManager,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController
) {
mContext = context;
@@ -101,17 +105,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
updateTouchableRegion();
}
});
- mHeadsUpManager.addHeadsUpPhoneListener(
- new HeadsUpManagerPhone.OnHeadsUpPhoneListenerChange() {
- @Override
- public void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway) {
- if (!headsUpGoingAway) {
- updateTouchableRegionAfterLayout();
- } else {
- updateTouchableRegion();
- }
- }
- });
+ mHeadsUpManager.addHeadsUpPhoneListener(this::onHeadsUpGoingAwayStateChanged);
mNotificationShadeWindowController = notificationShadeWindowController;
mNotificationShadeWindowController.setForcePluginOpenListener((forceOpen) -> {
@@ -119,6 +113,9 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
});
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+ shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
+
+ mOnComputeInternalInsetsListener = this::onComputeInternalInsets;
}
protected void setup(
@@ -136,17 +133,11 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
pw.println(mTouchableRegion);
}
- /**
- * Notify that the status bar panel gets expanded or collapsed.
- *
- * @param isExpanded True to notify expanded, false to notify collapsed.
- * TODO(b/237811427) replace with a listener
- */
- public void setPanelExpanded(boolean isExpanded) {
+ private void onShadeExpansionFullyChanged(Boolean isExpanded) {
if (isExpanded != mIsStatusBarExpanded) {
mIsStatusBarExpanded = isExpanded;
if (isExpanded) {
- // make sure our state is sane
+ // make sure our state is sensible
mForceCollapsedUntilLayout = false;
}
updateTouchableRegion();
@@ -260,18 +251,22 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
|| mUnlockedScreenOffAnimationController.isAnimationPlaying();
}
- private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener =
- new OnComputeInternalInsetsListener() {
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (shouldMakeEntireScreenTouchable()) {
- return;
- }
+ private void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway) {
+ if (!headsUpGoingAway) {
+ updateTouchableRegionAfterLayout();
+ } else {
+ updateTouchableRegion();
+ }
+ }
- // Update touch insets to include any area needed for touching features that live in
- // the status bar (ie: heads up notifications)
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(calculateTouchableRegion());
+ private void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+ if (shouldMakeEntireScreenTouchable()) {
+ return;
}
- };
+
+ // Update touch insets to include any area needed for touching features that live in
+ // the status bar (ie: heads up notifications)
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(calculateTouchableRegion());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
index e7d9221ac861..678c2d972e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManager.java
@@ -87,7 +87,7 @@ public class SystemUIDialogManager implements Dumpable {
private void updateDialogListeners() {
if (shouldHideAffordance()) {
- mKeyguardViewController.resetAlternateAuth(true);
+ mKeyguardViewController.hideAlternateBouncer(true);
}
for (Listener listener : mListeners) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index dc902666874d..615f2304ecf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -226,6 +226,7 @@ public abstract class StatusBarViewModule {
BroadcastDispatcher broadcastDispatcher,
@Main Handler mainHandler,
ContentResolver contentResolver,
+ FeatureFlags featureFlags,
BatteryController batteryController
) {
return new BatteryMeterViewController(
@@ -235,6 +236,7 @@ public abstract class StatusBarViewModule {
broadcastDispatcher,
mainHandler,
contentResolver,
+ featureFlags,
batteryController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt
index 60bd0383f8c7..501467f13007 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt
@@ -32,6 +32,7 @@ import javax.inject.Inject
interface MobileMappingsProxy {
fun mapIconSets(config: Config): Map<String, MobileIconGroup>
fun getDefaultIcons(config: Config): MobileIconGroup
+ fun getIconKey(displayInfo: TelephonyDisplayInfo): String
fun toIconKey(@NetworkType networkType: Int): String
fun toIconKeyOverride(@NetworkType networkType: Int): String
}
@@ -44,6 +45,9 @@ class MobileMappingsProxyImpl @Inject constructor() : MobileMappingsProxy {
override fun getDefaultIcons(config: Config): MobileIconGroup =
MobileMappings.getDefaultIcons(config)
+ override fun getIconKey(displayInfo: TelephonyDisplayInfo): String =
+ MobileMappings.getIconKey(displayInfo)
+
override fun toIconKey(@NetworkType networkType: Int): String =
MobileMappings.toIconKey(networkType)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 149ed0a71b91..d10d7cf8460b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -155,6 +155,9 @@ public interface BatteryController extends DemoMode,
default void onWirelessChargingChanged(boolean isWirlessCharging) {
}
+
+ default void onIsOverheatedChanged(boolean isOverheated) {
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index c7ad76722929..3c2ac7b7a124 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.policy;
+import static android.os.BatteryManager.BATTERY_HEALTH_OVERHEAT;
+import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
+import static android.os.BatteryManager.EXTRA_HEALTH;
import static android.os.BatteryManager.EXTRA_PRESENT;
import android.annotation.WorkerThread;
@@ -87,6 +90,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
protected boolean mPowerSave;
private boolean mAodPowerSave;
private boolean mWirelessCharging;
+ private boolean mIsOverheated = false;
private boolean mTestMode = false;
@VisibleForTesting
boolean mHasReceivedBattery = false;
@@ -184,6 +188,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
cb.onPowerSaveChanged(mPowerSave);
cb.onBatteryUnknownStateChanged(mStateUnknown);
cb.onWirelessChargingChanged(mWirelessCharging);
+ cb.onIsOverheatedChanged(mIsOverheated);
}
@Override
@@ -222,6 +227,13 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
fireBatteryUnknownStateChanged();
}
+ int batteryHealth = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
+ boolean isOverheated = batteryHealth == BATTERY_HEALTH_OVERHEAT;
+ if (isOverheated != mIsOverheated) {
+ mIsOverheated = isOverheated;
+ fireIsOverheatedChanged();
+ }
+
fireBatteryLevelChanged();
} else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
updatePowerSave();
@@ -292,6 +304,10 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
return mPluggedChargingSource == BatteryManager.BATTERY_PLUGGED_WIRELESS;
}
+ public boolean isOverheated() {
+ return mIsOverheated;
+ }
+
@Override
public void getEstimatedTimeRemainingString(EstimateFetchCompletion completion) {
// Need to fetch or refresh the estimate, but it may involve binder calls so offload the
@@ -402,6 +418,15 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
}
}
+ private void fireIsOverheatedChanged() {
+ synchronized (mChangeCallbacks) {
+ final int n = mChangeCallbacks.size();
+ for (int i = 0; i < n; i++) {
+ mChangeCallbacks.get(i).onIsOverheatedChanged(mIsOverheated);
+ }
+ }
+ }
+
@Override
public void dispatchDemoCommand(String command, Bundle args) {
if (!mDemoModeController.isInDemoMode()) {
@@ -412,6 +437,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
String plugged = args.getString("plugged");
String powerSave = args.getString("powersave");
String present = args.getString("present");
+ String overheated = args.getString("overheated");
if (level != null) {
mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
}
@@ -426,6 +452,10 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mStateUnknown = !present.equals("true");
fireBatteryUnknownStateChanged();
}
+ if (overheated != null) {
+ mIsOverheated = overheated.equals("true");
+ fireIsOverheatedChanged();
+ }
fireBatteryLevelChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleController.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
index 48df15c78ea5..93e78acc63fd 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import androidx.annotation.VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
index c7f0b7e0056e..f558fee776e6 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.content.Context
import android.graphics.Canvas
@@ -31,11 +31,21 @@ import android.view.View
class MultiRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
internal val ripples = ArrayList<RippleAnimation>()
+ private val listeners = ArrayList<RipplesFinishedListener>()
private val ripplePaint = Paint()
private var isWarningLogged = false
companion object {
- const val TAG = "MultiRippleView"
+ private const val TAG = "MultiRippleView"
+
+ interface RipplesFinishedListener {
+ /** Triggered when all the ripples finish running. */
+ fun onRipplesFinish()
+ }
+ }
+
+ fun addRipplesFinishedListener(listener: RipplesFinishedListener) {
+ listeners.add(listener)
}
override fun onDraw(canvas: Canvas?) {
@@ -62,6 +72,10 @@ class MultiRippleView(context: Context?, attrs: AttributeSet?) : View(context, a
shouldInvalidate = shouldInvalidate || anim.isPlaying()
}
- if (shouldInvalidate) invalidate()
+ if (shouldInvalidate) {
+ invalidate()
+ } else { // Nothing is playing.
+ listeners.forEach { listener -> listener.onRipplesFinish() }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimation.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
index aca9e254e4c3..b2f8994ebf51 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimationConfig.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
index 88122544c7cd..ae73df201f8d 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimationConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
@@ -1,4 +1,4 @@
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.graphics.Color
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
index d2f3a6a7bee1..a950d34513ee 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.graphics.PointF
import android.graphics.RuntimeShader
import android.util.MathUtils
+import com.android.systemui.surfaceeffects.shaderutil.SdfShaderLibrary
+import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
/**
* Shader class that renders an expanding ripple effect. The ripple contains three elements:
@@ -31,7 +33,7 @@ import android.util.MathUtils
* Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
*/
class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.CIRCLE) :
- RuntimeShader(buildShader(rippleShape)) {
+ RuntimeShader(buildShader(rippleShape)) {
/** Shapes that the [RippleShader] supports. */
enum class RippleShape {
@@ -39,25 +41,30 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
ROUNDED_BOX,
ELLIPSE
}
- //language=AGSL
+ // language=AGSL
companion object {
- private const val SHADER_UNIFORMS = """uniform vec2 in_center;
- uniform vec2 in_size;
- uniform float in_progress;
- uniform float in_cornerRadius;
- uniform float in_thickness;
- uniform float in_time;
- uniform float in_distort_radial;
- uniform float in_distort_xy;
- uniform float in_fadeSparkle;
- uniform float in_fadeFill;
- uniform float in_fadeRing;
- uniform float in_blur;
- uniform float in_pixelDensity;
- layout(color) uniform vec4 in_color;
- uniform float in_sparkle_strength;"""
-
- private const val SHADER_CIRCLE_MAIN = """vec4 main(vec2 p) {
+ private const val SHADER_UNIFORMS =
+ """
+ uniform vec2 in_center;
+ uniform vec2 in_size;
+ uniform float in_progress;
+ uniform float in_cornerRadius;
+ uniform float in_thickness;
+ uniform float in_time;
+ uniform float in_distort_radial;
+ uniform float in_distort_xy;
+ uniform float in_fadeSparkle;
+ uniform float in_fadeFill;
+ uniform float in_fadeRing;
+ uniform float in_blur;
+ uniform float in_pixelDensity;
+ layout(color) uniform vec4 in_color;
+ uniform float in_sparkle_strength;
+ """
+
+ private const val SHADER_CIRCLE_MAIN =
+ """
+ vec4 main(vec2 p) {
vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
float radius = in_size.x * 0.5;
float sparkleRing = soften(circleRing(p_distorted-in_center, radius), in_blur);
@@ -73,7 +80,9 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
}
"""
- private const val SHADER_ROUNDED_BOX_MAIN = """vec4 main(vec2 p) {
+ private const val SHADER_ROUNDED_BOX_MAIN =
+ """
+ vec4 main(vec2 p) {
float sparkleRing = soften(roundedBoxRing(p-in_center, in_size, in_cornerRadius,
in_thickness), in_blur);
float inside = soften(sdRoundedBox(p-in_center, in_size * 1.2, in_cornerRadius),
@@ -89,7 +98,9 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
}
"""
- private const val SHADER_ELLIPSE_MAIN = """vec4 main(vec2 p) {
+ private const val SHADER_ELLIPSE_MAIN =
+ """
+ vec4 main(vec2 p) {
vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
float sparkleRing = soften(ellipseRing(p_distorted-in_center, in_size), in_blur);
@@ -105,22 +116,31 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
}
"""
- private const val CIRCLE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
- SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.CIRCLE_SDF +
+ private const val CIRCLE_SHADER =
+ SHADER_UNIFORMS +
+ ShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
+ SdfShaderLibrary.CIRCLE_SDF +
SHADER_CIRCLE_MAIN
- private const val ROUNDED_BOX_SHADER = SHADER_UNIFORMS +
- RippleShaderUtilLibrary.SHADER_LIB + SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
- SdfShaderLibrary.ROUNDED_BOX_SDF + SHADER_ROUNDED_BOX_MAIN
- private const val ELLIPSE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
- SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.ELLIPSE_SDF +
+ private const val ROUNDED_BOX_SHADER =
+ SHADER_UNIFORMS +
+ ShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
+ SdfShaderLibrary.ROUNDED_BOX_SDF +
+ SHADER_ROUNDED_BOX_MAIN
+ private const val ELLIPSE_SHADER =
+ SHADER_UNIFORMS +
+ ShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
+ SdfShaderLibrary.ELLIPSE_SDF +
SHADER_ELLIPSE_MAIN
private fun buildShader(rippleShape: RippleShape): String =
- when (rippleShape) {
- RippleShape.CIRCLE -> CIRCLE_SHADER
- RippleShape.ROUNDED_BOX -> ROUNDED_BOX_SHADER
- RippleShape.ELLIPSE -> ELLIPSE_SHADER
- }
+ when (rippleShape) {
+ RippleShape.CIRCLE -> CIRCLE_SHADER
+ RippleShape.ROUNDED_BOX -> ROUNDED_BOX_SHADER
+ RippleShape.ELLIPSE -> ELLIPSE_SHADER
+ }
private fun subProgress(start: Float, end: Float, progress: Float): Float {
val min = Math.min(start, end)
@@ -130,9 +150,7 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
}
}
- /**
- * Sets the center position of the ripple.
- */
+ /** Sets the center position of the ripple. */
fun setCenter(x: Float, y: Float) {
setFloatUniform("in_center", x, y)
}
@@ -144,21 +162,21 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
maxSize.y = height
}
- /**
- * Progress of the ripple. Float value between [0, 1].
- */
+ /** Progress of the ripple. Float value between [0, 1]. */
var progress: Float = 0.0f
set(value) {
field = value
setFloatUniform("in_progress", value)
val curvedProg = 1 - (1 - value) * (1 - value) * (1 - value)
- setFloatUniform("in_size", /* width= */ maxSize.x * curvedProg,
- /* height= */ maxSize.y * curvedProg)
+ setFloatUniform(
+ "in_size",
+ /* width= */ maxSize.x * curvedProg,
+ /* height= */ maxSize.y * curvedProg
+ )
setFloatUniform("in_thickness", maxSize.y * curvedProg * 0.5f)
// radius should not exceed width and height values.
- setFloatUniform("in_cornerRadius",
- Math.min(maxSize.x, maxSize.y) * curvedProg)
+ setFloatUniform("in_cornerRadius", Math.min(maxSize.x, maxSize.y) * curvedProg)
setFloatUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value))
@@ -175,18 +193,14 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
setFloatUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple))
}
- /**
- * Play time since the start of the effect.
- */
+ /** Play time since the start of the effect. */
var time: Float = 0.0f
set(value) {
field = value
setFloatUniform("in_time", value)
}
- /**
- * A hex value representing the ripple color, in the format of ARGB
- */
+ /** A hex value representing the ripple color, in the format of ARGB */
var color: Int = 0xffffff
set(value) {
field = value
@@ -194,9 +208,9 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
}
/**
- * Noise sparkle intensity. Expected value between [0, 1]. The sparkle is white, and thus
- * with strength 0 it's transparent, leaving the ripple fully smooth, while with strength 1
- * it's opaque white and looks the most grainy.
+ * Noise sparkle intensity. Expected value between [0, 1]. The sparkle is white, and thus with
+ * strength 0 it's transparent, leaving the ripple fully smooth, while with strength 1 it's
+ * opaque white and looks the most grainy.
*/
var sparkleStrength: Float = 0.0f
set(value) {
@@ -204,9 +218,7 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
setFloatUniform("in_sparkle_strength", value)
}
- /**
- * Distortion strength of the ripple. Expected value between[0, 1].
- */
+ /** Distortion strength of the ripple. Expected value between[0, 1]. */
var distortionStrength: Float = 0.0f
set(value) {
field = value
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
index a6d79303962f..299469494295 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
@@ -26,7 +26,7 @@ import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import androidx.core.graphics.ColorUtils
-import com.android.systemui.ripple.RippleShader.RippleShape
+import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape
/**
* A generic expanding ripple effect.
@@ -98,15 +98,18 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a
rippleShader.time = now.toFloat()
invalidate()
}
- animator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- onAnimationEnd?.run()
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ onAnimationEnd?.run()
+ }
}
- })
+ )
animator.start()
}
- /** Set the color to be used for the ripple.
+ /**
+ * Set the color to be used for the ripple.
*
* The alpha value of the color will be applied to the ripple. The alpha range is [0-100].
*/
@@ -123,9 +126,7 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a
rippleShader.rippleFill = rippleFill
}
- /**
- * Set the intensity of the sparkles.
- */
+ /** Set the intensity of the sparkles. */
fun setSparkleStrength(strength: Float) {
rippleShader.sparkleStrength = strength
}
@@ -143,20 +144,30 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a
// active effect area. Values here should be kept in sync with the animation implementation
// in the ripple shader.
if (rippleShape == RippleShape.CIRCLE) {
- val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * maxWidth
+ val maskRadius =
+ (1 -
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxWidth
canvas.drawCircle(centerX, centerY, maskRadius, ripplePaint)
} else {
- val maskWidth = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * maxWidth * 2
- val maskHeight = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * maxHeight * 2
+ val maskWidth =
+ (1 -
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxWidth * 2
+ val maskHeight =
+ (1 -
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxHeight * 2
canvas.drawRect(
- /* left= */ centerX - maskWidth,
- /* top= */ centerY - maskHeight,
- /* right= */ centerX + maskWidth,
- /* bottom= */ centerY + maskHeight,
- ripplePaint)
+ /* left= */ centerX - maskWidth,
+ /* top= */ centerY - maskHeight,
+ /* right= */ centerX + maskWidth,
+ /* bottom= */ centerY + maskHeight,
+ ripplePaint
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
index 5e256c653992..8b2f46648fef 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.shaderutil
/** Library class that contains 2D signed distance functions. */
class SdfShaderLibrary {
- //language=AGSL
+ // language=AGSL
companion object {
- const val CIRCLE_SDF = """
+ const val CIRCLE_SDF =
+ """
float sdCircle(vec2 p, float r) {
return (length(p)-r) / r;
}
@@ -34,7 +35,8 @@ class SdfShaderLibrary {
}
"""
- const val ROUNDED_BOX_SDF = """
+ const val ROUNDED_BOX_SDF =
+ """
float sdRoundedBox(vec2 p, vec2 size, float cornerRadius) {
size *= 0.5;
cornerRadius *= 0.5;
@@ -58,7 +60,8 @@ class SdfShaderLibrary {
// Used non-trigonometry parametrization and Halley's method (iterative) for root finding.
// This is more expensive than the regular circle SDF, recommend to use the circle SDF if
// possible.
- const val ELLIPSE_SDF = """float sdEllipse(vec2 p, vec2 wh) {
+ const val ELLIPSE_SDF =
+ """float sdEllipse(vec2 p, vec2 wh) {
wh *= 0.5;
// symmetry
@@ -98,7 +101,8 @@ class SdfShaderLibrary {
}
"""
- const val SHADER_SDF_OPERATION_LIB = """
+ const val SHADER_SDF_OPERATION_LIB =
+ """
float soften(float d, float blur) {
float blurHalf = blur * 0.5;
return smoothstep(-blurHalf, blurHalf, d);
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
new file mode 100644
index 000000000000..d78e0c153db8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.surfaceeffects.shaderutil
+
+/** A common utility functions that are used for computing shaders. */
+class ShaderUtilLibrary {
+ // language=AGSL
+ companion object {
+ const val SHADER_LIB =
+ """
+ float triangleNoise(vec2 n) {
+ n = fract(n * vec2(5.3987, 5.4421));
+ n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
+ float xy = n.x * n.y;
+ // compute in [0..2[ and remap to [-1.0..1.0[
+ return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
+ }
+
+ const float PI = 3.1415926535897932384626;
+
+ float sparkles(vec2 uv, float t) {
+ float n = triangleNoise(uv);
+ float s = 0.0;
+ for (float i = 0; i < 4; i += 1) {
+ float l = i * 0.01;
+ float h = l + 0.1;
+ float o = smoothstep(n - l, h, n);
+ o *= abs(sin(PI * o * (t + 0.55 * i)));
+ s += o;
+ }
+ return s;
+ }
+
+ vec2 distort(vec2 p, float time, float distort_amount_radial,
+ float distort_amount_xy) {
+ float angle = atan(p.y, p.x);
+ return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
+ cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
+ + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
+ cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
+ }
+
+ // Return range [-1, 1].
+ vec3 hash(vec3 p) {
+ p = fract(p * vec3(.3456, .1234, .9876));
+ p += dot(p, p.yxz + 43.21);
+ p = (p.xxy + p.yxx) * p.zyx;
+ return (fract(sin(p) * 4567.1234567) - .5) * 2.;
+ }
+
+ // Skew factors (non-uniform).
+ const float SKEW = 0.3333333; // 1/3
+ const float UNSKEW = 0.1666667; // 1/6
+
+ // Return range roughly [-1,1].
+ // It's because the hash function (that returns a random gradient vector) returns
+ // different magnitude of vectors. Noise doesn't have to be in the precise range thus
+ // skipped normalize.
+ float simplex3d(vec3 p) {
+ // Skew the input coordinate, so that we get squashed cubical grid
+ vec3 s = floor(p + (p.x + p.y + p.z) * SKEW);
+
+ // Unskew back
+ vec3 u = s - (s.x + s.y + s.z) * UNSKEW;
+
+ // Unskewed coordinate that is relative to p, to compute the noise contribution
+ // based on the distance.
+ vec3 c0 = p - u;
+
+ // We have six simplices (in this case tetrahedron, since we are in 3D) that we
+ // could possibly in.
+ // Here, we are finding the correct tetrahedron (simplex shape), and traverse its
+ // four vertices (c0..3) when computing noise contribution.
+ // The way we find them is by comparing c0's x,y,z values.
+ // For example in 2D, we can find the triangle (simplex shape in 2D) that we are in
+ // by comparing x and y values. i.e. x>y lower, x<y, upper triangle.
+ // Same applies in 3D.
+ //
+ // Below indicates the offsets (or offset directions) when c0=(x0,y0,z0)
+ // x0>y0>z0: (1,0,0), (1,1,0), (1,1,1)
+ // x0>z0>y0: (1,0,0), (1,0,1), (1,1,1)
+ // z0>x0>y0: (0,0,1), (1,0,1), (1,1,1)
+ // z0>y0>x0: (0,0,1), (0,1,1), (1,1,1)
+ // y0>z0>x0: (0,1,0), (0,1,1), (1,1,1)
+ // y0>x0>z0: (0,1,0), (1,1,0), (1,1,1)
+ //
+ // The rule is:
+ // * For offset1, set 1 at the max component, otherwise 0.
+ // * For offset2, set 0 at the min component, otherwise 1.
+ // * For offset3, set 1 for all.
+ //
+ // Encode x0-y0, y0-z0, z0-x0 in a vec3
+ vec3 en = c0 - c0.yzx;
+ // Each represents whether x0>y0, y0>z0, z0>x0
+ en = step(vec3(0.), en);
+ // en.zxy encodes z0>x0, x0>y0, y0>x0
+ vec3 offset1 = en * (1. - en.zxy); // find max
+ vec3 offset2 = 1. - en.zxy * (1. - en); // 1-(find min)
+ vec3 offset3 = vec3(1.);
+
+ vec3 c1 = c0 - offset1 + UNSKEW;
+ vec3 c2 = c0 - offset2 + UNSKEW * 2.;
+ vec3 c3 = c0 - offset3 + UNSKEW * 3.;
+
+ // Kernel summation: dot(max(0, r^2-d^2))^4, noise contribution)
+ //
+ // First compute d^2, squared distance to the point.
+ vec4 w; // w = max(0, r^2 - d^2))
+ w.x = dot(c0, c0);
+ w.y = dot(c1, c1);
+ w.z = dot(c2, c2);
+ w.w = dot(c3, c3);
+
+ // Noise contribution should decay to zero before they cross the simplex boundary.
+ // Usually r^2 is 0.5 or 0.6;
+ // 0.5 ensures continuity but 0.6 increases the visual quality for the application
+ // where discontinuity isn't noticeable.
+ w = max(0.6 - w, 0.);
+
+ // Noise contribution from each point.
+ vec4 nc;
+ nc.x = dot(hash(s), c0);
+ nc.y = dot(hash(s + offset1), c1);
+ nc.z = dot(hash(s + offset2), c2);
+ nc.w = dot(hash(s + offset3), c3);
+
+ nc *= w*w*w*w;
+
+ // Add all the noise contributions.
+ // Should multiply by the possible max contribution to adjust the range in [-1,1].
+ return dot(vec4(32.), nc);
+ }
+ """
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
new file mode 100644
index 000000000000..5ac3aad749fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.surfaceeffects.turbulencenoise
+
+import android.graphics.BlendMode
+import android.graphics.Color
+
+/** Turbulence noise animation configuration. */
+data class TurbulenceNoiseAnimationConfig(
+ /** The number of grids that is used to generate noise. */
+ val gridCount: Float = DEFAULT_NOISE_GRID_COUNT,
+
+ /** Multiplier for the noise luma matte. Increase this for brighter effects. */
+ val luminosityMultiplier: Float = DEFAULT_LUMINOSITY_MULTIPLIER,
+
+ /**
+ * Noise move speed variables.
+ *
+ * Its sign determines the direction; magnitude determines the speed. <ul>
+ * ```
+ * <li> [noiseMoveSpeedX] positive: right to left; negative: left to right.
+ * <li> [noiseMoveSpeedY] positive: bottom to top; negative: top to bottom.
+ * <li> [noiseMoveSpeedZ] its sign doesn't matter much, as it moves in Z direction. Use it
+ * to add turbulence in place.
+ * ```
+ * </ul>
+ */
+ val noiseMoveSpeedX: Float = 0f,
+ val noiseMoveSpeedY: Float = 0f,
+ val noiseMoveSpeedZ: Float = DEFAULT_NOISE_SPEED_Z,
+
+ /** Color of the effect. */
+ var color: Int = DEFAULT_COLOR,
+ /** Background color of the effect. */
+ val backgroundColor: Int = DEFAULT_BACKGROUND_COLOR,
+ val opacity: Int = DEFAULT_OPACITY,
+ val width: Float = 0f,
+ val height: Float = 0f,
+ val duration: Float = DEFAULT_NOISE_DURATION_IN_MILLIS,
+ val pixelDensity: Float = 1f,
+ val blendMode: BlendMode = DEFAULT_BLEND_MODE,
+ val onAnimationEnd: Runnable? = null
+) {
+ companion object {
+ const val DEFAULT_NOISE_DURATION_IN_MILLIS = 7500F
+ const val DEFAULT_LUMINOSITY_MULTIPLIER = 1f
+ const val DEFAULT_NOISE_GRID_COUNT = 1.2f
+ const val DEFAULT_NOISE_SPEED_Z = 0.3f
+ const val DEFAULT_OPACITY = 150 // full opacity is 255.
+ const val DEFAULT_COLOR = Color.WHITE
+ const val DEFAULT_BACKGROUND_COLOR = Color.BLACK
+ val DEFAULT_BLEND_MODE = BlendMode.SRC_OVER
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
new file mode 100644
index 000000000000..4c7e5f4c7093
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.surfaceeffects.turbulencenoise
+
+/** A controller that plays [TurbulenceNoiseView]. */
+class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoiseView) {
+ /** Updates the color of the noise. */
+ fun updateNoiseColor(color: Int) {
+ turbulenceNoiseView.updateColor(color)
+ }
+
+ // TODO: add cancel and/ or pause once design requirements become clear.
+ /** Plays [TurbulenceNoiseView] with the given config. */
+ fun play(turbulenceNoiseAnimationConfig: TurbulenceNoiseAnimationConfig) {
+ turbulenceNoiseView.play(turbulenceNoiseAnimationConfig)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt
new file mode 100644
index 000000000000..19c114d2693c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.surfaceeffects.turbulencenoise
+
+import android.graphics.RuntimeShader
+import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
+import java.lang.Float.max
+
+/** Shader that renders turbulence simplex noise, with no octave. */
+class TurbulenceNoiseShader : RuntimeShader(TURBULENCE_NOISE_SHADER) {
+ // language=AGSL
+ companion object {
+ private const val UNIFORMS =
+ """
+ uniform float in_gridNum;
+ uniform vec3 in_noiseMove;
+ uniform vec2 in_size;
+ uniform float in_aspectRatio;
+ uniform float in_opacity;
+ uniform float in_pixelDensity;
+ layout(color) uniform vec4 in_color;
+ layout(color) uniform vec4 in_backgroundColor;
+ """
+
+ private const val SHADER_LIB =
+ """
+ float getLuminosity(vec3 c) {
+ return 0.3*c.r + 0.59*c.g + 0.11*c.b;
+ }
+
+ vec3 maskLuminosity(vec3 dest, float lum) {
+ dest.rgb *= vec3(lum);
+ // Clip back into the legal range
+ dest = clamp(dest, vec3(0.), vec3(1.0));
+ return dest;
+ }
+ """
+
+ private const val MAIN_SHADER =
+ """
+ vec4 main(vec2 p) {
+ vec2 uv = p / in_size.xy;
+ uv.x *= in_aspectRatio;
+
+ vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
+ float luma = simplex3d(noiseP) * in_opacity;
+ vec3 mask = maskLuminosity(in_color.rgb, luma);
+ vec3 color = in_backgroundColor.rgb + mask * 0.6;
+
+ // Add dither with triangle distribution to avoid color banding. Ok to dither in the
+ // shader here as we are in gamma space.
+ float dither = triangleNoise(p * in_pixelDensity) / 255.;
+
+ // The result color should be pre-multiplied, i.e. [R*A, G*A, B*A, A], thus need to
+ // multiply rgb with a to get the correct result.
+ color = (color + dither.rrr) * in_color.a;
+ return vec4(color, in_color.a);
+ }
+ """
+
+ private const val TURBULENCE_NOISE_SHADER =
+ ShaderUtilLibrary.SHADER_LIB + UNIFORMS + SHADER_LIB + MAIN_SHADER
+ }
+
+ /** Sets the number of grid for generating noise. */
+ fun setGridCount(gridNumber: Float = 1.0f) {
+ setFloatUniform("in_gridNum", gridNumber)
+ }
+
+ /**
+ * Sets the pixel density of the screen.
+ *
+ * Used it for noise dithering.
+ */
+ fun setPixelDensity(pixelDensity: Float) {
+ setFloatUniform("in_pixelDensity", pixelDensity)
+ }
+
+ /** Sets the noise color of the effect. */
+ fun setColor(color: Int) {
+ setColorUniform("in_color", color)
+ }
+
+ /** Sets the background color of the effect. */
+ fun setBackgroundColor(color: Int) {
+ setColorUniform("in_backgroundColor", color)
+ }
+
+ /**
+ * Sets the opacity to achieve fade in/ out of the animation.
+ *
+ * Expected value range is [1, 0].
+ */
+ fun setOpacity(opacity: Float) {
+ setFloatUniform("in_opacity", opacity)
+ }
+
+ /** Sets the size of the shader. */
+ fun setSize(width: Float, height: Float) {
+ setFloatUniform("in_size", width, height)
+ setFloatUniform("in_aspectRatio", width / max(height, 0.001f))
+ }
+
+ /** Sets noise move speed in x, y, and z direction. */
+ fun setNoiseMove(x: Float, y: Float, z: Float) {
+ setFloatUniform("in_noiseMove", x, y, z)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
new file mode 100644
index 000000000000..8649d5924587
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.surfaceeffects.turbulencenoise
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import androidx.core.graphics.ColorUtils
+import java.util.Random
+import kotlin.math.sin
+
+/** View that renders turbulence noise effect. */
+class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
+
+ companion object {
+ private const val MS_TO_SEC = 0.001f
+ private const val TWO_PI = Math.PI.toFloat() * 2f
+ }
+
+ @VisibleForTesting val turbulenceNoiseShader = TurbulenceNoiseShader()
+ private val paint = Paint().apply { this.shader = turbulenceNoiseShader }
+ private val random = Random()
+ private val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f)
+ private var config: TurbulenceNoiseAnimationConfig? = null
+
+ val isPlaying: Boolean
+ get() = animator.isRunning
+
+ init {
+ // Only visible during the animation.
+ visibility = INVISIBLE
+ }
+
+ /** Updates the color during the animation. No-op if there's no animation playing. */
+ fun updateColor(color: Int) {
+ config?.let {
+ it.color = color
+ applyConfig(it)
+ }
+ }
+
+ override fun onDraw(canvas: Canvas?) {
+ if (canvas == null || !canvas.isHardwareAccelerated) {
+ // Drawing with the turbulence noise shader requires hardware acceleration, so skip
+ // if it's unsupported.
+ return
+ }
+
+ canvas.drawPaint(paint)
+ }
+
+ internal fun play(config: TurbulenceNoiseAnimationConfig) {
+ if (isPlaying) {
+ return // Ignore if the animation is playing.
+ }
+ visibility = VISIBLE
+ applyConfig(config)
+
+ // Add random offset to avoid same patterned noise.
+ val offsetX = random.nextFloat()
+ val offsetY = random.nextFloat()
+
+ animator.duration = config.duration.toLong()
+ animator.addUpdateListener { updateListener ->
+ val timeInSec = updateListener.currentPlayTime * MS_TO_SEC
+ // Remap [0,1] to [0, 2*PI]
+ val progress = TWO_PI * updateListener.animatedValue as Float
+
+ turbulenceNoiseShader.setNoiseMove(
+ offsetX + timeInSec * config.noiseMoveSpeedX,
+ offsetY + timeInSec * config.noiseMoveSpeedY,
+ timeInSec * config.noiseMoveSpeedZ
+ )
+
+ // Fade in and out the noise as the animation progress.
+ // TODO: replace it with a better curve
+ turbulenceNoiseShader.setOpacity(sin(TWO_PI - progress) * config.luminosityMultiplier)
+
+ invalidate()
+ }
+
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ visibility = INVISIBLE
+ config.onAnimationEnd?.run()
+ }
+ }
+ )
+ animator.start()
+ }
+
+ private fun applyConfig(config: TurbulenceNoiseAnimationConfig) {
+ this.config = config
+ with(turbulenceNoiseShader) {
+ setGridCount(config.gridCount)
+ setColor(ColorUtils.setAlphaComponent(config.color, config.opacity))
+ setBackgroundColor(config.backgroundColor)
+ setSize(config.width, config.height)
+ setPixelDensity(config.pixelDensity)
+ }
+ paint.blendMode = config.blendMode
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 4cb41f3a977e..a9d05d11dc00 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -65,8 +65,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
height = WindowManager.LayoutParams.WRAP_CONTENT
type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
- WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
- WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
format = PixelFormat.TRANSLUCENT
setTrustedOverlay()
}
@@ -95,6 +94,13 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
private var wakeReasonAcquired: String? = null
/**
+ * A stack of pairs of device id and temporary view info. This is used when there may be
+ * multiple devices in range, and we want to always display the chip for the most recently
+ * active device.
+ */
+ internal val activeViews: ArrayDeque<Pair<String, T>> = ArrayDeque()
+
+ /**
* Displays the view with the provided [newInfo].
*
* This method handles inflating and attaching the view, then delegates to [updateView] to
@@ -103,6 +109,12 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
fun displayView(newInfo: T) {
val currentDisplayInfo = displayInfo
+ // Update our list of active devices by removing it if necessary, then adding back at the
+ // front of the list
+ val id = newInfo.id
+ val position = findAndRemoveFromActiveViewsList(id)
+ activeViews.addFirst(Pair(id, newInfo))
+
if (currentDisplayInfo != null &&
currentDisplayInfo.info.windowTitle == newInfo.windowTitle) {
// We're already displaying information in the correctly-titled window, so we just need
@@ -114,27 +126,37 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
// We're already displaying information but that information is under a different
// window title. So, we need to remove the old window with the old title and add a
// new window with the new title.
- removeView(removalReason = "New info has new window title: ${newInfo.windowTitle}")
+ removeView(
+ id,
+ removalReason = "New info has new window title: ${newInfo.windowTitle}"
+ )
}
// At this point, we're guaranteed to no longer be displaying a view.
// So, set up all our callbacks and inflate the view.
configurationController.addCallback(displayScaleListener)
- // Wake the screen if necessary so the user will see the view. (Per b/239426653, we want
- // the view to show over the dream state, so we should only wake up if the screen is
- // completely off.)
- if (!powerManager.isScreenOn) {
- wakeLock = wakeLockBuilder
+
+ wakeLock = if (!powerManager.isScreenOn) {
+ // If the screen is off, fully wake it so the user can see the view.
+ wakeLockBuilder
.setTag(newInfo.windowTitle)
.setLevelsAndFlags(
- PowerManager.FULL_WAKE_LOCK or
- PowerManager.ACQUIRE_CAUSES_WAKEUP
+ PowerManager.FULL_WAKE_LOCK or
+ PowerManager.ACQUIRE_CAUSES_WAKEUP
)
.build()
- wakeLock?.acquire(newInfo.wakeReason)
- wakeReasonAcquired = newInfo.wakeReason
+ } else {
+ // Per b/239426653, we want the view to show over the dream state.
+ // If the screen is on, using screen bright level will leave screen on the dream
+ // state but ensure the screen will not go off before wake lock is released.
+ wakeLockBuilder
+ .setTag(newInfo.windowTitle)
+ .setLevelsAndFlags(PowerManager.SCREEN_BRIGHT_WAKE_LOCK)
+ .build()
}
- logger.logViewAddition(newInfo.windowTitle)
+ wakeLock?.acquire(newInfo.wakeReason)
+ wakeReasonAcquired = newInfo.wakeReason
+ logger.logViewAddition(id, newInfo.windowTitle)
inflateAndUpdateView(newInfo)
}
@@ -145,9 +167,13 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
// include it just to be safe.
FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS
)
- cancelViewTimeout?.run()
+
+ // Only cancel timeout of the most recent view displayed, as it will be reset.
+ if (position == 0) {
+ cancelViewTimeout?.run()
+ }
cancelViewTimeout = mainExecutor.executeDelayed(
- { removeView(REMOVAL_REASON_TIMEOUT) },
+ { removeView(id, REMOVAL_REASON_TIMEOUT) },
timeout.toLong()
)
}
@@ -190,28 +216,67 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
}
/**
- * Hides the view.
+ * Hides the view given its [id].
*
+ * @param id the id of the device responsible of displaying the temp view.
* @param removalReason a short string describing why the view was removed (timeout, state
* change, etc.)
*/
- fun removeView(removalReason: String) {
+ fun removeView(id: String, removalReason: String) {
val currentDisplayInfo = displayInfo ?: return
+ val removalPosition = findAndRemoveFromActiveViewsList(id)
+ if (removalPosition == null) {
+ logger.logViewRemovalIgnored(id, "view not found in the list")
+ return
+ }
+ if (removalPosition != 0) {
+ logger.logViewRemovalIgnored(id, "most recent view is being displayed.")
+ return
+ }
+ logger.logViewRemoval(id, removalReason)
+
+ val newViewToDisplay = if (activeViews.isEmpty()) {
+ null
+ } else {
+ activeViews[0].second
+ }
+
val currentView = currentDisplayInfo.view
animateViewOut(currentView) {
windowManager.removeView(currentView)
wakeLock?.release(wakeReasonAcquired)
}
- logger.logViewRemoval(removalReason)
configurationController.removeCallback(displayScaleListener)
// Re-set to null immediately (instead as part of the animation end runnable) so
- // that if a new view event comes in while this view is animating out, we still display the
- // new view appropriately.
+ // that if a new view event comes in while this view is animating out, we still display
+ // the new view appropriately.
displayInfo = null
// No need to time the view out since it's already gone
cancelViewTimeout?.run()
+
+ if (newViewToDisplay != null) {
+ mainExecutor.executeDelayed({ displayView(newViewToDisplay)}, DISPLAY_VIEW_DELAY)
+ }
+ }
+
+ /**
+ * Finds and removes the active view with the given [id] from the stack, or null if there is no
+ * active view with that ID
+ *
+ * @param id that temporary view belonged to.
+ *
+ * @return index of the view in the stack , otherwise null.
+ */
+ private fun findAndRemoveFromActiveViewsList(id: String): Int? {
+ for (i in 0 until activeViews.size) {
+ if (activeViews[i].first == id) {
+ activeViews.removeAt(i)
+ return i
+ }
+ }
+ return null
}
/**
@@ -252,6 +317,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
}
private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
+const val DISPLAY_VIEW_DELAY = 50L
private data class IconInfo(
val iconName: String,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
index cbb500296888..df8396051dda 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
@@ -37,6 +37,11 @@ abstract class TemporaryViewInfo {
* disappears.
*/
open val timeoutMs: Int = DEFAULT_TIMEOUT_MILLIS
+
+ /**
+ * The id of the temporary view.
+ */
+ abstract val id: String
}
const val DEFAULT_TIMEOUT_MILLIS = 10000
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
index 428a104484a7..133a384e7e17 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
@@ -24,13 +24,42 @@ open class TemporaryViewLogger(
internal val buffer: LogBuffer,
internal val tag: String,
) {
- /** Logs that we added the view in a window titled [windowTitle]. */
- fun logViewAddition(windowTitle: String) {
- buffer.log(tag, LogLevel.DEBUG, { str1 = windowTitle }, { "View added. window=$str1" })
+ /** Logs that we added the view with the given [id] in a window titled [windowTitle]. */
+ fun logViewAddition(id: String, windowTitle: String) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ {
+ str1 = windowTitle
+ str2 = id
+ },
+ { "View added. window=$str1 id=$str2" }
+ )
}
- /** Logs that we removed the chip for the given [reason]. */
- fun logViewRemoval(reason: String) {
- buffer.log(tag, LogLevel.DEBUG, { str1 = reason }, { "View removed due to: $str1" })
+ /** Logs that we removed the view with the given [id] for the given [reason]. */
+ fun logViewRemoval(id: String, reason: String) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ {
+ str1 = reason
+ str2 = id
+ },
+ { "View with id=$str2 is removed due to: $str1" }
+ )
+ }
+
+ /** Logs that we ignored removal of the view with the given [id]. */
+ fun logViewRemovalIgnored(id: String, reason: String) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ {
+ str1 = reason
+ str2 = id
+ },
+ { "Removal of view with id=$str2 is ignored because $str1" }
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 6237365d0cee..b92e0ec0428f 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -40,6 +40,7 @@ data class ChipbarInfo(
override val windowTitle: String,
override val wakeReason: String,
override val timeoutMs: Int,
+ override val id: String,
) : TemporaryViewInfo()
/** The possible items to display at the end of the chipbar. */
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 10a09dd169e8..82200c61eeb5 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -47,6 +47,7 @@ import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeControllerImpl;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -157,7 +158,8 @@ public abstract class TvSystemUIModule {
ConfigurationController configurationController,
@Main Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ ShadeExpansionStateManager shadeExpansionStateManager) {
return new HeadsUpManagerPhone(
context,
headsUpManagerLogger,
@@ -168,7 +170,8 @@ public abstract class TvSystemUIModule {
configurationController,
handler,
accessibilityManagerWrapper,
- uiEventLogger
+ uiEventLogger,
+ shadeExpansionStateManager
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index bf706735d531..e8a22ec4fbe7 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -46,6 +46,7 @@ import com.android.internal.R;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.CoreStartable;
import com.android.systemui.SystemUIApplication;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.util.NotificationChannels;
@@ -61,15 +62,24 @@ public class StorageNotification implements CoreStartable {
private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
private static final String ACTION_FINISH_WIZARD = "com.android.systemui.action.FINISH_WIZARD";
private final Context mContext;
+ private final BroadcastDispatcher mBroadcastDispatcher;
// TODO: delay some notifications to avoid bumpy fast operations
- private NotificationManager mNotificationManager;
- private StorageManager mStorageManager;
+ private final NotificationManager mNotificationManager;
+ private final StorageManager mStorageManager;
@Inject
- public StorageNotification(Context context) {
+ public StorageNotification(
+ Context context,
+ BroadcastDispatcher broadcastDispatcher,
+ NotificationManager notificationManager,
+ StorageManager storageManager
+ ) {
mContext = context;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mNotificationManager = notificationManager;
+ mStorageManager = storageManager;
}
private static class MoveInfo {
@@ -168,17 +178,22 @@ public class StorageNotification implements CoreStartable {
@Override
public void start() {
- mNotificationManager = mContext.getSystemService(NotificationManager.class);
-
- mStorageManager = mContext.getSystemService(StorageManager.class);
mStorageManager.registerListener(mListener);
- mContext.registerReceiver(mSnoozeReceiver, new IntentFilter(ACTION_SNOOZE_VOLUME),
- android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null,
- Context.RECEIVER_EXPORTED_UNAUDITED);
- mContext.registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_WIZARD),
- android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null,
- Context.RECEIVER_EXPORTED_UNAUDITED);
+ mBroadcastDispatcher.registerReceiver(
+ mSnoozeReceiver,
+ new IntentFilter(ACTION_SNOOZE_VOLUME),
+ null,
+ null,
+ Context.RECEIVER_EXPORTED_UNAUDITED,
+ android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ mBroadcastDispatcher.registerReceiver(
+ mFinishReceiver,
+ new IntentFilter(ACTION_FINISH_WIZARD),
+ null,
+ null,
+ Context.RECEIVER_EXPORTED_UNAUDITED,
+ android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
// Kick current state into place
final List<DiskInfo> disks = mStorageManager.getDisks();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 3094a8c2eb1b..0d9627248a28 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -1228,6 +1228,9 @@ public class VolumeDialogImpl implements VolumeDialog,
effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
break;
case RINGER_MODE_VIBRATE:
+ // Feedback handled by onStateChange, for feedback both when user toggles
+ // directly in volume dialog, or drags slider to a value of 0 in settings.
+ break;
default:
effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
}
@@ -1628,9 +1631,8 @@ public class VolumeDialogImpl implements VolumeDialog,
&& mState.ringerModeInternal != -1
&& mState.ringerModeInternal != state.ringerModeInternal
&& state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
- mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK));
+ mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK));
}
-
mState = state;
mDynamic.clear();
// add any new dynamic rows
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index e51236be2ffb..78c28ea23be0 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -93,6 +93,21 @@
android:excludeFromRecents="true"
/>
+ <activity android:name="com.android.systemui.controls.management.ControlsEditingActivityTest$TestableControlsEditingActivity"
+ android:exported="false"
+ android:excludeFromRecents="true"
+ />
+
+ <activity android:name="com.android.systemui.controls.management.ControlsFavoritingActivityTest$TestableControlsFavoritingActivity"
+ android:exported="false"
+ android:excludeFromRecents="true"
+ />
+
+ <activity android:name="com.android.systemui.controls.management.ControlsProviderSelectorActivityTest$TestableControlsProviderSelectorActivity"
+ android:exported="false"
+ android:excludeFromRecents="true"
+ />
+
<activity android:name="com.android.systemui.screenshot.ScrollViewActivity"
android:exported="false" />
@@ -125,6 +140,12 @@
tools:replace="android:authorities"
tools:node="remove" />
+ <provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+ android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
+ android:enabled="false"
+ tools:replace="android:authorities"
+ tools:node="remove" />
+
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.android.systemui.test.fileprovider"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index 5d2b0ca4e7ea..829008403e02 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -16,8 +16,11 @@
package com.android.keyguard;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -90,4 +93,11 @@ public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
mMessageAreaController.setIsVisible(true);
verify(mKeyguardMessageArea).setIsVisible(true);
}
+
+ @Test
+ public void testGetMessage() {
+ String msg = "abc";
+ when(mKeyguardMessageArea.getText()).thenReturn(msg);
+ assertThat(mMessageAreaController.getMessage()).isEqualTo(msg);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index b369098cafc0..ffd95f4041f9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -31,6 +31,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -118,4 +119,14 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
keyguardPasswordViewController.startAppearAnimation()
verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_password)
}
+
+ @Test
+ fun startAppearAnimation_withExistingMessage() {
+ `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+ keyguardPasswordViewController.startAppearAnimation()
+ verify(
+ mKeyguardMessageAreaController,
+ never()
+ ).setMessage(R.string.keyguard_enter_your_password)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 9eff70487c74..b3d1c8f909d8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -33,6 +33,7 @@ import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
import org.mockito.MockitoAnnotations
@SmallTest
@@ -112,4 +113,14 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
mKeyguardPatternViewController.startAppearAnimation()
verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern)
}
+
+ @Test
+ fun startAppearAnimation_withExistingMessage() {
+ `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+ mKeyguardPatternViewController.startAppearAnimation()
+ verify(
+ mKeyguardMessageAreaController,
+ never()
+ ).setMessage(R.string.keyguard_enter_your_password)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index d9efdeaea04c..8bcfe6f2b6f5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -100,4 +100,12 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
pinViewController.startAppearAnimation()
verify(keyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pin)
}
+
+ @Test
+ fun startAppearAnimation_withExistingMessage() {
+ Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+ pinViewController.startAppearAnimation()
+ verify(keyguardMessageAreaController, Mockito.never())
+ .setMessage(R.string.keyguard_enter_your_password)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index aa4469f12161..4d58b09f1076 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -548,6 +548,22 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
verify(mKeyguardPasswordViewControllerMock, never()).showMessage(null, null);
}
+ @Test
+ public void onDensityorFontScaleChanged() {
+ ArgumentCaptor<ConfigurationController.ConfigurationListener>
+ configurationListenerArgumentCaptor = ArgumentCaptor.forClass(
+ ConfigurationController.ConfigurationListener.class);
+ mKeyguardSecurityContainerController.onViewAttached();
+ verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+ configurationListenerArgumentCaptor.getValue().onDensityOrFontScaleChanged();
+
+ verify(mView).onDensityOrFontScaleChanged();
+ verify(mKeyguardSecurityViewFlipperController).onDensityOrFontScaleChanged();
+ verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
+ any(KeyguardSecurityCallback.class));
+ }
+
+
private KeyguardSecurityContainer.SwipeListener getRegisteredSwipeListener() {
mKeyguardSecurityContainerController.onViewAttached();
verify(mView).setSwipeListener(mSwipeListenerArgumentCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 1bd14e558fa0..36ed669e299c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -262,9 +262,12 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
ConstraintSet.Constraint userSwitcherConstraint =
getViewConstraint(R.id.keyguard_bouncer_user_switcher);
- assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.topToBottom).isEqualTo(
+ R.id.keyguard_bouncer_user_switcher);
assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
assertThat(userSwitcherConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.bottomToTop).isEqualTo(
+ mSecurityViewFlipper.getId());
assertThat(userSwitcherConstraint.layout.topMargin).isEqualTo(
getContext().getResources().getDimensionPixelSize(
R.dimen.bouncer_user_switcher_y_trans));
@@ -308,6 +311,17 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
}
@Test
+ public void testOnDensityOrFontScaleChanged() {
+ setupUserSwitcher();
+ View oldUserSwitcher = mKeyguardSecurityContainer.findViewById(
+ R.id.keyguard_bouncer_user_switcher);
+ mKeyguardSecurityContainer.onDensityOrFontScaleChanged();
+ View newUserSwitcher = mKeyguardSecurityContainer.findViewById(
+ R.id.keyguard_bouncer_user_switcher);
+ assertThat(oldUserSwitcher).isNotEqualTo(newUserSwitcher);
+ }
+
+ @Test
public void testTouchesAreRecognizedAsBeingOnTheOtherSideOfSecurity() {
setupUserSwitcher();
setViewWidth(VIEW_WIDTH);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index 9296d3d5ec82..fd02ac97cec2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -106,4 +106,10 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
}
}
}
+
+ @Test
+ public void onDensityOrFontScaleChanged() {
+ mKeyguardSecurityViewFlipperController.onDensityOrFontScaleChanged();
+ verify(mView).removeAllViews();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 9c14ee610b25..a3a089f4647f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -629,7 +629,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testNoStartAuthenticate_whenAboutToShowBouncer() {
- mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(
+ mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(
/* bouncerIsOrWillBeShowing */ true, /* bouncerFullyShown */ false);
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
@@ -1314,7 +1314,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
Arrays.asList("Unlocked by wearable"));
// THEN the showTrustGrantedMessage should be called with the first message
- verify(mTestCallback).showTrustGrantedMessage("Unlocked by wearable");
+ verify(mTestCallback).onTrustGrantedWithFlags(
+ eq(0),
+ eq(KeyguardUpdateMonitor.getCurrentUser()),
+ eq("Unlocked by wearable"));
}
@Test
@@ -1852,7 +1855,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
private void setKeyguardBouncerVisibility(boolean isVisible) {
- mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible);
+ mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(isVisible, isVisible);
mTestableLooper.processAllMessages();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
index 6b1ef389a98e..81d0034128b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
@@ -3,6 +3,7 @@ package com.android.systemui
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.content.pm.UserInfo
import android.content.res.Resources
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -11,9 +12,11 @@ import com.android.systemui.flags.Flag
import com.android.systemui.flags.FlagListenable
import com.android.systemui.flags.Flags
import com.android.systemui.flags.UnreleasedFlag
+import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
@@ -26,9 +29,9 @@ import org.mockito.Mock
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@@ -44,6 +47,8 @@ class ChooserSelectorTest : SysuiTestCase() {
private lateinit var chooserSelector: ChooserSelector
@Mock private lateinit var mockContext: Context
+ @Mock private lateinit var mockProfileContext: Context
+ @Mock private lateinit var mockUserTracker: UserTracker
@Mock private lateinit var mockPackageManager: PackageManager
@Mock private lateinit var mockResources: Resources
@Mock private lateinit var mockFeatureFlags: FeatureFlags
@@ -52,12 +57,20 @@ class ChooserSelectorTest : SysuiTestCase() {
fun setup() {
MockitoAnnotations.initMocks(this)
- `when`(mockContext.packageManager).thenReturn(mockPackageManager)
- `when`(mockContext.resources).thenReturn(mockResources)
- `when`(mockResources.getString(anyInt())).thenReturn(
+ whenever(mockContext.createContextAsUser(any(), anyInt())).thenReturn(mockProfileContext)
+ whenever(mockContext.resources).thenReturn(mockResources)
+ whenever(mockProfileContext.packageManager).thenReturn(mockPackageManager)
+ whenever(mockResources.getString(anyInt())).thenReturn(
ComponentName("TestPackage", "TestClass").flattenToString())
-
- chooserSelector = ChooserSelector(mockContext, mockFeatureFlags, testScope, testDispatcher)
+ whenever(mockUserTracker.userProfiles).thenReturn(listOf(UserInfo(), UserInfo()))
+
+ chooserSelector = ChooserSelector(
+ mockContext,
+ mockUserTracker,
+ mockFeatureFlags,
+ testScope,
+ testDispatcher,
+ )
}
@After
@@ -74,7 +87,9 @@ class ChooserSelectorTest : SysuiTestCase() {
// Assert
verify(mockFeatureFlags).addListener(
- eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
+ flagListener.capture(),
+ )
verify(mockFeatureFlags, never()).removeListener(any())
// Act
@@ -87,86 +102,102 @@ class ChooserSelectorTest : SysuiTestCase() {
@Test
fun initialize_enablesUnbundledChooser_whenFlagEnabled() {
// Arrange
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
// Act
chooserSelector.start()
// Assert
- verify(mockPackageManager).setComponentEnabledSetting(
+ verify(mockPackageManager, times(2)).setComponentEnabledSetting(
eq(ComponentName("TestPackage", "TestClass")),
eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
- anyInt())
+ anyInt(),
+ )
}
@Test
fun initialize_disablesUnbundledChooser_whenFlagDisabled() {
// Arrange
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
// Act
chooserSelector.start()
// Assert
- verify(mockPackageManager).setComponentEnabledSetting(
+ verify(mockPackageManager, times(2)).setComponentEnabledSetting(
eq(ComponentName("TestPackage", "TestClass")),
eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
- anyInt())
+ anyInt(),
+ )
}
@Test
fun enablesUnbundledChooser_whenFlagBecomesEnabled() {
// Arrange
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
chooserSelector.start()
verify(mockFeatureFlags).addListener(
- eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
+ flagListener.capture(),
+ )
verify(mockPackageManager, never()).setComponentEnabledSetting(
- any(), eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED), anyInt())
+ any(),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
+ anyInt(),
+ )
// Act
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id))
// Assert
- verify(mockPackageManager).setComponentEnabledSetting(
+ verify(mockPackageManager, times(2)).setComponentEnabledSetting(
eq(ComponentName("TestPackage", "TestClass")),
eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
- anyInt())
+ anyInt(),
+ )
}
@Test
fun disablesUnbundledChooser_whenFlagBecomesDisabled() {
// Arrange
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(true)
chooserSelector.start()
verify(mockFeatureFlags).addListener(
- eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
+ flagListener.capture(),
+ )
verify(mockPackageManager, never()).setComponentEnabledSetting(
- any(), eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED), anyInt())
+ any(),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
+ anyInt(),
+ )
// Act
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id))
// Assert
- verify(mockPackageManager).setComponentEnabledSetting(
+ verify(mockPackageManager, times(2)).setComponentEnabledSetting(
eq(ComponentName("TestPackage", "TestClass")),
eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
- anyInt())
+ anyInt(),
+ )
}
@Test
fun doesNothing_whenAnotherFlagChanges() {
// Arrange
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
chooserSelector.start()
verify(mockFeatureFlags).addListener(
- eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED), flagListener.capture())
+ eq<Flag<*>>(Flags.CHOOSER_UNBUNDLED),
+ flagListener.capture(),
+ )
clearInvocations(mockPackageManager)
// Act
- `when`(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
+ whenever(mockFeatureFlags.isEnabled(any<UnreleasedFlag>())).thenReturn(false)
flagListener.value.onFlagChanged(TestFlagEvent(Flags.CHOOSER_UNBUNDLED.id + 1))
// Assert
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
index a4e0825360df..588620646b73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui
+import android.content.Context
import android.graphics.Canvas
import android.graphics.Insets
import android.graphics.Path
@@ -48,6 +49,7 @@ class DisplayCutoutBaseViewTest : SysuiTestCase() {
@Mock private lateinit var mockCanvas: Canvas
@Mock private lateinit var mockRootView: View
@Mock private lateinit var mockDisplay: Display
+ @Mock private lateinit var mockContext: Context
private lateinit var cutoutBaseView: DisplayCutoutBaseView
private val cutout: DisplayCutout = DisplayCutout.Builder()
@@ -168,7 +170,9 @@ class DisplayCutoutBaseViewTest : SysuiTestCase() {
R.bool.config_fillMainBuiltInDisplayCutout, fillCutout)
cutoutBaseView = spy(DisplayCutoutBaseView(mContext))
- whenever(cutoutBaseView.display).thenReturn(mockDisplay)
+
+ whenever(cutoutBaseView.context).thenReturn(mockContext)
+ whenever(mockContext.display).thenReturn(mockDisplay)
whenever(mockDisplay.uniqueId).thenReturn("mockDisplayUniqueId")
whenever(cutoutBaseView.rootView).thenReturn(mockRootView)
whenever(cutoutBaseView.getPhysicalPixelDisplaySizeRatio()).thenReturn(1f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
index 054650bb8a75..8207fa6958f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui
+import android.content.Context
import android.graphics.Insets
import android.graphics.PixelFormat
import android.graphics.Rect
@@ -44,6 +45,7 @@ class ScreenDecorHwcLayerTest : SysuiTestCase() {
@Mock private lateinit var mockDisplay: Display
@Mock private lateinit var mockRootView: View
+ @Mock private lateinit var mockContext: Context
private val displayWidth = 100
private val displayHeight = 200
@@ -75,7 +77,8 @@ class ScreenDecorHwcLayerTest : SysuiTestCase() {
decorHwcLayer = Mockito.spy(ScreenDecorHwcLayer(mContext, decorationSupport))
whenever(decorHwcLayer.width).thenReturn(displayWidth)
whenever(decorHwcLayer.height).thenReturn(displayHeight)
- whenever(decorHwcLayer.display).thenReturn(mockDisplay)
+ whenever(decorHwcLayer.context).thenReturn(mockContext)
+ whenever(mockContext.display).thenReturn(mockDisplay)
whenever(decorHwcLayer.rootView).thenReturn(mockRootView)
whenever(mockRootView.left).thenReturn(0)
whenever(mockRootView.top).thenReturn(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt
new file mode 100644
index 000000000000..982f033d05e5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.battery
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.battery.BatterySpecs.BATTERY_HEIGHT
+import com.android.systemui.battery.BatterySpecs.BATTERY_HEIGHT_WITH_SHIELD
+import com.android.systemui.battery.BatterySpecs.BATTERY_WIDTH
+import com.android.systemui.battery.BatterySpecs.BATTERY_WIDTH_WITH_SHIELD
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SmallTest
+class AccessorizedBatteryDrawableTest : SysuiTestCase() {
+ @Test
+ fun intrinsicSize_shieldFalse_isBatterySize() {
+ val drawable = AccessorizedBatteryDrawable(context, frameColor = 0)
+ drawable.displayShield = false
+
+ val density = context.resources.displayMetrics.density
+ assertThat(drawable.intrinsicHeight).isEqualTo((BATTERY_HEIGHT * density).toInt())
+ assertThat(drawable.intrinsicWidth).isEqualTo((BATTERY_WIDTH * density).toInt())
+ }
+
+ @Test
+ fun intrinsicSize_shieldTrue_isBatteryPlusShieldSize() {
+ val drawable = AccessorizedBatteryDrawable(context, frameColor = 0)
+ drawable.displayShield = true
+
+ val density = context.resources.displayMetrics.density
+ assertThat(drawable.intrinsicHeight)
+ .isEqualTo((BATTERY_HEIGHT_WITH_SHIELD * density).toInt())
+ assertThat(drawable.intrinsicWidth).isEqualTo((BATTERY_WIDTH_WITH_SHIELD * density).toInt())
+ }
+
+ // TODO(b/255625888): Screenshot tests for this drawable would be amazing!
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
index 1d038a43ec8e..bc8f96198b27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
@@ -35,6 +35,8 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
@@ -59,6 +61,7 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase {
private Handler mHandler;
@Mock
private ContentResolver mContentResolver;
+ private FakeFeatureFlags mFeatureFlags;
@Mock
private BatteryController mBatteryController;
@@ -71,19 +74,13 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase {
when(mBatteryMeterView.getContext()).thenReturn(mContext);
when(mBatteryMeterView.getResources()).thenReturn(mContext.getResources());
- mController = new BatteryMeterViewController(
- mBatteryMeterView,
- mConfigurationController,
- mTunerService,
- mBroadcastDispatcher,
- mHandler,
- mContentResolver,
- mBatteryController
- );
+ mFeatureFlags = new FakeFeatureFlags();
+ mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, false);
}
@Test
public void onViewAttached_callbacksRegistered() {
+ initController();
mController.onViewAttached();
verify(mConfigurationController).addCallback(any());
@@ -101,6 +98,7 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase {
@Test
public void onViewDetached_callbacksUnregistered() {
+ initController();
// Set everything up first.
mController.onViewAttached();
@@ -114,6 +112,7 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase {
@Test
public void ignoreTunerUpdates_afterOnViewAttached_callbackUnregistered() {
+ initController();
// Start out receiving tuner updates
mController.onViewAttached();
@@ -124,10 +123,43 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase {
@Test
public void ignoreTunerUpdates_beforeOnViewAttached_callbackNeverRegistered() {
+ initController();
+
mController.ignoreTunerUpdates();
mController.onViewAttached();
verify(mTunerService, never()).addTunable(any(), any());
}
+
+ @Test
+ public void shieldFlagDisabled_viewNotified() {
+ mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, false);
+
+ initController();
+
+ verify(mBatteryMeterView).setDisplayShieldEnabled(false);
+ }
+
+ @Test
+ public void shieldFlagEnabled_viewNotified() {
+ mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, true);
+
+ initController();
+
+ verify(mBatteryMeterView).setDisplayShieldEnabled(true);
+ }
+
+ private void initController() {
+ mController = new BatteryMeterViewController(
+ mBatteryMeterView,
+ mConfigurationController,
+ mTunerService,
+ mBroadcastDispatcher,
+ mHandler,
+ mContentResolver,
+ mFeatureFlags,
+ mBatteryController
+ );
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
index b4ff2a5e1fbf..eb7d9c3900f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
@@ -17,7 +17,9 @@ package com.android.systemui.battery
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import android.widget.ImageView
import androidx.test.filters.SmallTest
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatteryMeterView.BatteryEstimateFetcher
import com.android.systemui.statusbar.policy.BatteryController.EstimateFetchCompletion
@@ -58,6 +60,182 @@ class BatteryMeterViewTest : SysuiTestCase() {
// No assert needed
}
+ @Test
+ fun contentDescription_unknown() {
+ mBatteryMeterView.onBatteryUnknownStateChanged(true)
+
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(R.string.accessibility_battery_unknown)
+ )
+ }
+
+ @Test
+ fun contentDescription_estimate() {
+ mBatteryMeterView.onBatteryLevelChanged(15, false)
+ mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+ mBatteryMeterView.setBatteryEstimateFetcher(Fetcher())
+
+ mBatteryMeterView.updatePercentText()
+
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(
+ R.string.accessibility_battery_level_with_estimate, 15, ESTIMATE
+ )
+ )
+ }
+
+ @Test
+ fun contentDescription_estimateAndOverheated() {
+ mBatteryMeterView.onBatteryLevelChanged(17, false)
+ mBatteryMeterView.onIsOverheatedChanged(true)
+ mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+ mBatteryMeterView.setBatteryEstimateFetcher(Fetcher())
+
+ mBatteryMeterView.updatePercentText()
+
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(
+ R.string.accessibility_battery_level_charging_paused_with_estimate,
+ 17,
+ ESTIMATE,
+ )
+ )
+ }
+
+ @Test
+ fun contentDescription_overheated() {
+ mBatteryMeterView.onBatteryLevelChanged(90, false)
+ mBatteryMeterView.onIsOverheatedChanged(true)
+
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(R.string.accessibility_battery_level_charging_paused, 90)
+ )
+ }
+
+ @Test
+ fun contentDescription_charging() {
+ mBatteryMeterView.onBatteryLevelChanged(45, true)
+
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(R.string.accessibility_battery_level_charging, 45)
+ )
+ }
+
+ @Test
+ fun contentDescription_notCharging() {
+ mBatteryMeterView.onBatteryLevelChanged(45, false)
+
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(R.string.accessibility_battery_level, 45)
+ )
+ }
+
+ @Test
+ fun changesFromEstimateToPercent_textAndContentDescriptionChanges() {
+ mBatteryMeterView.onBatteryLevelChanged(15, false)
+ mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+ mBatteryMeterView.setBatteryEstimateFetcher(Fetcher())
+
+ mBatteryMeterView.updatePercentText()
+
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(
+ R.string.accessibility_battery_level_with_estimate, 15, ESTIMATE
+ )
+ )
+
+ // Update the show mode from estimate to percent
+ mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ON)
+
+ assertThat(mBatteryMeterView.batteryPercentViewText).isEqualTo("15%")
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(R.string.accessibility_battery_level, 15)
+ )
+ }
+
+ @Test
+ fun contentDescription_manyUpdates_alwaysUpdated() {
+ // Overheated
+ mBatteryMeterView.onBatteryLevelChanged(90, false)
+ mBatteryMeterView.onIsOverheatedChanged(true)
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(R.string.accessibility_battery_level_charging_paused, 90)
+ )
+
+ // Overheated & estimate
+ mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+ mBatteryMeterView.setBatteryEstimateFetcher(Fetcher())
+ mBatteryMeterView.updatePercentText()
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(
+ R.string.accessibility_battery_level_charging_paused_with_estimate,
+ 90,
+ ESTIMATE,
+ )
+ )
+
+ // Just estimate
+ mBatteryMeterView.onIsOverheatedChanged(false)
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(
+ R.string.accessibility_battery_level_with_estimate,
+ 90,
+ ESTIMATE,
+ )
+ )
+
+ // Just percent
+ mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ON)
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(R.string.accessibility_battery_level, 90)
+ )
+
+ // Charging
+ mBatteryMeterView.onBatteryLevelChanged(90, true)
+ assertThat(mBatteryMeterView.contentDescription).isEqualTo(
+ context.getString(R.string.accessibility_battery_level_charging, 90)
+ )
+ }
+
+ @Test
+ fun isOverheatedChanged_true_drawableGetsTrue() {
+ mBatteryMeterView.setDisplayShieldEnabled(true)
+ val drawable = getBatteryDrawable()
+
+ mBatteryMeterView.onIsOverheatedChanged(true)
+
+ assertThat(drawable.displayShield).isTrue()
+ }
+
+ @Test
+ fun isOverheatedChanged_false_drawableGetsFalse() {
+ mBatteryMeterView.setDisplayShieldEnabled(true)
+ val drawable = getBatteryDrawable()
+
+ // Start as true
+ mBatteryMeterView.onIsOverheatedChanged(true)
+
+ // Update to false
+ mBatteryMeterView.onIsOverheatedChanged(false)
+
+ assertThat(drawable.displayShield).isFalse()
+ }
+
+ @Test
+ fun isOverheatedChanged_true_featureflagOff_drawableGetsFalse() {
+ mBatteryMeterView.setDisplayShieldEnabled(false)
+ val drawable = getBatteryDrawable()
+
+ mBatteryMeterView.onIsOverheatedChanged(true)
+
+ assertThat(drawable.displayShield).isFalse()
+ }
+
+ private fun getBatteryDrawable(): AccessorizedBatteryDrawable {
+ return (mBatteryMeterView.getChildAt(0) as ImageView)
+ .drawable as AccessorizedBatteryDrawable
+ }
+
private class Fetcher : BatteryEstimateFetcher {
override fun fetchBatteryTimeRemainingEstimate(
completion: EstimateFetchCompletion) {
@@ -68,4 +246,4 @@ class BatteryMeterViewTest : SysuiTestCase() {
private companion object {
const val ESTIMATE = "2 hours 2 minutes"
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatterySpecsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatterySpecsTest.kt
new file mode 100644
index 000000000000..39cb0476e429
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatterySpecsTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.battery
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.battery.BatterySpecs.BATTERY_HEIGHT
+import com.android.systemui.battery.BatterySpecs.BATTERY_HEIGHT_WITH_SHIELD
+import com.android.systemui.battery.BatterySpecs.BATTERY_WIDTH
+import com.android.systemui.battery.BatterySpecs.BATTERY_WIDTH_WITH_SHIELD
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SmallTest
+class BatterySpecsTest : SysuiTestCase() {
+ @Test
+ fun getFullBatteryHeight_shieldFalse_returnsMainHeight() {
+ val fullHeight = BatterySpecs.getFullBatteryHeight(56f, displayShield = false)
+
+ assertThat(fullHeight).isEqualTo(56f)
+ }
+
+ @Test
+ fun getFullBatteryHeight_shieldTrue_returnsMainHeightPlusShield() {
+ val mainHeight = BATTERY_HEIGHT * 5
+ val fullHeight = BatterySpecs.getFullBatteryHeight(mainHeight, displayShield = true)
+
+ // Since the main battery was scaled 5x, the output height should also be scaled 5x
+ val expectedFullHeight = BATTERY_HEIGHT_WITH_SHIELD * 5
+
+ assertThat(fullHeight).isWithin(.0001f).of(expectedFullHeight)
+ }
+
+ @Test
+ fun getFullBatteryWidth_shieldFalse_returnsMainWidth() {
+ val fullWidth = BatterySpecs.getFullBatteryWidth(33f, displayShield = false)
+
+ assertThat(fullWidth).isEqualTo(33f)
+ }
+
+ @Test
+ fun getFullBatteryWidth_shieldTrue_returnsMainWidthPlusShield() {
+ val mainWidth = BATTERY_WIDTH * 3.3f
+
+ val fullWidth = BatterySpecs.getFullBatteryWidth(mainWidth, displayShield = true)
+
+ // Since the main battery was scaled 3.3x, the output width should also be scaled 5x
+ val expectedFullWidth = BATTERY_WIDTH_WITH_SHIELD * 3.3f
+ assertThat(fullWidth).isWithin(.0001f).of(expectedFullWidth)
+ }
+
+ @Test
+ fun getMainBatteryHeight_shieldFalse_returnsFullHeight() {
+ val mainHeight = BatterySpecs.getMainBatteryHeight(89f, displayShield = false)
+
+ assertThat(mainHeight).isEqualTo(89f)
+ }
+
+ @Test
+ fun getMainBatteryHeight_shieldTrue_returnsNotFullHeight() {
+ val fullHeight = BATTERY_HEIGHT_WITH_SHIELD * 7.7f
+
+ val mainHeight = BatterySpecs.getMainBatteryHeight(fullHeight, displayShield = true)
+
+ // Since the full height was scaled 7.7x, the main height should also be scaled 7.7x.
+ val expectedHeight = BATTERY_HEIGHT * 7.7f
+ assertThat(mainHeight).isWithin(.0001f).of(expectedHeight)
+ }
+
+ @Test
+ fun getMainBatteryWidth_shieldFalse_returnsFullWidth() {
+ val mainWidth = BatterySpecs.getMainBatteryWidth(2345f, displayShield = false)
+
+ assertThat(mainWidth).isEqualTo(2345f)
+ }
+
+ @Test
+ fun getMainBatteryWidth_shieldTrue_returnsNotFullWidth() {
+ val fullWidth = BATTERY_WIDTH_WITH_SHIELD * 0.6f
+
+ val mainWidth = BatterySpecs.getMainBatteryWidth(fullWidth, displayShield = true)
+
+ // Since the full width was scaled 0.6x, the main height should also be scaled 0.6x.
+ val expectedWidth = BATTERY_WIDTH * 0.6f
+ assertThat(mainWidth).isWithin(.0001f).of(expectedWidth)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index d1107c612977..eaef159e9020 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -161,6 +161,25 @@ class AuthContainerViewTest : SysuiTestCase() {
}
@Test
+ fun testFocusLossAfterRotating() {
+ val container = initializeFingerprintContainer()
+ waitForIdleSync()
+
+ val requestID = authContainer?.requestId ?: 0L
+
+ verify(callback).onDialogAnimatedIn(requestID)
+ container.onOrientationChanged()
+ container.onWindowFocusChanged(false)
+ waitForIdleSync()
+
+ verify(callback, never()).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
+ eq<ByteArray?>(null), /* credentialAttestation */
+ eq(requestID)
+ )
+ }
+
+ @Test
fun testDismissesOnFocusLoss_hidesKeyboardWhenVisible() {
val container = initializeFingerprintContainer(
authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 90948ff3b769..53bc2c231d0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -43,7 +43,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -106,7 +106,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var udfpsEnrollView: UdfpsEnrollView
@Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
@Mock private lateinit var featureFlags: FeatureFlags
- @Mock private lateinit var bouncerInteractor: BouncerInteractor
+ @Mock private lateinit var mPrimaryBouncerInteractor: PrimaryBouncerInteractor
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
@@ -141,7 +141,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
configurationController, systemClock, keyguardStateController,
unlockedScreenOffAnimationController, udfpsDisplayMode, REQUEST_ID, reason,
controllerCallback, onTouch, activityLaunchAnimator, featureFlags,
- bouncerInteractor, isDebuggable
+ mPrimaryBouncerInteractor, isDebuggable
)
block()
}
@@ -191,10 +191,11 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT)
overlayParams = UdfpsOverlayParams(
sensorBounds,
+ sensorBounds,
DISPLAY_WIDTH,
DISPLAY_HEIGHT,
scaleFactor = 1f,
- rotation
+ rotation = rotation
)
block()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 25f26020830d..acdafe3e1c7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -73,7 +73,7 @@ import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -114,10 +114,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
-
// Unit under test
private UdfpsController mUdfpsController;
-
// Dependencies
private FakeExecutor mBiometricsExecutor;
@Mock
@@ -171,7 +169,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
private UdfpsDisplayMode mUdfpsDisplayMode;
@Mock
private FeatureFlags mFeatureFlags;
-
// Stuff for configuring mocks
@Mock
private UdfpsView mUdfpsView;
@@ -192,7 +189,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private AlternateUdfpsTouchProvider mAlternateTouchProvider;
@Mock
- private BouncerInteractor mBouncerInteractor;
+ private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
// Capture listeners so that they can be used to send events
@Captor
@@ -249,54 +246,42 @@ public class UdfpsControllerTest extends SysuiTestCase {
FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC,
true /* resetLockoutRequiresHardwareAuthToken */);
- List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
- props.add(mOpticalProps);
- props.add(mUltrasonicProps);
- when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
-
mFgExecutor = new FakeExecutor(new FakeSystemClock());
// Create a fake background executor.
mBiometricsExecutor = new FakeExecutor(new FakeSystemClock());
- mUdfpsController = new UdfpsController(
- mContext,
- execution,
- mLayoutInflater,
- mFingerprintManager,
- mWindowManager,
- mStatusBarStateController,
- mFgExecutor,
- new ShadeExpansionStateManager(),
- mStatusBarKeyguardViewManager,
- mDumpManager,
- mKeyguardUpdateMonitor,
- mFeatureFlags,
- mFalsingManager,
- mPowerManager,
- mAccessibilityManager,
- mLockscreenShadeTransitionController,
- mScreenLifecycle,
- mVibrator,
- mUdfpsHapticsSimulator,
- mUdfpsShell,
- mKeyguardStateController,
- mDisplayManager,
- mHandler,
- mConfigurationController,
- mSystemClock,
- mUnlockedScreenOffAnimationController,
- mSystemUIDialogManager,
- mLatencyTracker,
- mActivityLaunchAnimator,
- Optional.of(mAlternateTouchProvider),
- mBiometricsExecutor,
- mBouncerInteractor);
+ initUdfpsController(true /* hasAlternateTouchProvider */);
+ }
+
+ private void initUdfpsController(boolean hasAlternateTouchProvider) {
+ initUdfpsController(mOpticalProps, hasAlternateTouchProvider);
+ }
+
+ private void initUdfpsController(FingerprintSensorPropertiesInternal sensorProps,
+ boolean hasAlternateTouchProvider) {
+ reset(mFingerprintManager);
+ reset(mScreenLifecycle);
+
+ final Optional<AlternateUdfpsTouchProvider> alternateTouchProvider =
+ hasAlternateTouchProvider ? Optional.of(mAlternateTouchProvider) : Optional.empty();
+
+ mUdfpsController = new UdfpsController(mContext, new FakeExecution(), mLayoutInflater,
+ mFingerprintManager, mWindowManager, mStatusBarStateController, mFgExecutor,
+ new ShadeExpansionStateManager(), mStatusBarKeyguardViewManager, mDumpManager,
+ mKeyguardUpdateMonitor, mFeatureFlags, mFalsingManager, mPowerManager,
+ mAccessibilityManager, mLockscreenShadeTransitionController, mScreenLifecycle,
+ mVibrator, mUdfpsHapticsSimulator, mUdfpsShell, mKeyguardStateController,
+ mDisplayManager, mHandler, mConfigurationController, mSystemClock,
+ mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker,
+ mActivityLaunchAnimator, alternateTouchProvider, mBiometricsExecutor,
+ mPrimaryBouncerInteractor);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
mScreenObserver = mScreenObserverCaptor.getValue();
- mUdfpsController.updateOverlayParams(mOpticalProps, new UdfpsOverlayParams());
+
+ mUdfpsController.updateOverlayParams(sensorProps, new UdfpsOverlayParams());
mUdfpsController.setUdfpsDisplayMode(mUdfpsDisplayMode);
}
@@ -333,8 +318,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
}
@Test
- public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice()
- throws RemoteException {
+ public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
onActionMoveTouch_whenCanDismissLockScreen_entersDevice(false /* stale */);
}
@@ -405,14 +389,14 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN overlay was showing and the udfps bouncer is showing
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(true);
// WHEN the overlay is hidden
mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId);
mFgExecutor.runAllReady();
// THEN the udfps bouncer is reset
- verify(mStatusBarKeyguardViewManager).resetAlternateAuth(eq(true));
+ verify(mStatusBarKeyguardViewManager).hideAlternateBouncer(eq(true));
}
@Test
@@ -437,7 +421,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]};
final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90};
final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0],
- displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0]);
+ sensorBounds[0], displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0]);
for (int i1 = 0; i1 <= 1; ++i1) {
for (int i2 = 0; i2 <= 1; ++i2) {
@@ -445,9 +429,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
for (int i4 = 0; i4 <= 1; ++i4) {
for (int i5 = 0; i5 <= 1; ++i5) {
final UdfpsOverlayParams newParams = new UdfpsOverlayParams(
- sensorBounds[i1],
- displayWidth[i2], displayHeight[i3], scaleFactor[i4],
- rotation[i5]);
+ sensorBounds[i1], sensorBounds[i1], displayWidth[i2],
+ displayHeight[i3], scaleFactor[i4], rotation[i5]);
if (newParams.equals(oldParams)) {
continue;
@@ -490,8 +473,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
// Initialize the overlay.
mUdfpsController.updateOverlayParams(mOpticalProps,
- new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
- rotation));
+ new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
+ scaleFactor, rotation));
// Show the overlay.
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
@@ -501,8 +484,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
// Update overlay with the same parameters.
mUdfpsController.updateOverlayParams(mOpticalProps,
- new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
- rotation));
+ new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
+ scaleFactor, rotation));
mFgExecutor.runAllReady();
// Ensure the overlay was not recreated.
@@ -522,8 +505,37 @@ public class UdfpsControllerTest extends SysuiTestCase {
new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0);
}
+ private static class TestParams {
+ public final FingerprintSensorPropertiesInternal sensorProps;
+ public final boolean hasAlternateTouchProvider;
+
+ TestParams(FingerprintSensorPropertiesInternal sensorProps,
+ boolean hasAlternateTouchProvider) {
+ this.sensorProps = sensorProps;
+ this.hasAlternateTouchProvider = hasAlternateTouchProvider;
+ }
+ }
+
+ private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) {
+ for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps,
+ mUltrasonicProps)) {
+ for (boolean hasAlternateTouchProvider : new boolean[]{false, true}) {
+ initUdfpsController(sensorProps, hasAlternateTouchProvider);
+ testParamsConsumer.accept(new TestParams(sensorProps, hasAlternateTouchProvider));
+ }
+ }
+ }
+
@Test
- public void onTouch_propagatesTouchInNativeOrientationAndResolution() throws RemoteException {
+ public void onTouch_propagatesTouchInNativeOrientationAndResolution() {
+ runWithAllParams(
+ this::onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized);
+ }
+
+ private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized(
+ TestParams testParams) throws RemoteException {
+ reset(mUdfpsView);
+
final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner.
final int displayWidth = 1080;
final int displayHeight = 1920;
@@ -542,15 +554,15 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// Show the overlay.
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
// Test ROTATION_0
- mUdfpsController.updateOverlayParams(mOpticalProps,
- new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
- Surface.ROTATION_0));
+ mUdfpsController.updateOverlayParams(testParams.sensorProps,
+ new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
+ scaleFactor, Surface.ROTATION_0));
MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor,
touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
@@ -560,14 +572,21 @@ public class UdfpsControllerTest extends SysuiTestCase {
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricsExecutor.runAllReady();
event.recycle();
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor));
+ }
// Test ROTATION_90
reset(mAlternateTouchProvider);
- mUdfpsController.updateOverlayParams(mOpticalProps,
- new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
- Surface.ROTATION_90));
+ reset(mFingerprintManager);
+ mUdfpsController.updateOverlayParams(testParams.sensorProps,
+ new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
+ scaleFactor, Surface.ROTATION_90));
event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricsExecutor.runAllReady();
@@ -576,14 +595,21 @@ public class UdfpsControllerTest extends SysuiTestCase {
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricsExecutor.runAllReady();
event.recycle();
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor));
+ }
// Test ROTATION_270
reset(mAlternateTouchProvider);
- mUdfpsController.updateOverlayParams(mOpticalProps,
- new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
- Surface.ROTATION_270));
+ reset(mFingerprintManager);
+ mUdfpsController.updateOverlayParams(testParams.sensorProps,
+ new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
+ scaleFactor, Surface.ROTATION_270));
event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricsExecutor.runAllReady();
@@ -592,14 +618,21 @@ public class UdfpsControllerTest extends SysuiTestCase {
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricsExecutor.runAllReady();
event.recycle();
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor));
+ }
// Test ROTATION_180
reset(mAlternateTouchProvider);
- mUdfpsController.updateOverlayParams(mOpticalProps,
- new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
- Surface.ROTATION_180));
+ reset(mFingerprintManager);
+ mUdfpsController.updateOverlayParams(testParams.sensorProps,
+ new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
+ scaleFactor, Surface.ROTATION_180));
// ROTATION_180 is not supported. It should be treated like ROTATION_0.
event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
@@ -609,26 +642,22 @@ public class UdfpsControllerTest extends SysuiTestCase {
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricsExecutor.runAllReady();
event.recycle();
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
- }
-
- private void runForAllUdfpsTypes(
- ThrowingConsumer<FingerprintSensorPropertiesInternal> sensorPropsConsumer) {
- for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps,
- mUltrasonicProps)) {
- mUdfpsController.updateOverlayParams(sensorProps, new UdfpsOverlayParams());
- sensorPropsConsumer.accept(sensorProps);
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
+ eq(expectedY), eq(expectedMinor), eq(expectedMajor));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor));
}
}
@Test
public void fingerDown() {
- runForAllUdfpsTypes(this::fingerDownForSensor);
+ runWithAllParams(this::fingerDownParameterized);
}
- private void fingerDownForSensor(FingerprintSensorPropertiesInternal sensorProps)
- throws RemoteException {
+ private void fingerDownParameterized(TestParams testParams) throws RemoteException {
reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mLatencyTracker,
mKeyguardUpdateMonitor);
@@ -638,7 +667,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
// GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -656,14 +685,22 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFgExecutor.runAllReady();
- // THEN FingerprintManager is notified about onPointerDown
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f),
- eq(0f));
- verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
- anyFloat(), anyFloat());
+ // THEN the touch provider is notified about onPointerDown.
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f),
+ eq(0f));
+ verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
+ anyInt(), anyFloat(), anyFloat());
+ verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(0), eq(0), eq(0f), eq(0f));
+ verify(mAlternateTouchProvider, never()).onPointerDown(anyInt(), anyInt(), anyInt(),
+ anyFloat(), anyFloat());
+ }
// AND display configuration begins
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
} else {
@@ -672,16 +709,27 @@ public class UdfpsControllerTest extends SysuiTestCase {
verify(mUdfpsView, never()).configureDisplay(any());
}
verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// AND onDisplayConfigured notifies FingerprintManager about onUiReady
mOnDisplayConfiguredCaptor.getValue().run();
mBiometricsExecutor.runAllReady();
- InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
- inOrder.verify(mAlternateTouchProvider).onUiReady();
- inOrder.verify(mLatencyTracker).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+ if (testParams.hasAlternateTouchProvider) {
+ InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
+ inOrder.verify(mAlternateTouchProvider).onUiReady();
+ inOrder.verify(mLatencyTracker).onActionEnd(
+ eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+ verify(mFingerprintManager, never()).onUiReady(anyLong(), anyInt());
+ } else {
+ InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
+ inOrder.verify(mFingerprintManager).onUiReady(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId));
+ inOrder.verify(mLatencyTracker).onActionEnd(
+ eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
+ verify(mAlternateTouchProvider, never()).onUiReady();
+ }
} else {
+ verify(mFingerprintManager, never()).onUiReady(anyLong(), anyInt());
verify(mAlternateTouchProvider, never()).onUiReady();
verify(mLatencyTracker, never()).onActionEnd(
eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
@@ -690,24 +738,23 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void aodInterrupt() {
- runForAllUdfpsTypes(this::aodInterruptForSensor);
+ runWithAllParams(this::aodInterruptParameterized);
}
- private void aodInterruptForSensor(FingerprintSensorPropertiesInternal sensorProps)
- throws RemoteException {
+ private void aodInterruptParameterized(TestParams testParams) throws RemoteException {
mUdfpsController.cancelAodInterrupt();
reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mKeyguardUpdateMonitor);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
// GIVEN that the overlay is showing and screen is on and fp is running
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// THEN display configuration begins
// AND onDisplayConfigured notifies FingerprintManager about onUiReady
verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
@@ -716,29 +763,37 @@ public class UdfpsControllerTest extends SysuiTestCase {
verify(mUdfpsView, never()).configureDisplay(mOnDisplayConfiguredCaptor.capture());
}
mBiometricsExecutor.runAllReady();
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
- eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */);
- verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
- anyFloat(), anyFloat());
- verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
+
+ if (testParams.hasAlternateTouchProvider) {
+ verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0),
+ eq(3f) /* minor */, eq(2f) /* major */);
+ verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
+ anyInt(), anyFloat(), anyFloat());
+ verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
+ } else {
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(0), eq(0), eq(3f) /* minor */,
+ eq(2f) /* major */);
+ verify(mAlternateTouchProvider, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
+ anyFloat(), anyFloat());
+ }
}
@Test
public void cancelAodInterrupt() {
- runForAllUdfpsTypes(this::cancelAodInterruptForSensor);
+ runWithAllParams(this::cancelAodInterruptParameterized);
}
- private void cancelAodInterruptForSensor(FingerprintSensorPropertiesInternal sensorProps)
- throws RemoteException {
+ private void cancelAodInterruptParameterized(TestParams testParams) throws RemoteException {
reset(mUdfpsView);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
// WHEN it is cancelled
mUdfpsController.cancelAodInterrupt();
@@ -755,21 +810,20 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void aodInterruptTimeout() {
- runForAllUdfpsTypes(this::aodInterruptTimeoutForSensor);
+ runWithAllParams(this::aodInterruptTimeoutParameterized);
}
- private void aodInterruptTimeoutForSensor(FingerprintSensorPropertiesInternal sensorProps)
- throws RemoteException {
+ private void aodInterruptTimeoutParameterized(TestParams testParams) throws RemoteException {
reset(mUdfpsView);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
} else {
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
@@ -777,7 +831,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN it times out
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// THEN the display is unconfigured.
verify(mUdfpsView).unconfigureDisplay();
} else {
@@ -788,23 +842,23 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void aodInterruptCancelTimeoutActionOnFingerUp() {
- runForAllUdfpsTypes(this::aodInterruptCancelTimeoutActionOnFingerUpForSensor);
+ runWithAllParams(this::aodInterruptCancelTimeoutActionOnFingerUpParameterized);
}
- private void aodInterruptCancelTimeoutActionOnFingerUpForSensor(
- FingerprintSensorPropertiesInternal sensorProps) throws RemoteException {
+ private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams)
+ throws RemoteException {
reset(mUdfpsView);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the ACTION_UP event
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
} else {
@@ -834,7 +888,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
moveEvent.recycle();
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the finger up event
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
} else {
@@ -845,7 +899,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// THEN the display should be unconfigured once. If the timeout action is not
// cancelled, the display would be unconfigured twice which would cause two
// FP attempts.
@@ -857,23 +911,23 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void aodInterruptCancelTimeoutActionOnAcquired() {
- runForAllUdfpsTypes(this::aodInterruptCancelTimeoutActionOnAcquiredForSensor);
+ runWithAllParams(this::aodInterruptCancelTimeoutActionOnAcquiredParameterized);
}
- private void aodInterruptCancelTimeoutActionOnAcquiredForSensor(
- FingerprintSensorPropertiesInternal sensorProps) throws RemoteException {
+ private void aodInterruptCancelTimeoutActionOnAcquiredParameterized(TestParams testParams)
+ throws RemoteException {
reset(mUdfpsView);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the acquired event
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
} else {
@@ -881,7 +935,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
}
// WHEN acquired is received
- mOverlayController.onAcquired(sensorProps.sensorId,
+ mOverlayController.onAcquired(testParams.sensorProps.sensorId,
BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD);
// Configure UdfpsView to accept the ACTION_DOWN event
@@ -901,7 +955,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
moveEvent.recycle();
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the finger up event
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
} else {
@@ -912,7 +966,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- if (sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
+ if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// THEN the display should be unconfigured once. If the timeout action is not
// cancelled, the display would be unconfigured twice which would cause two
// FP attempts.
@@ -924,15 +978,14 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void aodInterruptScreenOff() {
- runForAllUdfpsTypes(this::aodInterruptScreenOffForSensor);
+ runWithAllParams(this::aodInterruptScreenOffParameterized);
}
- private void aodInterruptScreenOffForSensor(FingerprintSensorPropertiesInternal sensorProps)
- throws RemoteException {
+ private void aodInterruptScreenOffParameterized(TestParams testParams) throws RemoteException {
reset(mUdfpsView);
// GIVEN screen off
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOff();
mFgExecutor.runAllReady();
@@ -946,17 +999,16 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void aodInterrupt_fingerprintNotRunning() {
- runForAllUdfpsTypes(this::aodInterrupt_fingerprintNotRunningForSensor);
+ runWithAllParams(this::aodInterrupt_fingerprintNotRunningParameterized);
}
- private void aodInterrupt_fingerprintNotRunningForSensor(
- FingerprintSensorPropertiesInternal sensorProps) throws RemoteException {
+ private void aodInterrupt_fingerprintNotRunningParameterized(TestParams testParams)
+ throws RemoteException {
reset(mUdfpsView);
// GIVEN showing overlay
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
- mUdfpsOverlayControllerCallback);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
index e5c7a42c06a6..75629f451526 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
@@ -30,7 +30,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionListener;
@@ -72,7 +72,7 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase {
protected @Mock UdfpsController mUdfpsController;
protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
protected @Mock KeyguardBouncer mBouncer;
- protected @Mock BouncerInteractor mBouncerInteractor;
+ protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
protected FakeSystemClock mSystemClock = new FakeSystemClock();
@@ -86,9 +86,9 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase {
private @Captor ArgumentCaptor<ShadeExpansionListener> mExpansionListenerCaptor;
protected List<ShadeExpansionListener> mExpansionListeners;
- private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor>
- mAltAuthInterceptorCaptor;
- protected StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor;
+ private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.AlternateBouncer>
+ mAlternateBouncerCaptor;
+ protected StatusBarKeyguardViewManager.AlternateBouncer mAlternateBouncer;
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
mKeyguardStateControllerCallbackCaptor;
@@ -131,9 +131,9 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase {
}
protected void captureAltAuthInterceptor() {
- verify(mStatusBarKeyguardViewManager).setAlternateAuthInterceptor(
- mAltAuthInterceptorCaptor.capture());
- mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue();
+ verify(mStatusBarKeyguardViewManager).setAlternateBouncer(
+ mAlternateBouncerCaptor.capture());
+ mAlternateBouncer = mAlternateBouncerCaptor.getValue();
}
protected void captureKeyguardStateControllerCallback() {
@@ -149,7 +149,7 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase {
protected UdfpsKeyguardViewController createUdfpsKeyguardViewController(
boolean useModernBouncer) {
mFeatureFlags.set(Flags.MODERN_BOUNCER, useModernBouncer);
- when(mStatusBarKeyguardViewManager.getBouncer()).thenReturn(
+ when(mStatusBarKeyguardViewManager.getPrimaryBouncer()).thenReturn(
useModernBouncer ? null : mBouncer);
return new UdfpsKeyguardViewController(
mView,
@@ -167,6 +167,6 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase {
mUdfpsController,
mActivityLaunchAnimator,
mFeatureFlags,
- mBouncerInteractor);
+ mPrimaryBouncerInteractor);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 55b61948ee45..16728b6f2ab9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -46,9 +46,9 @@ import org.mockito.Captor;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewControllerBaseTest {
- private @Captor ArgumentCaptor<KeyguardBouncer.BouncerExpansionCallback>
+ private @Captor ArgumentCaptor<KeyguardBouncer.PrimaryBouncerExpansionCallback>
mBouncerExpansionCallbackCaptor;
- private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
+ private KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback;
@Override
public UdfpsKeyguardViewController createUdfpsKeyguardViewController() {
@@ -63,7 +63,7 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController
captureBouncerExpansionCallback();
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
- when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
mBouncerExpansionCallback.onVisibilityChanged(true);
assertTrue(mController.shouldPauseAuth());
@@ -239,13 +239,13 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController
sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
assertTrue(mController.shouldPauseAuth());
- mAltAuthInterceptor.showAlternateAuthBouncer(); // force show
+ mAlternateBouncer.showAlternateBouncer(); // force show
assertFalse(mController.shouldPauseAuth());
- assertTrue(mAltAuthInterceptor.isShowingAlternateAuthBouncer());
+ assertTrue(mAlternateBouncer.isShowingAlternateBouncer());
- mAltAuthInterceptor.hideAlternateAuthBouncer(); // stop force show
+ mAlternateBouncer.hideAlternateBouncer(); // stop force show
assertTrue(mController.shouldPauseAuth());
- assertFalse(mAltAuthInterceptor.isShowingAlternateAuthBouncer());
+ assertFalse(mAlternateBouncer.isShowingAlternateBouncer());
}
@Test
@@ -258,7 +258,7 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController
mController.onViewDetached();
// THEN remove alternate auth interceptor
- verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAltAuthInterceptor);
+ verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAlternateBouncer);
}
@Test
@@ -268,14 +268,15 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController
captureAltAuthInterceptor();
// GIVEN udfps bouncer isn't showing
- mAltAuthInterceptor.hideAlternateAuthBouncer();
+ mAlternateBouncer.hideAlternateBouncer();
// WHEN touch is observed outside the view
mController.onTouchOutsideView();
// THEN bouncer / alt auth methods are never called
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
- verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).hideAlternateBouncer(anyBoolean());
}
@Test
@@ -285,32 +286,33 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController
captureAltAuthInterceptor();
// GIVEN udfps bouncer is showing
- mAltAuthInterceptor.showAlternateAuthBouncer();
+ mAlternateBouncer.showAlternateBouncer();
// WHEN touch is observed outside the view 200ms later (just within threshold)
mSystemClock.advanceTime(200);
mController.onTouchOutsideView();
// THEN bouncer / alt auth methods are never called because not enough time has passed
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
- verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).hideAlternateBouncer(anyBoolean());
}
@Test
- public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() {
+ public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showPrimaryBouncer() {
// GIVEN view is attached
mController.onViewAttached();
captureAltAuthInterceptor();
// GIVEN udfps bouncer is showing
- mAltAuthInterceptor.showAlternateAuthBouncer();
+ mAlternateBouncer.showAlternateBouncer();
// WHEN touch is observed outside the view 205ms later
mSystemClock.advanceTime(205);
mController.onTouchOutsideView();
// THEN show the bouncer
- verify(mStatusBarKeyguardViewManager).showBouncer(eq(true));
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(eq(true));
}
@Test
@@ -341,7 +343,7 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController
when(mResourceContext.getString(anyInt())).thenReturn("test string");
// WHEN status bar expansion is 0 but udfps bouncer is requested
- mAltAuthInterceptor.showAlternateAuthBouncer();
+ mAlternateBouncer.showAlternateBouncer();
// THEN alpha is 255
verify(mView).setUnpausedAlpha(255);
@@ -372,7 +374,7 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController
captureKeyguardStateControllerCallback();
captureAltAuthInterceptor();
updateStatusBarExpansion(1f, true);
- mAltAuthInterceptor.showAlternateAuthBouncer();
+ mAlternateBouncer.showAlternateBouncer();
reset(mView);
// WHEN we're transitioning to the full shade
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
index 7b1976811868..68e744e53843 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
@@ -25,8 +25,8 @@ import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
-import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.KeyguardBouncer
import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -59,14 +59,14 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle
}
override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewController? {
- mBouncerInteractor =
- BouncerInteractor(
+ mPrimaryBouncerInteractor =
+ PrimaryBouncerInteractor(
keyguardBouncerRepository,
mock(BouncerView::class.java),
mock(Handler::class.java),
mKeyguardStateController,
mock(KeyguardSecurityModel::class.java),
- mock(BouncerCallbackInteractor::class.java),
+ mock(PrimaryBouncerCallbackInteractor::class.java),
mock(FalsingCollector::class.java),
mock(DismissCallbackRegistry::class.java),
mock(KeyguardBypassController::class.java),
@@ -86,7 +86,7 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle
// WHEN the bouncer expansion is VISIBLE
val job = mController.listenForBouncerExpansion(this)
- keyguardBouncerRepository.setVisible(true)
+ keyguardBouncerRepository.setPrimaryVisible(true)
keyguardBouncerRepository.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
yield()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index b78c06391057..d550b927154c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
@@ -40,8 +40,8 @@ import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.nullable
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
private const val SENSOR_X = 50
private const val SENSOR_Y = 250
@@ -68,7 +68,8 @@ class UdfpsViewTest : SysuiTestCase() {
view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
view.animationViewController = animationViewController
val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
- view.overlayParams = UdfpsOverlayParams(sensorBounds, 1920, 1080, 1f, Surface.ROTATION_0)
+ view.overlayParams = UdfpsOverlayParams(sensorBounds, sensorBounds, 1920,
+ 1080, 1f, Surface.ROTATION_0)
view.setUdfpsDisplayModeProvider(hbmProvider)
ViewUtils.attachView(view)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 2af055783c22..d1597149ce0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -24,7 +24,7 @@ import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.ripple.RippleView
+import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
new file mode 100644
index 000000000000..0b72a68005a6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
@@ -0,0 +1,112 @@
+package com.android.systemui.controls.management
+
+import android.content.ComponentName
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.CustomIconCache
+import com.android.systemui.controls.controller.ControlsControllerImpl
+import com.android.systemui.controls.ui.ControlsUiController
+import java.util.concurrent.CountDownLatch
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ControlsEditingActivityTest : SysuiTestCase() {
+ @Mock lateinit var controller: ControlsControllerImpl
+
+ @Mock lateinit var broadcastDispatcher: BroadcastDispatcher
+
+ @Mock lateinit var customIconCache: CustomIconCache
+
+ @Mock lateinit var uiController: ControlsUiController
+
+ private lateinit var controlsEditingActivity: ControlsEditingActivity_Factory
+ private var latch: CountDownLatch = CountDownLatch(1)
+
+ @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
+ @Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
+
+ @Rule
+ @JvmField
+ var activityRule =
+ ActivityTestRule(
+ object :
+ SingleActivityFactory<TestableControlsEditingActivity>(
+ TestableControlsEditingActivity::class.java
+ ) {
+ override fun create(intent: Intent?): TestableControlsEditingActivity {
+ return TestableControlsEditingActivity(
+ controller,
+ broadcastDispatcher,
+ customIconCache,
+ uiController,
+ mockDispatcher,
+ latch
+ )
+ }
+ },
+ false,
+ false
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ val intent = Intent()
+ intent.putExtra(ControlsEditingActivity.EXTRA_STRUCTURE, "TestTitle")
+ val cname = ComponentName("TestPackageName", "TestClassName")
+ intent.putExtra(Intent.EXTRA_COMPONENT_NAME, cname)
+ activityRule.launchActivity(intent)
+ }
+
+ @Test
+ fun testBackCallbackRegistrationAndUnregistration() {
+ // 1. ensure that launching the activity results in it registering a callback
+ verify(mockDispatcher)
+ .registerOnBackInvokedCallback(
+ ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ captureCallback.capture()
+ )
+ activityRule.finishActivity()
+ latch.await() // ensure activity is finished
+ // 2. ensure that when the activity is finished, it unregisters the same callback
+ verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
+ }
+
+ public class TestableControlsEditingActivity(
+ private val controller: ControlsControllerImpl,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val customIconCache: CustomIconCache,
+ private val uiController: ControlsUiController,
+ private val mockDispatcher: OnBackInvokedDispatcher,
+ private val latch: CountDownLatch
+ ) : ControlsEditingActivity(controller, broadcastDispatcher, customIconCache, uiController) {
+ override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
+ return mockDispatcher
+ }
+
+ override fun onStop() {
+ super.onStop()
+ // ensures that test runner thread does not proceed until ui thread is done
+ latch.countDown()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
new file mode 100644
index 000000000000..4b0f7e6cd736
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -0,0 +1,122 @@
+package com.android.systemui.controls.management
+
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.controller.ControlsControllerImpl
+import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.dagger.qualifiers.Main
+import com.google.common.util.concurrent.MoreExecutors
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ControlsFavoritingActivityTest : SysuiTestCase() {
+ @Main private val executor: Executor = MoreExecutors.directExecutor()
+
+ @Mock lateinit var controller: ControlsControllerImpl
+
+ @Mock lateinit var listingController: ControlsListingController
+
+ @Mock lateinit var broadcastDispatcher: BroadcastDispatcher
+
+ @Mock lateinit var uiController: ControlsUiController
+
+ private lateinit var controlsFavoritingActivity: ControlsFavoritingActivity_Factory
+ private var latch: CountDownLatch = CountDownLatch(1)
+
+ @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
+ @Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
+
+ @Rule
+ @JvmField
+ var activityRule =
+ ActivityTestRule(
+ object :
+ SingleActivityFactory<TestableControlsFavoritingActivity>(
+ TestableControlsFavoritingActivity::class.java
+ ) {
+ override fun create(intent: Intent?): TestableControlsFavoritingActivity {
+ return TestableControlsFavoritingActivity(
+ executor,
+ controller,
+ listingController,
+ broadcastDispatcher,
+ uiController,
+ mockDispatcher,
+ latch
+ )
+ }
+ },
+ false,
+ false
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ val intent = Intent()
+ intent.putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
+ activityRule.launchActivity(intent)
+ }
+
+ @Test
+ fun testBackCallbackRegistrationAndUnregistration() {
+ // 1. ensure that launching the activity results in it registering a callback
+ verify(mockDispatcher)
+ .registerOnBackInvokedCallback(
+ ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ captureCallback.capture()
+ )
+ activityRule.finishActivity()
+ latch.await() // ensure activity is finished
+ // 2. ensure that when the activity is finished, it unregisters the same callback
+ verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
+ }
+
+ public class TestableControlsFavoritingActivity(
+ executor: Executor,
+ controller: ControlsControllerImpl,
+ listingController: ControlsListingController,
+ broadcastDispatcher: BroadcastDispatcher,
+ uiController: ControlsUiController,
+ private val mockDispatcher: OnBackInvokedDispatcher,
+ private val latch: CountDownLatch
+ ) :
+ ControlsFavoritingActivity(
+ executor,
+ controller,
+ listingController,
+ broadcastDispatcher,
+ uiController
+ ) {
+ override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
+ return mockDispatcher
+ }
+
+ override fun onStop() {
+ super.onStop()
+ // ensures that test runner thread does not proceed until ui thread is done
+ latch.countDown()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index db41d8d37a43..dedc7239bf85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -16,27 +16,42 @@
package com.android.systemui.controls.management
+import android.Manifest
import android.content.ComponentName
import android.content.Context
import android.content.ContextWrapper
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
+import android.os.Bundle
import android.os.UserHandle
+import android.service.controls.ControlsProviderService
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.settingslib.applications.ServiceListing
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags.USE_APP_PANELS
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argThat
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import org.junit.After
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatcher
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.mock
@@ -51,10 +66,8 @@ import java.util.concurrent.Executor
class ControlsListingControllerImplTest : SysuiTestCase() {
companion object {
- private const val TEST_LABEL = "TEST_LABEL"
- private const val TEST_PERMISSION = "permission"
- fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
- fun <T> any(): T = Mockito.any<T>()
+ private const val FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
}
@Mock
@@ -63,15 +76,17 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
private lateinit var mockCallback: ControlsListingController.ControlsListingCallback
@Mock
private lateinit var mockCallbackOther: ControlsListingController.ControlsListingCallback
- @Mock
- private lateinit var serviceInfo: ServiceInfo
- @Mock
- private lateinit var serviceInfo2: ServiceInfo
@Mock(stubOnly = true)
private lateinit var userTracker: UserTracker
+ @Mock(stubOnly = true)
+ private lateinit var dumpManager: DumpManager
+ @Mock
+ private lateinit var packageManager: PackageManager
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
- private var componentName = ComponentName("pkg1", "class1")
- private var componentName2 = ComponentName("pkg2", "class2")
+ private var componentName = ComponentName("pkg", "class1")
+ private var activityName = ComponentName("pkg", "activity")
private val executor = FakeExecutor(FakeSystemClock())
@@ -87,9 +102,15 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(serviceInfo.componentName).thenReturn(componentName)
- `when`(serviceInfo2.componentName).thenReturn(componentName2)
`when`(userTracker.userId).thenReturn(user)
+ `when`(userTracker.userContext).thenReturn(context)
+ // Return disabled by default
+ `when`(packageManager.getComponentEnabledSetting(any()))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED)
+ mContext.setMockPackageManager(packageManager)
+
+ // Return true by default, we'll test the false path
+ `when`(featureFlags.isEnabled(USE_APP_PANELS)).thenReturn(true)
val wrapper = object : ContextWrapper(mContext) {
override fun createContextAsUser(user: UserHandle, flags: Int): Context {
@@ -97,7 +118,14 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
}
}
- controller = ControlsListingControllerImpl(wrapper, executor, { mockSL }, userTracker)
+ controller = ControlsListingControllerImpl(
+ wrapper,
+ executor,
+ { mockSL },
+ userTracker,
+ dumpManager,
+ featureFlags
+ )
verify(mockSL).addCallback(capture(serviceListingCallbackCaptor))
}
@@ -123,9 +151,16 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
Unit
}
`when`(mockServiceListing.reload()).then {
- callback?.onServicesReloaded(listOf(serviceInfo))
+ callback?.onServicesReloaded(listOf(ServiceInfo(componentName)))
}
- ControlsListingControllerImpl(mContext, exec, { mockServiceListing }, userTracker)
+ ControlsListingControllerImpl(
+ mContext,
+ exec,
+ { mockServiceListing },
+ userTracker,
+ dumpManager,
+ featureFlags
+ )
}
@Test
@@ -148,7 +183,7 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
@Test
fun testCallbackGetsList() {
- val list = listOf(serviceInfo)
+ val list = listOf(ServiceInfo(componentName))
controller.addCallback(mockCallback)
controller.addCallback(mockCallbackOther)
@@ -188,6 +223,8 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
@Test
fun testChangeUserSendsCorrectServiceUpdate() {
+ val serviceInfo = ServiceInfo(componentName)
+
val list = listOf(serviceInfo)
controller.addCallback(mockCallback)
@@ -223,4 +260,284 @@ class ControlsListingControllerImplTest : SysuiTestCase() {
verify(mockCallback).onServicesUpdated(capture(captor))
assertEquals(0, captor.value.size)
}
+
+ @Test
+ fun test_nullPanelActivity() {
+ val list = listOf(ServiceInfo(componentName))
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testNoActivity_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityWithoutPermission_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ setUpQueryResult(listOf(ActivityInfo(activityName)))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityPermissionNotExported_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ setUpQueryResult(listOf(
+ ActivityInfo(activityName, permission = Manifest.permission.BIND_CONTROLS)
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityDisabled_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityEnabled_correctPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertEquals(activityName, controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityDefaultEnabled_correctPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ enabled = true,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertEquals(activityName, controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityDefaultDisabled_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ enabled = false,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityDefaultEnabled_flagDisabled_nullPanel() {
+ `when`(featureFlags.isEnabled(USE_APP_PANELS)).thenReturn(false)
+ val serviceInfo = ServiceInfo(
+ componentName,
+ activityName,
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ enabled = true,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ @Test
+ fun testActivityDifferentPackage_nullPanel() {
+ val serviceInfo = ServiceInfo(
+ componentName,
+ ComponentName("other_package", "cls")
+ )
+
+ `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+ setUpQueryResult(listOf(
+ ActivityInfo(
+ activityName,
+ enabled = true,
+ exported = true,
+ permission = Manifest.permission.BIND_CONTROLS
+ )
+ ))
+
+ val list = listOf(serviceInfo)
+ serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+ executor.runAllReady()
+
+ assertNull(controller.getCurrentServices()[0].panelActivity)
+ }
+
+ private fun ServiceInfo(
+ componentName: ComponentName,
+ panelActivityComponentName: ComponentName? = null
+ ): ServiceInfo {
+ return ServiceInfo().apply {
+ packageName = componentName.packageName
+ name = componentName.className
+ panelActivityComponentName?.let {
+ metaData = Bundle().apply {
+ putString(
+ ControlsProviderService.META_DATA_PANEL_ACTIVITY,
+ it.flattenToShortString()
+ )
+ }
+ }
+ }
+ }
+
+ private fun ActivityInfo(
+ componentName: ComponentName,
+ exported: Boolean = false,
+ enabled: Boolean = true,
+ permission: String? = null
+ ): ActivityInfo {
+ return ActivityInfo().apply {
+ packageName = componentName.packageName
+ name = componentName.className
+ this.permission = permission
+ this.exported = exported
+ this.enabled = enabled
+ }
+ }
+
+ private fun setUpQueryResult(infos: List<ActivityInfo>) {
+ `when`(
+ packageManager.queryIntentActivitiesAsUser(
+ argThat(IntentMatcher(activityName)),
+ argThat(FlagsMatcher(FLAGS)),
+ eq(UserHandle.of(user))
+ )
+ ).thenReturn(infos.map {
+ ResolveInfo().apply { activityInfo = it }
+ })
+ }
+
+ private class IntentMatcher(
+ private val componentName: ComponentName
+ ) : ArgumentMatcher<Intent> {
+ override fun matches(argument: Intent?): Boolean {
+ return argument?.component == componentName
+ }
+ }
+
+ private class FlagsMatcher(
+ private val flags: Long
+ ) : ArgumentMatcher<PackageManager.ResolveInfoFlags> {
+ override fun matches(argument: PackageManager.ResolveInfoFlags?): Boolean {
+ return flags == argument?.value
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
new file mode 100644
index 000000000000..acc62227cde6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.management
+
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.google.common.util.concurrent.MoreExecutors
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ControlsProviderSelectorActivityTest : SysuiTestCase() {
+ @Main private val executor: Executor = MoreExecutors.directExecutor()
+
+ @Background private val backExecutor: Executor = MoreExecutors.directExecutor()
+
+ @Mock lateinit var listingController: ControlsListingController
+
+ @Mock lateinit var controlsController: ControlsController
+
+ @Mock lateinit var broadcastDispatcher: BroadcastDispatcher
+
+ @Mock lateinit var uiController: ControlsUiController
+
+ private lateinit var controlsProviderSelectorActivity: ControlsProviderSelectorActivity_Factory
+ private var latch: CountDownLatch = CountDownLatch(1)
+
+ @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
+ @Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
+
+ @Rule
+ @JvmField
+ var activityRule =
+ ActivityTestRule(
+ object :
+ SingleActivityFactory<TestableControlsProviderSelectorActivity>(
+ TestableControlsProviderSelectorActivity::class.java
+ ) {
+ override fun create(intent: Intent?): TestableControlsProviderSelectorActivity {
+ return TestableControlsProviderSelectorActivity(
+ executor,
+ backExecutor,
+ listingController,
+ controlsController,
+ broadcastDispatcher,
+ uiController,
+ mockDispatcher,
+ latch
+ )
+ }
+ },
+ false,
+ false
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ val intent = Intent()
+ intent.putExtra(ControlsProviderSelectorActivity.BACK_SHOULD_EXIT, true)
+ activityRule.launchActivity(intent)
+ }
+
+ @Test
+ fun testBackCallbackRegistrationAndUnregistration() {
+ // 1. ensure that launching the activity results in it registering a callback
+ verify(mockDispatcher)
+ .registerOnBackInvokedCallback(
+ ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ captureCallback.capture()
+ )
+ activityRule.finishActivity()
+ latch.await() // ensure activity is finished
+ // 2. ensure that when the activity is finished, it unregisters the same callback
+ verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
+ }
+
+ public class TestableControlsProviderSelectorActivity(
+ executor: Executor,
+ backExecutor: Executor,
+ listingController: ControlsListingController,
+ controlsController: ControlsController,
+ broadcastDispatcher: BroadcastDispatcher,
+ uiController: ControlsUiController,
+ private val mockDispatcher: OnBackInvokedDispatcher,
+ private val latch: CountDownLatch
+ ) :
+ ControlsProviderSelectorActivity(
+ executor,
+ backExecutor,
+ listingController,
+ controlsController,
+ broadcastDispatcher,
+ uiController
+ ) {
+ override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
+ return mockDispatcher
+ }
+
+ override fun onStop() {
+ super.onStop()
+ // ensures that test runner thread does not proceed until ui thread is done
+ latch.countDown()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index c234178f8af3..517804db2a70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -36,10 +36,10 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
-import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
+import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import org.junit.Before;
@@ -90,7 +90,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
ViewRootImpl mViewRoot;
@Mock
- BouncerCallbackInteractor mBouncerCallbackInteractor;
+ PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
@Mock
DreamOverlayAnimationsController mAnimationsController;
@@ -106,7 +106,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
when(mDreamOverlayContainerView.getResources()).thenReturn(mResources);
when(mDreamOverlayContainerView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
- when(mStatusBarKeyguardViewManager.getBouncer()).thenReturn(mBouncer);
+ when(mStatusBarKeyguardViewManager.getPrimaryBouncer()).thenReturn(mBouncer);
when(mDreamOverlayContainerView.getViewRootImpl()).thenReturn(mViewRoot);
mController = new DreamOverlayContainerViewController(
@@ -121,7 +121,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
MAX_BURN_IN_OFFSET,
BURN_IN_PROTECTION_UPDATE_INTERVAL,
MILLIS_UNTIL_FULL_JITTER,
- mBouncerCallbackInteractor,
+ mPrimaryBouncerCallbackInteractor,
mAnimationsController,
mStateController);
}
@@ -167,8 +167,8 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
@Test
public void testBouncerAnimation_doesNotApply() {
- final ArgumentCaptor<BouncerExpansionCallback> bouncerExpansionCaptor =
- ArgumentCaptor.forClass(BouncerExpansionCallback.class);
+ final ArgumentCaptor<PrimaryBouncerExpansionCallback> bouncerExpansionCaptor =
+ ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
mController.onViewAttached();
verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());
@@ -178,8 +178,8 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
@Test
public void testBouncerAnimation_updateBlur() {
- final ArgumentCaptor<BouncerExpansionCallback> bouncerExpansionCaptor =
- ArgumentCaptor.forClass(BouncerExpansionCallback.class);
+ final ArgumentCaptor<PrimaryBouncerExpansionCallback> bouncerExpansionCaptor =
+ ArgumentCaptor.forClass(PrimaryBouncerExpansionCallback.class);
mController.onViewAttached();
verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index 849ac5ef90d7..7a2ba95f74a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -347,21 +347,22 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
addComplication(engine, thirdViewInfo);
- // The first added view should now be underneath the second view.
+ // The first added view should now be underneath the third view.
verifyChange(firstViewInfo, false, lp -> {
assertThat(lp.topToBottom == thirdViewInfo.view.getId()).isTrue();
assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
assertThat(lp.topMargin).isEqualTo(margin);
});
- // The second view should be in underneath the third view.
+ // The second view should be to the start of the third view.
verifyChange(secondViewInfo, false, lp -> {
assertThat(lp.endToStart == thirdViewInfo.view.getId()).isTrue();
assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
assertThat(lp.getMarginEnd()).isEqualTo(margin);
});
- // The third view should be in at the top.
+ // The third view should be at the top end corner. No margin should be applied if not
+ // specified.
verifyChange(thirdViewInfo, true, lp -> {
assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
@@ -425,14 +426,14 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
addComplication(engine, thirdViewInfo);
- // The first added view should now be underneath the second view.
+ // The first added view should now be underneath the third view.
verifyChange(firstViewInfo, false, lp -> {
assertThat(lp.topToBottom == thirdViewInfo.view.getId()).isTrue();
assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
assertThat(lp.topMargin).isEqualTo(complicationMargin);
});
- // The second view should be in underneath the third view.
+ // The second view should be to the start of the third view.
verifyChange(secondViewInfo, false, lp -> {
assertThat(lp.endToStart == thirdViewInfo.view.getId()).isTrue();
assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
@@ -441,6 +442,69 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
}
/**
+ * Ensures the root complication applies margin if specified.
+ */
+ @Test
+ public void testRootComplicationSpecifiedMargin() {
+ final int defaultMargin = 5;
+ final int complicationMargin = 10;
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, defaultMargin, mTouchSession, 0, 0);
+
+ final ViewInfo firstViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0),
+ Complication.CATEGORY_STANDARD,
+ mLayout);
+
+ addComplication(engine, firstViewInfo);
+
+ final ViewInfo secondViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_START,
+ 0),
+ Complication.CATEGORY_SYSTEM,
+ mLayout);
+
+ addComplication(engine, secondViewInfo);
+
+ firstViewInfo.clearInvocations();
+ secondViewInfo.clearInvocations();
+
+ final ViewInfo thirdViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_START,
+ 1,
+ complicationMargin),
+ Complication.CATEGORY_SYSTEM,
+ mLayout);
+
+ addComplication(engine, thirdViewInfo);
+
+ // The third view is the root view and has specified margin, which should be applied based
+ // on its direction.
+ verifyChange(thirdViewInfo, true, lp -> {
+ assertThat(lp.getMarginStart()).isEqualTo(0);
+ assertThat(lp.getMarginEnd()).isEqualTo(complicationMargin);
+ assertThat(lp.topMargin).isEqualTo(0);
+ assertThat(lp.bottomMargin).isEqualTo(0);
+ });
+ }
+
+ /**
* Ensures layout in a particular position updates.
*/
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
index cb7e47b28bcd..ce7561e95f1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
@@ -97,6 +97,31 @@ public class ComplicationLayoutParamsTest extends SysuiTestCase {
}
/**
+ * Ensures ComplicationLayoutParams correctly returns whether the complication specified margin.
+ */
+ @Test
+ public void testIsMarginSpecified() {
+ final ComplicationLayoutParams paramsNoMargin = new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0);
+ assertThat(paramsNoMargin.isMarginSpecified()).isFalse();
+
+ final ComplicationLayoutParams paramsWithMargin = new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0,
+ 20 /*margin*/);
+ assertThat(paramsWithMargin.isMarginSpecified()).isTrue();
+ }
+
+ /**
* Ensures unspecified margin uses default.
*/
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index aa8c93edce68..30ad485d7ac3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -90,7 +90,10 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
private ActivityStarter mActivityStarter;
@Mock
- UiEventLogger mUiEventLogger;
+ private UiEventLogger mUiEventLogger;
+
+ @Captor
+ private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor;
@Before
public void setup() {
@@ -164,6 +167,29 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
verify(mDreamOverlayStateController).addComplication(mComplication);
}
+ @Test
+ public void complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive() {
+ final DreamHomeControlsComplication.Registrant registrant =
+ new DreamHomeControlsComplication.Registrant(mComplication,
+ mDreamOverlayStateController, mControlsComponent);
+ registrant.start();
+
+ setServiceAvailable(true);
+ setHaveFavorites(false);
+
+ // Complication not available on start.
+ verify(mDreamOverlayStateController, never()).addComplication(mComplication);
+
+ // Favorite controls added, complication should be available now.
+ setHaveFavorites(true);
+
+ // Dream overlay becomes active.
+ setDreamOverlayActive(true);
+
+ // Verify complication is added.
+ verify(mDreamOverlayStateController).addComplication(mComplication);
+ }
+
/**
* Ensures clicking home controls chip logs UiEvent.
*/
@@ -196,10 +222,17 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
private void setServiceAvailable(boolean value) {
final List<ControlsServiceInfo> serviceInfos = mock(List.class);
+ when(mControlsListingController.getCurrentServices()).thenReturn(serviceInfos);
when(serviceInfos.isEmpty()).thenReturn(!value);
triggerControlsListingCallback(serviceInfos);
}
+ private void setDreamOverlayActive(boolean value) {
+ when(mDreamOverlayStateController.isOverlayActive()).thenReturn(value);
+ verify(mDreamOverlayStateController).addCallback(mStateCallbackCaptor.capture());
+ mStateCallbackCaptor.getValue().onStateChanged();
+ }
+
private void triggerControlsListingCallback(List<ControlsServiceInfo> serviceInfos) {
verify(mControlsListingController).addCallback(mCallbackCaptor.capture());
mCallbackCaptor.getValue().onServicesUpdated(serviceInfos);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index 318f2bc1c227..170a70f2fc40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -20,7 +20,6 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
-import java.lang.IllegalStateException
import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith
@@ -29,12 +28,12 @@ import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
class FakeFeatureFlagsTest : SysuiTestCase() {
- private val unreleasedFlag = UnreleasedFlag(-1000)
- private val releasedFlag = ReleasedFlag(-1001)
- private val stringFlag = StringFlag(-1002)
- private val resourceBooleanFlag = ResourceBooleanFlag(-1003, resourceId = -1)
- private val resourceStringFlag = ResourceStringFlag(-1004, resourceId = -1)
- private val sysPropBooleanFlag = SysPropBooleanFlag(-1005, name = "test")
+ private val unreleasedFlag = UnreleasedFlag(-1000, "-1000", "test")
+ private val releasedFlag = ReleasedFlag(-1001, "-1001", "test")
+ private val stringFlag = StringFlag(-1002, "-1002", "test")
+ private val resourceBooleanFlag = ResourceBooleanFlag(-1003, "-1003", "test", resourceId = -1)
+ private val resourceStringFlag = ResourceStringFlag(-1004, "-1004", "test", resourceId = -1)
+ private val sysPropBooleanFlag = SysPropBooleanFlag(-1005, "test", "test")
/**
* FakeFeatureFlags does not honor any default values. All flags which are accessed must be
@@ -47,7 +46,7 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
assertThat(flags.isEnabled(Flags.TEAMFOOD)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("TEAMFOOD")
+ assertThat(ex.message).contains("id=1")
}
try {
assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index 9c22cd2a077d..7592cc527190 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -31,10 +31,6 @@ import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
-import java.io.PrintWriter
-import java.io.Serializable
-import java.io.StringWriter
-import java.util.function.Consumer
import org.junit.Assert
import org.junit.Before
import org.junit.Test
@@ -46,8 +42,12 @@ import org.mockito.Mockito.inOrder
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
+import java.io.Serializable
+import java.io.StringWriter
+import java.util.function.Consumer
+import org.mockito.Mockito.`when` as whenever
/**
* NOTE: This test is for the version of FeatureFlagManager in src-debug, which allows overriding
@@ -57,21 +57,32 @@ import org.mockito.MockitoAnnotations
class FeatureFlagsDebugTest : SysuiTestCase() {
private lateinit var mFeatureFlagsDebug: FeatureFlagsDebug
- @Mock private lateinit var flagManager: FlagManager
- @Mock private lateinit var mockContext: Context
- @Mock private lateinit var secureSettings: SecureSettings
- @Mock private lateinit var systemProperties: SystemPropertiesHelper
- @Mock private lateinit var resources: Resources
- @Mock private lateinit var commandRegistry: CommandRegistry
- @Mock private lateinit var restarter: Restarter
+ @Mock
+ private lateinit var flagManager: FlagManager
+ @Mock
+ private lateinit var mockContext: Context
+ @Mock
+ private lateinit var secureSettings: SecureSettings
+ @Mock
+ private lateinit var systemProperties: SystemPropertiesHelper
+ @Mock
+ private lateinit var resources: Resources
+ @Mock
+ private lateinit var commandRegistry: CommandRegistry
+ @Mock
+ private lateinit var restarter: Restarter
private val flagMap = mutableMapOf<Int, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<Int>
private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
- private val teamfoodableFlagA = UnreleasedFlag(500, true)
- private val teamfoodableFlagB = ReleasedFlag(501, true)
+ private val teamfoodableFlagA = UnreleasedFlag(
+ 500, name = "a", namespace = "test", teamfood = true
+ )
+ private val teamfoodableFlagB = ReleasedFlag(
+ 501, name = "b", namespace = "test", teamfood = true
+ )
@Before
fun setup() {
@@ -84,7 +95,6 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
secureSettings,
systemProperties,
resources,
- deviceConfig,
serverFlagReader,
flagMap,
restarter
@@ -92,8 +102,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
mFeatureFlagsDebug.init()
verify(flagManager).onSettingsChangedAction = any()
broadcastReceiver = withArgCaptor {
- verify(mockContext).registerReceiver(capture(), any(), nullable(), nullable(),
- any())
+ verify(mockContext).registerReceiver(
+ capture(), any(), nullable(), nullable(),
+ any()
+ )
}
clearCacheAction = withArgCaptor {
verify(flagManager).clearCacheAction = capture()
@@ -107,10 +119,42 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
- assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(2))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(3))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(4))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(5))).isFalse()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ ReleasedFlag(
+ 2,
+ name = "2",
+ namespace = "test"
+ )
+ )
+ ).isTrue()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ UnreleasedFlag(
+ 3,
+ name = "3",
+ namespace = "test"
+ )
+ )
+ ).isTrue()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ ReleasedFlag(
+ 4,
+ name = "3",
+ namespace = "test"
+ )
+ )
+ ).isFalse()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ UnreleasedFlag(
+ 5,
+ name = "4",
+ namespace = "test"
+ )
+ )
+ ).isFalse()
}
@Test
@@ -138,9 +182,9 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun teamFoodFlag_Overridden() {
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
- .thenReturn(true)
+ .thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
- .thenReturn(false)
+ .thenReturn(false)
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isFalse()
@@ -161,17 +205,26 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(5), any())).thenReturn(false)
- assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(1, 1001))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, 1002))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(3, 1003))).isTrue()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ ResourceBooleanFlag(
+ 1,
+ "1",
+ "test",
+ 1001
+ )
+ )
+ ).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(2, "2", "test", 1002))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(3, "3", "test", 1003))).isTrue()
Assert.assertThrows(NameNotFoundException::class.java) {
- mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(4, 1004))
+ mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(4, "4", "test", 1004))
}
// Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NameNotFoundException::class.java) {
- mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(5, 1005))
+ mFeatureFlagsDebug.isEnabled(ResourceBooleanFlag(5, "5", "test", 1005))
}
}
@@ -184,36 +237,30 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
return@thenAnswer it.getArgument(1)
}
- assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(1, "a"))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(2, "b"))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(3, "c", true))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(4, "d", false))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(5, "e"))).isFalse()
- }
-
- @Test
- fun readDeviceConfigBooleanFlag() {
- val namespace = "test_namespace"
- deviceConfig.setProperty(namespace, "a", "true", false)
- deviceConfig.setProperty(namespace, "b", "false", false)
- deviceConfig.setProperty(namespace, "c", null, false)
-
- assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(1, "a", namespace)))
- .isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(2, "b", namespace)))
- .isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(3, "c", namespace)))
- .isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(1, "a", "test"))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(2, "b", "test"))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(3, "c", "test", true))).isTrue()
+ assertThat(
+ mFeatureFlagsDebug.isEnabled(
+ SysPropBooleanFlag(
+ 4,
+ "d",
+ "test",
+ false
+ )
+ )
+ ).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(5, "e", "test"))).isFalse()
}
@Test
fun readStringFlag() {
whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
- assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz")
- assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "baz"))).isEqualTo("baz")
- assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "buz"))).isEqualTo("foo")
- assertThat(mFeatureFlagsDebug.getString(StringFlag(4, "buz"))).isEqualTo("bar")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "1", "test", "biz"))).isEqualTo("biz")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(2, "2", "test", "baz"))).isEqualTo("baz")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(3, "3", "test", "buz"))).isEqualTo("foo")
+ assertThat(mFeatureFlagsDebug.getString(StringFlag(4, "4", "test", "buz"))).isEqualTo("bar")
}
@Test
@@ -229,20 +276,47 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("override4")
whenever(flagManager.readFlagValue<String>(eq(6), any())).thenReturn("override6")
- assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(1, 1001))).isEqualTo("")
- assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(2, 1002))).isEqualTo("resource2")
- assertThat(mFeatureFlagsDebug.getString(ResourceStringFlag(3, 1003))).isEqualTo("override3")
+ assertThat(
+ mFeatureFlagsDebug.getString(
+ ResourceStringFlag(
+ 1,
+ "1",
+ "test",
+ 1001
+ )
+ )
+ ).isEqualTo("")
+ assertThat(
+ mFeatureFlagsDebug.getString(
+ ResourceStringFlag(
+ 2,
+ "2",
+ "test",
+ 1002
+ )
+ )
+ ).isEqualTo("resource2")
+ assertThat(
+ mFeatureFlagsDebug.getString(
+ ResourceStringFlag(
+ 3,
+ "3",
+ "test",
+ 1003
+ )
+ )
+ ).isEqualTo("override3")
Assert.assertThrows(NullPointerException::class.java) {
- mFeatureFlagsDebug.getString(ResourceStringFlag(4, 1004))
+ mFeatureFlagsDebug.getString(ResourceStringFlag(4, "4", "test", 1004))
}
Assert.assertThrows(NameNotFoundException::class.java) {
- mFeatureFlagsDebug.getString(ResourceStringFlag(5, 1005))
+ mFeatureFlagsDebug.getString(ResourceStringFlag(5, "5", "test", 1005))
}
// Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NameNotFoundException::class.java) {
- mFeatureFlagsDebug.getString(ResourceStringFlag(6, 1005))
+ mFeatureFlagsDebug.getString(ResourceStringFlag(6, "6", "test", 1005))
}
}
@@ -250,10 +324,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
fun readIntFlag() {
whenever(flagManager.readFlagValue<Int>(eq(3), any())).thenReturn(22)
whenever(flagManager.readFlagValue<Int>(eq(4), any())).thenReturn(48)
- assertThat(mFeatureFlagsDebug.getInt(IntFlag(1, 12))).isEqualTo(12)
- assertThat(mFeatureFlagsDebug.getInt(IntFlag(2, 93))).isEqualTo(93)
- assertThat(mFeatureFlagsDebug.getInt(IntFlag(3, 8))).isEqualTo(22)
- assertThat(mFeatureFlagsDebug.getInt(IntFlag(4, 234))).isEqualTo(48)
+ assertThat(mFeatureFlagsDebug.getInt(IntFlag(1, "1", "test", 12))).isEqualTo(12)
+ assertThat(mFeatureFlagsDebug.getInt(IntFlag(2, "2", "test", 93))).isEqualTo(93)
+ assertThat(mFeatureFlagsDebug.getInt(IntFlag(3, "3", "test", 8))).isEqualTo(22)
+ assertThat(mFeatureFlagsDebug.getInt(IntFlag(4, "4", "test", 234))).isEqualTo(48)
}
@Test
@@ -269,26 +343,26 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
whenever(flagManager.readFlagValue<Int>(eq(4), any())).thenReturn(500)
whenever(flagManager.readFlagValue<Int>(eq(5), any())).thenReturn(9519)
- assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(1, 1001))).isEqualTo(88)
- assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(2, 1002))).isEqualTo(61)
- assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(3, 1003))).isEqualTo(20)
+ assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(1, "1", "test", 1001))).isEqualTo(88)
+ assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(2, "2", "test", 1002))).isEqualTo(61)
+ assertThat(mFeatureFlagsDebug.getInt(ResourceIntFlag(3, "3", "test", 1003))).isEqualTo(20)
Assert.assertThrows(NotFoundException::class.java) {
- mFeatureFlagsDebug.getInt(ResourceIntFlag(4, 1004))
+ mFeatureFlagsDebug.getInt(ResourceIntFlag(4, "4", "test", 1004))
}
// Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NotFoundException::class.java) {
- mFeatureFlagsDebug.getInt(ResourceIntFlag(5, 1005))
+ mFeatureFlagsDebug.getInt(ResourceIntFlag(5, "5", "test", 1005))
}
}
@Test
fun broadcastReceiver_IgnoresInvalidData() {
- addFlag(UnreleasedFlag(1))
- addFlag(ResourceBooleanFlag(2, 1002))
- addFlag(StringFlag(3, "flag3"))
- addFlag(ResourceStringFlag(4, 1004))
+ addFlag(UnreleasedFlag(1, "1", "test"))
+ addFlag(ResourceBooleanFlag(2, "2", "test", 1002))
+ addFlag(StringFlag(3, "3", "test", "flag3"))
+ addFlag(ResourceStringFlag(4, "4", "test", 1004))
broadcastReceiver.onReceive(mockContext, null)
broadcastReceiver.onReceive(mockContext, Intent())
@@ -304,7 +378,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun intentWithId_NoValueKeyClears() {
- addFlag(UnreleasedFlag(1))
+ addFlag(UnreleasedFlag(1, name = "1", namespace = "test"))
// trying to erase an id not in the map does nothing
broadcastReceiver.onReceive(
@@ -323,10 +397,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun setBooleanFlag() {
- addFlag(UnreleasedFlag(1))
- addFlag(UnreleasedFlag(2))
- addFlag(ResourceBooleanFlag(3, 1003))
- addFlag(ResourceBooleanFlag(4, 1004))
+ addFlag(UnreleasedFlag(1, "1", "test"))
+ addFlag(UnreleasedFlag(2, "2", "test"))
+ addFlag(ResourceBooleanFlag(3, "3", "test", 1003))
+ addFlag(ResourceBooleanFlag(4, "4", "test", 1004))
setByBroadcast(1, false)
verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}")
@@ -343,8 +417,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun setStringFlag() {
- addFlag(StringFlag(1, "flag1"))
- addFlag(ResourceStringFlag(2, 1002))
+ addFlag(StringFlag(1, "flag1", "1", "test"))
+ addFlag(ResourceStringFlag(2, "2", "test", 1002))
setByBroadcast(1, "override1")
verifyPutData(1, "{\"type\":\"string\",\"value\":\"override1\"}")
@@ -355,7 +429,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun setFlag_ClearsCache() {
- val flag1 = addFlag(StringFlag(1, "flag1"))
+ val flag1 = addFlag(StringFlag(1, "1", "test", "flag1"))
whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
// gets the flag & cache it
@@ -377,31 +451,31 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
@Test
fun serverSide_Overrides_MakesFalse() {
- val flag = ReleasedFlag(100)
+ val flag = ReleasedFlag(100, "100", "test")
- serverFlagReader.setFlagValue(flag.id, false)
+ serverFlagReader.setFlagValue(flag.namespace, flag.name, false)
assertThat(mFeatureFlagsDebug.isEnabled(flag)).isFalse()
}
@Test
fun serverSide_Overrides_MakesTrue() {
- val flag = UnreleasedFlag(100)
+ val flag = UnreleasedFlag(100, name = "100", namespace = "test")
- serverFlagReader.setFlagValue(flag.id, true)
+ serverFlagReader.setFlagValue(flag.namespace, flag.name, true)
assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue()
}
@Test
fun dumpFormat() {
- val flag1 = ReleasedFlag(1)
- val flag2 = ResourceBooleanFlag(2, 1002)
- val flag3 = UnreleasedFlag(3)
- val flag4 = StringFlag(4, "")
- val flag5 = StringFlag(5, "flag5default")
- val flag6 = ResourceStringFlag(6, 1006)
- val flag7 = ResourceStringFlag(7, 1007)
+ val flag1 = ReleasedFlag(1, "1", "test")
+ val flag2 = ResourceBooleanFlag(2, "2", "test", 1002)
+ val flag3 = UnreleasedFlag(3, "3", "test")
+ val flag4 = StringFlag(4, "4", "test", "")
+ val flag5 = StringFlag(5, "5", "test", "flag5default")
+ val flag6 = ResourceStringFlag(6, "6", "test", 1006)
+ val flag7 = ResourceStringFlag(7, "7", "test", 1007)
whenever(resources.getBoolean(1002)).thenReturn(true)
whenever(resources.getString(1006)).thenReturn("resource1006")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
index b2dd60c9566d..d5b5a4a4101e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -25,8 +25,8 @@ import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -59,7 +59,9 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
fun testBooleanResourceFlag() {
val flagId = 213
val flagResourceId = 3
- val flag = ResourceBooleanFlag(flagId, flagResourceId)
+ val flagName = "213"
+ val flagNamespace = "test"
+ val flag = ResourceBooleanFlag(flagId, flagName, flagNamespace, flagResourceId)
whenever(mResources.getBoolean(flagResourceId)).thenReturn(true)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isTrue()
}
@@ -71,57 +73,45 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
whenever(mResources.getString(1003)).thenReturn(null)
whenever(mResources.getString(1004)).thenAnswer { throw NameNotFoundException() }
- assertThat(mFeatureFlagsRelease.getString(ResourceStringFlag(1, 1001))).isEqualTo("")
- assertThat(mFeatureFlagsRelease.getString(ResourceStringFlag(2, 1002))).isEqualTo("res2")
+ assertThat(mFeatureFlagsRelease.getString(
+ ResourceStringFlag(1, "1", "test", 1001))).isEqualTo("")
+ assertThat(mFeatureFlagsRelease.getString(
+ ResourceStringFlag(2, "2", "test", 1002))).isEqualTo("res2")
assertThrows(NullPointerException::class.java) {
- mFeatureFlagsRelease.getString(ResourceStringFlag(3, 1003))
+ mFeatureFlagsRelease.getString(ResourceStringFlag(3, "3", "test", 1003))
}
assertThrows(NameNotFoundException::class.java) {
- mFeatureFlagsRelease.getString(ResourceStringFlag(4, 1004))
+ mFeatureFlagsRelease.getString(ResourceStringFlag(4, "4", "test", 1004))
}
}
@Test
- fun testReadDeviceConfigBooleanFlag() {
- val namespace = "test_namespace"
- deviceConfig.setProperty(namespace, "a", "true", false)
- deviceConfig.setProperty(namespace, "b", "false", false)
- deviceConfig.setProperty(namespace, "c", null, false)
-
- assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(1, "a", namespace)))
- .isTrue()
- assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(2, "b", namespace)))
- .isFalse()
- assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(3, "c", namespace)))
- .isFalse()
- }
-
- @Test
fun testSysPropBooleanFlag() {
val flagId = 213
val flagName = "sys_prop_flag"
+ val flagNamespace = "test"
val flagDefault = true
- val flag = SysPropBooleanFlag(flagId, flagName, flagDefault)
+ val flag = SysPropBooleanFlag(flagId, flagName, flagNamespace, flagDefault)
whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault)
}
@Test
fun serverSide_OverridesReleased_MakesFalse() {
- val flag = ReleasedFlag(100)
+ val flag = ReleasedFlag(100, "100", "test")
- serverFlagReader.setFlagValue(flag.id, false)
+ serverFlagReader.setFlagValue(flag.namespace, flag.name, false)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
}
@Test
fun serverSide_OverridesUnreleased_Ignored() {
- val flag = UnreleasedFlag(100)
+ val flag = UnreleasedFlag(100, "100", "test")
- serverFlagReader.setFlagValue(flag.id, true)
+ serverFlagReader.setFlagValue(flag.namespace, flag.name, true)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
index 7355319a2fbc..fea91c53424d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
@@ -33,10 +33,10 @@ class FlagCommandTest : SysuiTestCase() {
@Mock private lateinit var featureFlags: FeatureFlagsDebug
@Mock private lateinit var pw: PrintWriter
private val flagMap = mutableMapOf<Int, Flag<*>>()
- private val flagA = UnreleasedFlag(500)
- private val flagB = ReleasedFlag(501)
- private val stringFlag = StringFlag(502, "abracadabra")
- private val intFlag = IntFlag(503, 12)
+ private val flagA = UnreleasedFlag(500, "500", "test")
+ private val flagB = ReleasedFlag(501, "501", "test")
+ private val stringFlag = StringFlag(502, "502", "test", "abracadabra")
+ private val intFlag = IntFlag(503, "503", "test", 12)
private lateinit var cmd: FlagCommand
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
index 17324a01502b..fca7e96fb148 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -64,14 +64,14 @@ class FlagManagerTest : SysuiTestCase() {
verifyNoMoreInteractions(mFlagSettingsHelper)
// adding the first listener registers the observer
- mFlagManager.addListener(ReleasedFlag(1), listener1)
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
verifyNoMoreInteractions(mFlagSettingsHelper)
// adding another listener does nothing
- mFlagManager.addListener(ReleasedFlag(2), listener2)
+ mFlagManager.addListener(ReleasedFlag(2, "2", "test"), listener2)
verifyNoMoreInteractions(mFlagSettingsHelper)
// removing the original listener does nothing with second one still present
@@ -89,7 +89,7 @@ class FlagManagerTest : SysuiTestCase() {
val listener = mock<FlagListenable.Listener>()
val clearCacheAction = mock<Consumer<Int>>()
mFlagManager.clearCacheAction = clearCacheAction
- mFlagManager.addListener(ReleasedFlag(1), listener)
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
@@ -101,8 +101,8 @@ class FlagManagerTest : SysuiTestCase() {
fun testObserverInvokesListeners() {
val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>()
- mFlagManager.addListener(ReleasedFlag(1), listener1)
- mFlagManager.addListener(ReleasedFlag(10), listener10)
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener10)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
@@ -127,8 +127,8 @@ class FlagManagerTest : SysuiTestCase() {
fun testOnlySpecificFlagListenerIsInvoked() {
val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>()
- mFlagManager.addListener(ReleasedFlag(1), listener1)
- mFlagManager.addListener(ReleasedFlag(10), listener10)
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener1)
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener10)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -148,8 +148,8 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testSameListenerCanBeUsedForMultipleFlags() {
val listener = mock<FlagListenable.Listener>()
- mFlagManager.addListener(ReleasedFlag(1), listener)
- mFlagManager.addListener(ReleasedFlag(10), listener)
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test"), listener)
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test"), listener)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -177,7 +177,7 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testListenerCanSuppressRestart() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(ReleasedFlag(1)) { event ->
+ mFlagManager.addListener(ReleasedFlag(1, "1", "test")) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -188,7 +188,7 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testListenerOnlySuppressesRestartForOwnFlag() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(ReleasedFlag(10)) { event ->
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test")) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -199,10 +199,10 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testRestartWhenNotAllListenersRequestSuppress() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(ReleasedFlag(10)) { event ->
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test")) { event ->
event.requestNoRestart()
}
- mFlagManager.addListener(ReleasedFlag(10)) {
+ mFlagManager.addListener(ReleasedFlag(10, "10", "test")) {
// do not request
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt
deleted file mode 100644
index 2b556f112e5f..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.flags
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.google.common.truth.Truth
-import java.lang.StringBuilder
-import java.util.ArrayList
-import java.util.HashMap
-import org.junit.Test
-
-@SmallTest
-class FlagsTest : SysuiTestCase() {
- @Test
- fun testDuplicateFlagIdCheckWorks() {
- val flags = Flags.collectFlagsInClass(DuplicateFlagContainer)
- val duplicates = groupDuplicateFlags(flags)
- Truth.assertWithMessage(generateAssertionMessage(duplicates))
- .that(duplicates.size)
- .isEqualTo(2)
- }
-
- @Test
- fun testNoDuplicateFlagIds() {
- val flags = Flags.collectFlagsInClass(Flags)
- val duplicates = groupDuplicateFlags(flags)
- Truth.assertWithMessage(generateAssertionMessage(duplicates))
- .that(duplicates.size)
- .isEqualTo(0)
- }
-
- private fun generateAssertionMessage(duplicates: Map<Int, List<String>>): String {
- val stringBuilder = StringBuilder()
- stringBuilder.append("Duplicate flag keys found: {")
- for (id in duplicates.keys) {
- stringBuilder
- .append(" ")
- .append(id)
- .append(": [")
- .append(java.lang.String.join(", ", duplicates[id]))
- .append("]")
- }
- stringBuilder.append(" }")
- return stringBuilder.toString()
- }
-
- private fun groupDuplicateFlags(flags: Map<String, Flag<*>>): Map<Int, List<String>> {
- val grouping: MutableMap<Int, MutableList<String>> = HashMap()
- for (flag in flags) {
- grouping.putIfAbsent(flag.value.id, ArrayList())
- grouping[flag.value.id]!!.add(flag.key)
- }
- val result: MutableMap<Int, List<String>> = HashMap()
- for (id in grouping.keys) {
- if (grouping[id]!!.size > 1) {
- result[id] = grouping[id]!!
- }
- }
- return result
- }
-
- private object DuplicateFlagContainer {
- val A_FLAG: BooleanFlag = UnreleasedFlag(0)
- val B_FLAG: BooleanFlag = UnreleasedFlag(0)
- val C_FLAG = StringFlag(0)
- val D_FLAG: BooleanFlag = UnreleasedFlag(1)
- val E_FLAG = DoubleFlag(3)
- val F_FLAG = DoubleFlag(3)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
index 6f5f460d41c4..1633912abec7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
@@ -50,7 +50,7 @@ class ServerFlagReaderImplTest : SysuiTestCase() {
@Test
fun testChange_alertsListener() {
- val flag = ReleasedFlag(1)
+ val flag = ReleasedFlag(1, "1", "test")
serverFlagReader.listenForChanges(listOf(flag), changeListener)
deviceConfig.setProperty(NAMESPACE, "flag_override_1", "1", false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 53d9b87b2346..6ba06344314c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.keyguard.data.repository
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost
@@ -48,6 +50,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
private lateinit var underTest: KeyguardRepositoryImpl
@@ -59,10 +62,11 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
underTest =
KeyguardRepositoryImpl(
statusBarStateController,
- keyguardStateController,
dozeHost,
wakefulnessLifecycle,
biometricUnlockController,
+ keyguardStateController,
+ keyguardUpdateMonitor,
)
}
@@ -223,6 +227,15 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun isUdfpsSupported() = runBlockingTest {
+ whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
+ assertThat(underTest.isUdfpsSupported()).isTrue()
+
+ whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(false)
+ assertThat(underTest.isUdfpsSupported()).isFalse()
+ }
+
+ @Test
fun isBouncerShowing() = runBlockingTest {
whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
var latest: Boolean? = null
@@ -245,6 +258,48 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun isKeyguardGoingAway() = runBlockingTest {
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isFalse()
+
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
+
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
+ captor.value.onKeyguardGoingAwayChanged()
+ assertThat(latest).isTrue()
+
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
+ captor.value.onKeyguardGoingAwayChanged()
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun isDreaming() = runBlockingTest {
+ whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isFalse()
+
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+
+ captor.value.onDreamingStateChanged(true)
+ assertThat(latest).isTrue()
+
+ captor.value.onDreamingStateChanged(false)
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun biometricUnlockState() = runBlockingTest {
val values = mutableListOf<BiometricUnlockModel>()
val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 27d5d0a98978..2b03722f9f31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -25,8 +25,8 @@ import android.view.Choreographer.FrameCallback
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators
+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.BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -38,7 +38,6 @@ import java.util.UUID
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn
@@ -91,18 +90,51 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
}
}
- assertSteps(steps, listWithStep(BigDecimal(.1)))
+ assertSteps(steps, listWithStep(BigDecimal(.1)), AOD, LOCKSCREEN)
job.cancel()
provider.stop()
}
@Test
- fun `startTransition called during another transition fails`() {
- underTest.startTransition(TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, null))
- underTest.startTransition(TransitionInfo(OWNER_NAME, LOCKSCREEN, BOUNCER, null))
+ fun `starting second transition will cancel the first transition`() {
+ runBlocking(IMMEDIATE) {
+ val (animator, provider) = setupAnimator(this)
- assertThat(wtfHandler.failed).isTrue()
+ val steps = mutableListOf<TransitionStep>()
+ val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
+
+ underTest.startTransition(TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator))
+ // 3 yields(), alternating with the animator, results in a value 0.1, which can be
+ // canceled and tested against
+ yield()
+ yield()
+ yield()
+
+ // Now start 2nd transition, which will interrupt the first
+ val job2 = underTest.transition(LOCKSCREEN, AOD).onEach { steps.add(it) }.launchIn(this)
+ val (animator2, provider2) = setupAnimator(this)
+ underTest.startTransition(TransitionInfo(OWNER_NAME, LOCKSCREEN, AOD, animator2))
+
+ val startTime = System.currentTimeMillis()
+ while (animator2.isRunning()) {
+ yield()
+ if (System.currentTimeMillis() - startTime > MAX_TEST_DURATION) {
+ fail("Failed test due to excessive runtime of: $MAX_TEST_DURATION")
+ }
+ }
+
+ val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1))
+ assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN)
+
+ val secondTransitionSteps = listWithStep(step = BigDecimal(.1), start = BigDecimal(.9))
+ assertSteps(steps.subList(4, steps.size), secondTransitionSteps, LOCKSCREEN, AOD)
+
+ job.cancel()
+ job2.cancel()
+ provider.stop()
+ provider2.stop()
+ }
}
@Test
@@ -165,11 +197,15 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
assertThat(wtfHandler.failed).isTrue()
}
- private fun listWithStep(step: BigDecimal): List<BigDecimal> {
+ private fun listWithStep(
+ step: BigDecimal,
+ start: BigDecimal = BigDecimal.ZERO,
+ stop: BigDecimal = BigDecimal.ONE,
+ ): List<BigDecimal> {
val steps = mutableListOf<BigDecimal>()
- var i = BigDecimal.ZERO
- while (i.compareTo(BigDecimal.ONE) <= 0) {
+ var i = start
+ while (i.compareTo(stop) <= 0) {
steps.add(i)
i = (i + step).setScale(2, RoundingMode.HALF_UP)
}
@@ -177,23 +213,43 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
return steps
}
- private fun assertSteps(steps: List<TransitionStep>, fractions: List<BigDecimal>) {
+ private fun assertSteps(
+ steps: List<TransitionStep>,
+ fractions: List<BigDecimal>,
+ from: KeyguardState,
+ to: KeyguardState,
+ ) {
assertThat(steps[0])
- .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME))
+ .isEqualTo(
+ TransitionStep(
+ from,
+ to,
+ fractions[0].toFloat(),
+ TransitionState.STARTED,
+ OWNER_NAME
+ )
+ )
fractions.forEachIndexed { index, fraction ->
assertThat(steps[index + 1])
.isEqualTo(
TransitionStep(
- AOD,
- LOCKSCREEN,
+ from,
+ to,
fraction.toFloat(),
TransitionState.RUNNING,
OWNER_NAME
)
)
}
+ val lastValue = fractions[fractions.size - 1].toFloat()
+ val status =
+ if (lastValue < 1f) {
+ TransitionState.CANCELED
+ } else {
+ TransitionState.FINISHED
+ }
assertThat(steps[steps.size - 1])
- .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED, OWNER_NAME))
+ .isEqualTo(TransitionStep(from, to, lastValue, status, OWNER_NAME))
assertThat(wtfHandler.failed).isFalse()
}
@@ -230,7 +286,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
scope.launch {
frames.collect {
// Delay is required for AnimationHandler to properly register a callback
- delay(1)
+ yield()
val (frameNumber, callback) = it
callback?.doFrame(frameNumber)
}
@@ -243,7 +299,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
}
override fun postFrameCallback(cb: FrameCallback) {
- frames.value = Pair(++frameCount, cb)
+ frames.value = Pair(frameCount++, cb)
}
override fun postCommitCallback(runnable: Runnable) {}
override fun getFrameTime() = frameCount
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
index 3a61c57d086f..db9c4e713e37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
@@ -30,57 +30,61 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(JUnit4::class)
-class BouncerCallbackInteractorTest : SysuiTestCase() {
- private val bouncerCallbackInteractor = BouncerCallbackInteractor()
- @Mock private lateinit var bouncerExpansionCallback: KeyguardBouncer.BouncerExpansionCallback
+class PrimaryBouncerCallbackInteractorTest : SysuiTestCase() {
+ private val mPrimaryBouncerCallbackInteractor = PrimaryBouncerCallbackInteractor()
+ @Mock
+ private lateinit var mPrimaryBouncerExpansionCallback:
+ KeyguardBouncer.PrimaryBouncerExpansionCallback
@Mock private lateinit var keyguardResetCallback: KeyguardBouncer.KeyguardResetCallback
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- bouncerCallbackInteractor.addBouncerExpansionCallback(bouncerExpansionCallback)
- bouncerCallbackInteractor.addKeyguardResetCallback(keyguardResetCallback)
+ mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(
+ mPrimaryBouncerExpansionCallback
+ )
+ mPrimaryBouncerCallbackInteractor.addKeyguardResetCallback(keyguardResetCallback)
}
@Test
fun testOnFullyShown() {
- bouncerCallbackInteractor.dispatchFullyShown()
- verify(bouncerExpansionCallback).onFullyShown()
+ mPrimaryBouncerCallbackInteractor.dispatchFullyShown()
+ verify(mPrimaryBouncerExpansionCallback).onFullyShown()
}
@Test
fun testOnFullyHidden() {
- bouncerCallbackInteractor.dispatchFullyHidden()
- verify(bouncerExpansionCallback).onFullyHidden()
+ mPrimaryBouncerCallbackInteractor.dispatchFullyHidden()
+ verify(mPrimaryBouncerExpansionCallback).onFullyHidden()
}
@Test
fun testOnExpansionChanged() {
- bouncerCallbackInteractor.dispatchExpansionChanged(5f)
- verify(bouncerExpansionCallback).onExpansionChanged(5f)
+ mPrimaryBouncerCallbackInteractor.dispatchExpansionChanged(5f)
+ verify(mPrimaryBouncerExpansionCallback).onExpansionChanged(5f)
}
@Test
fun testOnVisibilityChanged() {
- bouncerCallbackInteractor.dispatchVisibilityChanged(View.INVISIBLE)
- verify(bouncerExpansionCallback).onVisibilityChanged(false)
+ mPrimaryBouncerCallbackInteractor.dispatchVisibilityChanged(View.INVISIBLE)
+ verify(mPrimaryBouncerExpansionCallback).onVisibilityChanged(false)
}
@Test
fun testOnStartingToHide() {
- bouncerCallbackInteractor.dispatchStartingToHide()
- verify(bouncerExpansionCallback).onStartingToHide()
+ mPrimaryBouncerCallbackInteractor.dispatchStartingToHide()
+ verify(mPrimaryBouncerExpansionCallback).onStartingToHide()
}
@Test
fun testOnStartingToShow() {
- bouncerCallbackInteractor.dispatchStartingToShow()
- verify(bouncerExpansionCallback).onStartingToShow()
+ mPrimaryBouncerCallbackInteractor.dispatchStartingToShow()
+ verify(mPrimaryBouncerExpansionCallback).onStartingToShow()
}
@Test
fun testOnKeyguardReset() {
- bouncerCallbackInteractor.dispatchReset()
+ mPrimaryBouncerCallbackInteractor.dispatchReset()
verify(keyguardResetCallback).onKeyguardReset()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
index 5743b2f03d3a..3269f5a913ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -53,59 +53,58 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
-class BouncerInteractorTest : SysuiTestCase() {
+class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var repository: KeyguardBouncerRepository
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var bouncerView: BouncerView
@Mock private lateinit var bouncerViewDelegate: BouncerViewDelegate
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
- @Mock private lateinit var bouncerCallbackInteractor: BouncerCallbackInteractor
+ @Mock private lateinit var mPrimaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
@Mock private lateinit var keyguardBypassController: KeyguardBypassController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private val mainHandler = FakeHandler(Looper.getMainLooper())
- private lateinit var bouncerInteractor: BouncerInteractor
+ private lateinit var mPrimaryBouncerInteractor: PrimaryBouncerInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
DejankUtils.setImmediate(true)
- bouncerInteractor =
- BouncerInteractor(
+ mPrimaryBouncerInteractor =
+ PrimaryBouncerInteractor(
repository,
bouncerView,
mainHandler,
keyguardStateController,
keyguardSecurityModel,
- bouncerCallbackInteractor,
+ mPrimaryBouncerCallbackInteractor,
falsingCollector,
dismissCallbackRegistry,
keyguardBypassController,
keyguardUpdateMonitor,
)
- `when`(repository.startingDisappearAnimation.value).thenReturn(null)
- `when`(repository.show.value).thenReturn(null)
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
+ `when`(repository.primaryBouncerShow.value).thenReturn(null)
`when`(bouncerView.delegate).thenReturn(bouncerViewDelegate)
}
@Test
fun testShow_isScrimmed() {
- bouncerInteractor.show(true)
- verify(repository).setShowMessage(null)
+ mPrimaryBouncerInteractor.show(true)
verify(repository).setOnScreenTurnedOff(false)
verify(repository).setKeyguardAuthenticated(null)
- verify(repository).setHide(false)
- verify(repository).setStartingToHide(false)
- verify(repository).setScrimmed(true)
+ verify(repository).setPrimaryHide(false)
+ verify(repository).setPrimaryStartingToHide(false)
+ verify(repository).setPrimaryScrimmed(true)
verify(repository).setPanelExpansion(EXPANSION_VISIBLE)
- verify(repository).setShowingSoon(true)
+ verify(repository).setPrimaryShowingSoon(true)
verify(keyguardStateController).notifyBouncerShowing(true)
- verify(bouncerCallbackInteractor).dispatchStartingToShow()
- verify(repository).setVisible(true)
- verify(repository).setShow(any(KeyguardBouncerModel::class.java))
- verify(repository).setShowingSoon(false)
+ verify(mPrimaryBouncerCallbackInteractor).dispatchStartingToShow()
+ verify(repository).setPrimaryVisible(true)
+ verify(repository).setPrimaryShow(any(KeyguardBouncerModel::class.java))
+ verify(repository).setPrimaryShowingSoon(false)
}
@Test
@@ -117,60 +116,61 @@ class BouncerInteractorTest : SysuiTestCase() {
fun testShow_keyguardIsDone() {
`when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true)
verify(keyguardStateController, never()).notifyBouncerShowing(true)
- verify(bouncerCallbackInteractor, never()).dispatchStartingToShow()
+ verify(mPrimaryBouncerCallbackInteractor, never()).dispatchStartingToShow()
}
@Test
fun testHide() {
- bouncerInteractor.hide()
+ mPrimaryBouncerInteractor.hide()
verify(falsingCollector).onBouncerHidden()
verify(keyguardStateController).notifyBouncerShowing(false)
- verify(repository).setShowingSoon(false)
- verify(repository).setVisible(false)
- verify(repository).setHide(true)
- verify(repository).setShow(null)
+ verify(repository).setPrimaryShowingSoon(false)
+ verify(repository).setPrimaryVisible(false)
+ verify(repository).setPrimaryHide(true)
+ verify(repository).setPrimaryShow(null)
}
@Test
fun testExpansion() {
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
- bouncerInteractor.setPanelExpansion(0.6f)
+ mPrimaryBouncerInteractor.setPanelExpansion(0.6f)
verify(repository).setPanelExpansion(0.6f)
- verify(bouncerCallbackInteractor).dispatchExpansionChanged(0.6f)
+ verify(mPrimaryBouncerCallbackInteractor).dispatchExpansionChanged(0.6f)
}
@Test
fun testExpansion_fullyShown() {
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
- `when`(repository.startingDisappearAnimation.value).thenReturn(null)
- bouncerInteractor.setPanelExpansion(EXPANSION_VISIBLE)
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
+ mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_VISIBLE)
verify(falsingCollector).onBouncerShown()
- verify(bouncerCallbackInteractor).dispatchFullyShown()
+ verify(mPrimaryBouncerCallbackInteractor).dispatchFullyShown()
}
@Test
fun testExpansion_fullyHidden() {
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
- `when`(repository.startingDisappearAnimation.value).thenReturn(null)
- bouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN)
- verify(repository).setVisible(false)
- verify(repository).setShow(null)
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
+ mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN)
+ verify(repository).setPrimaryVisible(false)
+ verify(repository).setPrimaryShow(null)
+ verify(repository).setPrimaryHide(true)
verify(falsingCollector).onBouncerHidden()
- verify(bouncerCallbackInteractor).dispatchReset()
- verify(bouncerCallbackInteractor).dispatchFullyHidden()
+ verify(mPrimaryBouncerCallbackInteractor).dispatchReset()
+ verify(mPrimaryBouncerCallbackInteractor).dispatchFullyHidden()
}
@Test
fun testExpansion_startingToHide() {
`when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE)
- bouncerInteractor.setPanelExpansion(0.1f)
- verify(repository).setStartingToHide(true)
- verify(bouncerCallbackInteractor).dispatchStartingToHide()
+ mPrimaryBouncerInteractor.setPanelExpansion(0.1f)
+ verify(repository).setPrimaryStartingToHide(true)
+ verify(mPrimaryBouncerCallbackInteractor).dispatchStartingToHide()
}
@Test
fun testShowMessage() {
- bouncerInteractor.showMessage("abc", null)
+ mPrimaryBouncerInteractor.showMessage("abc", null)
verify(repository).setShowMessage(BouncerShowMessageModel("abc", null))
}
@@ -178,100 +178,100 @@ class BouncerInteractorTest : SysuiTestCase() {
fun testDismissAction() {
val onDismissAction = mock(ActivityStarter.OnDismissAction::class.java)
val cancelAction = mock(Runnable::class.java)
- bouncerInteractor.setDismissAction(onDismissAction, cancelAction)
+ mPrimaryBouncerInteractor.setDismissAction(onDismissAction, cancelAction)
verify(bouncerViewDelegate).setDismissAction(onDismissAction, cancelAction)
}
@Test
fun testUpdateResources() {
- bouncerInteractor.updateResources()
+ mPrimaryBouncerInteractor.updateResources()
verify(repository).setResourceUpdateRequests(true)
}
@Test
fun testNotifyKeyguardAuthenticated() {
- bouncerInteractor.notifyKeyguardAuthenticated(true)
+ mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(true)
verify(repository).setKeyguardAuthenticated(true)
}
@Test
fun testOnScreenTurnedOff() {
- bouncerInteractor.onScreenTurnedOff()
+ mPrimaryBouncerInteractor.onScreenTurnedOff()
verify(repository).setOnScreenTurnedOff(true)
}
@Test
fun testSetKeyguardPosition() {
- bouncerInteractor.setKeyguardPosition(0f)
+ mPrimaryBouncerInteractor.setKeyguardPosition(0f)
verify(repository).setKeyguardPosition(0f)
}
@Test
fun testNotifyKeyguardAuthenticatedHandled() {
- bouncerInteractor.notifyKeyguardAuthenticatedHandled()
+ mPrimaryBouncerInteractor.notifyKeyguardAuthenticatedHandled()
verify(repository).setKeyguardAuthenticated(null)
}
@Test
fun testNotifyUpdatedResources() {
- bouncerInteractor.notifyUpdatedResources()
+ mPrimaryBouncerInteractor.notifyUpdatedResources()
verify(repository).setResourceUpdateRequests(false)
}
@Test
fun testSetBackButtonEnabled() {
- bouncerInteractor.setBackButtonEnabled(true)
+ mPrimaryBouncerInteractor.setBackButtonEnabled(true)
verify(repository).setIsBackButtonEnabled(true)
}
@Test
fun testStartDisappearAnimation() {
val runnable = mock(Runnable::class.java)
- bouncerInteractor.startDisappearAnimation(runnable)
- verify(repository).setStartDisappearAnimation(any(Runnable::class.java))
+ mPrimaryBouncerInteractor.startDisappearAnimation(runnable)
+ verify(repository).setPrimaryStartDisappearAnimation(any(Runnable::class.java))
}
@Test
fun testIsFullShowing() {
- `when`(repository.isVisible.value).thenReturn(true)
+ `when`(repository.primaryBouncerVisible.value).thenReturn(true)
`when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE)
- `when`(repository.startingDisappearAnimation.value).thenReturn(null)
- assertThat(bouncerInteractor.isFullyShowing()).isTrue()
- `when`(repository.isVisible.value).thenReturn(false)
- assertThat(bouncerInteractor.isFullyShowing()).isFalse()
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
+ assertThat(mPrimaryBouncerInteractor.isFullyShowing()).isTrue()
+ `when`(repository.primaryBouncerVisible.value).thenReturn(false)
+ assertThat(mPrimaryBouncerInteractor.isFullyShowing()).isFalse()
}
@Test
fun testIsScrimmed() {
- `when`(repository.isScrimmed.value).thenReturn(true)
- assertThat(bouncerInteractor.isScrimmed()).isTrue()
- `when`(repository.isScrimmed.value).thenReturn(false)
- assertThat(bouncerInteractor.isScrimmed()).isFalse()
+ `when`(repository.primaryBouncerScrimmed.value).thenReturn(true)
+ assertThat(mPrimaryBouncerInteractor.isScrimmed()).isTrue()
+ `when`(repository.primaryBouncerScrimmed.value).thenReturn(false)
+ assertThat(mPrimaryBouncerInteractor.isScrimmed()).isFalse()
}
@Test
fun testIsInTransit() {
- `when`(repository.showingSoon.value).thenReturn(true)
- assertThat(bouncerInteractor.isInTransit()).isTrue()
- `when`(repository.showingSoon.value).thenReturn(false)
- assertThat(bouncerInteractor.isInTransit()).isFalse()
+ `when`(repository.primaryBouncerShowingSoon.value).thenReturn(true)
+ assertThat(mPrimaryBouncerInteractor.isInTransit()).isTrue()
+ `when`(repository.primaryBouncerShowingSoon.value).thenReturn(false)
+ assertThat(mPrimaryBouncerInteractor.isInTransit()).isFalse()
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
- assertThat(bouncerInteractor.isInTransit()).isTrue()
+ assertThat(mPrimaryBouncerInteractor.isInTransit()).isTrue()
}
@Test
fun testIsAnimatingAway() {
- `when`(repository.startingDisappearAnimation.value).thenReturn(Runnable {})
- assertThat(bouncerInteractor.isAnimatingAway()).isTrue()
- `when`(repository.startingDisappearAnimation.value).thenReturn(null)
- assertThat(bouncerInteractor.isAnimatingAway()).isFalse()
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(Runnable {})
+ assertThat(mPrimaryBouncerInteractor.isAnimatingAway()).isTrue()
+ `when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
+ assertThat(mPrimaryBouncerInteractor.isAnimatingAway()).isFalse()
}
@Test
fun testWillDismissWithAction() {
`when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(true)
- assertThat(bouncerInteractor.willDismissWithAction()).isTrue()
+ assertThat(mPrimaryBouncerInteractor.willDismissWithAction()).isTrue()
`when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(false)
- assertThat(bouncerInteractor.willDismissWithAction()).isFalse()
+ assertThat(mPrimaryBouncerInteractor.willDismissWithAction()).isFalse()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
index a8f413848009..a94374680b91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
@@ -25,7 +25,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.monet.ColorScheme
-import com.android.systemui.ripple.MultiRippleController
+import com.android.systemui.surfaceeffects.ripple.MultiRippleController
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
import junit.framework.Assert.assertEquals
import org.junit.After
import org.junit.Before
@@ -62,6 +63,7 @@ class ColorSchemeTransitionTest : SysuiTestCase() {
@Mock private lateinit var mediaViewHolder: MediaViewHolder
@Mock private lateinit var gutsViewHolder: GutsViewHolder
@Mock private lateinit var multiRippleController: MultiRippleController
+ @Mock private lateinit var turbulenceNoiseController: TurbulenceNoiseController
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
@@ -76,6 +78,7 @@ class ColorSchemeTransitionTest : SysuiTestCase() {
context,
mediaViewHolder,
multiRippleController,
+ turbulenceNoiseController,
animatingColorTransitionFactory
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index 81901569bde8..eb1f5e429182 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -78,9 +78,10 @@ import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.dialog.MediaOutputDialogFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.ripple.MultiRippleView
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.surfaceeffects.ripple.MultiRippleView
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.KotlinArgumentCaptor
@@ -178,6 +179,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
private lateinit var dismiss: FrameLayout
private lateinit var dismissText: TextView
private lateinit var multiRippleView: MultiRippleView
+ private lateinit var turbulenceNoiseView: TurbulenceNoiseView
private lateinit var session: MediaSession
private lateinit var device: MediaDeviceData
@@ -382,6 +384,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
multiRippleView = MultiRippleView(context, null)
+ turbulenceNoiseView = TurbulenceNoiseView(context, null)
whenever(viewHolder.player).thenReturn(view)
whenever(viewHolder.appIcon).thenReturn(appIcon)
@@ -425,6 +428,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.actionsTopBarrier).thenReturn(actionsTopBarrier)
whenever(viewHolder.multiRippleView).thenReturn(multiRippleView)
+ whenever(viewHolder.turbulenceNoiseView).thenReturn(turbulenceNoiseView)
}
/** Initialize elements for the recommendation view holder */
@@ -1137,6 +1141,19 @@ public class MediaControlPanelTest : SysuiTestCase() {
/* ***** Guts tests for the player ***** */
@Test
+ fun player_longClick_isFalse() {
+ whenever(falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)).thenReturn(true)
+ player.attachPlayer(viewHolder)
+
+ val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+ verify(viewHolder.player).onLongClickListener = captor.capture()
+
+ captor.value.onLongClick(viewHolder.player)
+ verify(mediaViewController, never()).openGuts()
+ verify(mediaViewController, never()).closeGuts()
+ }
+
+ @Test
fun player_longClickWhenGutsClosed_gutsOpens() {
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, KEY)
@@ -1316,6 +1333,20 @@ public class MediaControlPanelTest : SysuiTestCase() {
/* ***** Guts tests for the recommendations ***** */
@Test
+ fun recommendations_longClick_isFalse() {
+ whenever(falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)).thenReturn(true)
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+ verify(viewHolder.player).onLongClickListener = captor.capture()
+
+ captor.value.onLongClick(viewHolder.player)
+ verify(mediaViewController, never()).openGuts()
+ verify(mediaViewController, never()).closeGuts()
+ }
+
+ @Test
fun recommendations_longClickWhenGutsClosed_gutsOpens() {
player.attachRecommendation(recommendationViewHolder)
player.bindRecommendation(smartspaceData)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 68a5f47c5e0b..885cc54af7cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -261,7 +261,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Test
fun updateView_noOverrides_usesInfoFromAppIcon() {
controllerReceiver.displayView(
- ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appNameOverride = null)
+ ChipReceiverInfo(
+ routeInfo,
+ appIconDrawableOverride = null,
+ appNameOverride = null,
+ id = "id",
+ )
)
val view = getChipView()
@@ -274,7 +279,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
val drawableOverride = context.getDrawable(R.drawable.ic_celebration)!!
controllerReceiver.displayView(
- ChipReceiverInfo(routeInfo, drawableOverride, appNameOverride = null)
+ ChipReceiverInfo(
+ routeInfo,
+ drawableOverride,
+ appNameOverride = null,
+ id = "id",
+ )
)
val view = getChipView()
@@ -286,7 +296,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
val appNameOverride = "Sweet New App"
controllerReceiver.displayView(
- ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appNameOverride)
+ ChipReceiverInfo(
+ routeInfo,
+ appIconDrawableOverride = null,
+ appNameOverride,
+ id = "id",
+ )
)
val view = getChipView()
@@ -340,7 +355,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
.addFeature("feature")
.setClientPackageName(packageName)
.build()
- return ChipReceiverInfo(routeInfo, null, null)
+ return ChipReceiverInfo(routeInfo, null, null, id = "id")
}
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt
new file mode 100644
index 000000000000..9b346d0120ef
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/IconLoaderLibAppIconLoaderTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.mediaprojection.appselector.data
+
+import android.content.ComponentName
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import androidx.test.filters.SmallTest
+import com.android.launcher3.icons.BitmapInfo
+import com.android.launcher3.icons.FastBitmapDrawable
+import com.android.launcher3.icons.IconFactory
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.system.PackageManagerWrapper
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class IconLoaderLibAppIconLoaderTest : SysuiTestCase() {
+
+ private val iconFactory: IconFactory = mock()
+ private val packageManagerWrapper: PackageManagerWrapper = mock()
+ private val packageManager: PackageManager = mock()
+ private val dispatcher = Dispatchers.Unconfined
+
+ private val appIconLoader =
+ IconLoaderLibAppIconLoader(
+ backgroundDispatcher = dispatcher,
+ context = context,
+ packageManagerWrapper = packageManagerWrapper,
+ packageManager = packageManager,
+ iconFactoryProvider = { iconFactory }
+ )
+
+ @Test
+ fun loadIcon_loadsIconUsingTheSameUserId() {
+ val icon = createIcon()
+ val component = ComponentName("com.test", "TestApplication")
+ givenIcon(component, userId = 123, icon = icon)
+
+ val loadedIcon = runBlocking { appIconLoader.loadIcon(userId = 123, component = component) }
+
+ assertThat(loadedIcon).isEqualTo(icon)
+ }
+
+ private fun givenIcon(component: ComponentName, userId: Int, icon: FastBitmapDrawable) {
+ val activityInfo = mock<ActivityInfo>()
+ whenever(packageManagerWrapper.getActivityInfo(component, userId)).thenReturn(activityInfo)
+ val rawIcon = mock<Drawable>()
+ whenever(activityInfo.loadIcon(packageManager)).thenReturn(rawIcon)
+
+ val bitmapInfo = mock<BitmapInfo>()
+ whenever(iconFactory.createBadgedIconBitmap(eq(rawIcon), any())).thenReturn(bitmapInfo)
+ whenever(bitmapInfo.newIcon(context)).thenReturn(icon)
+ }
+
+ private fun createIcon(): FastBitmapDrawable =
+ FastBitmapDrawable(Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888))
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
index 4c7240673fb0..3620233fc9df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
@@ -19,6 +19,7 @@ import com.android.systemui.privacy.PrivacyDialogController
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -66,6 +67,8 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
private lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock
private lateinit var safetyCenterManager: SafetyCenterManager
+ @Mock
+ private lateinit var deviceProvisionedController: DeviceProvisionedController
private val uiExecutor = FakeExecutor(FakeSystemClock())
private val backgroundExecutor = FakeExecutor(FakeSystemClock())
@@ -80,6 +83,7 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
whenever(privacyChip.context).thenReturn(context)
whenever(privacyChip.resources).thenReturn(context.resources)
whenever(privacyChip.isAttachedToWindow).thenReturn(true)
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
@@ -98,7 +102,8 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
activityStarter,
appOpsController,
broadcastDispatcher,
- safetyCenterManager
+ safetyCenterManager,
+ deviceProvisionedController
)
backgroundExecutor.runAllReady()
@@ -199,6 +204,18 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
)
}
+ @Test
+ fun testNoDialogWhenDeviceNotProvisioned() {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+ controller.onParentVisible()
+
+ val captor = argumentCaptor<View.OnClickListener>()
+ verify(privacyChip).setOnClickListener(capture(captor))
+
+ captor.value.onClick(privacyChip)
+ verify(privacyDialogController, never()).showDialog(any(Context::class.java))
+ }
+
private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
whenever(privacyItemController.locationAvailable).thenReturn(location)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index cd7a949443c9..72e022ed7bbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -439,6 +439,17 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
verify(mQSPanelController).setExpanded(false);
}
+ @Test
+ public void startsListeningAfterStateChangeToExpanded_inSplitShade() {
+ QSFragment fragment = resumeAndGetFragment();
+ enableSplitShade();
+ fragment.setQsVisible(true);
+ clearInvocations(mQSPanelController);
+
+ fragment.setExpanded(true);
+ verify(mQSPanelController).setListening(true, true);
+ }
+
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
MockitoAnnotations.initMocks(this);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 3c867ab32725..9f28708a388e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -64,7 +64,7 @@ class QSPanelControllerTest : SysuiTestCase() {
whenever(brightnessSliderFactory.create(any(), any())).thenReturn(brightnessSlider)
whenever(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
whenever(qsPanel.resources).thenReturn(mContext.orCreateTestableResources.resources)
- whenever(statusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false)
+ whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false)
whenever(qsPanel.setListening(anyBoolean())).then {
whenever(qsPanel.isListening).thenReturn(it.getArgument(0))
}
@@ -116,9 +116,9 @@ class QSPanelControllerTest : SysuiTestCase() {
@Test
fun testIsBouncerInTransit() {
- whenever(statusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true)
+ whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true)
assertThat(controller.isBouncerInTransit()).isEqualTo(true)
- whenever(statusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false)
+ whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false)
assertThat(controller.isBouncerInTransit()).isEqualTo(false)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index 5abc0e1f9c89..35c8cc70953d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -27,6 +27,8 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.content.Context;
+import android.content.res.Resources;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -42,16 +44,22 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TileLayoutTest extends SysuiTestCase {
- private TileLayout mTileLayout;
+ private Resources mResources;
private int mLayoutSizeForOneTile;
+ private TileLayout mTileLayout; // under test
@Before
public void setUp() throws Exception {
- mTileLayout = new TileLayout(mContext);
+ Context context = Mockito.spy(mContext);
+ mResources = Mockito.spy(context.getResources());
+ Mockito.when(mContext.getResources()).thenReturn(mResources);
+
+ mTileLayout = new TileLayout(context);
// Layout needs to leave space for the tile margins. Three times the margin size is
// sufficient for any number of columns.
mLayoutSizeForOneTile =
@@ -203,4 +211,21 @@ public class TileLayoutTest extends SysuiTestCase {
verify(tileRecord1.tileView).setPosition(0);
verify(tileRecord2.tileView).setPosition(1);
}
+
+ @Test
+ public void resourcesChanged_updateResources_returnsTrue() {
+ Mockito.when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(1);
+ mTileLayout.updateResources(); // setup with 1
+ Mockito.when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(2);
+
+ assertEquals(true, mTileLayout.updateResources());
+ }
+
+ @Test
+ public void resourcesSame_updateResources_returnsFalse() {
+ Mockito.when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(1);
+ mTileLayout.updateResources(); // setup with 1
+
+ assertEquals(false, mTileLayout.updateResources());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index b067ee76f3b3..f55d262cfc0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -40,6 +40,8 @@ import com.android.systemui.plugins.qs.QSTile.State;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
@UiThreadTest
@@ -138,7 +140,7 @@ public class QSIconViewImplTest extends SysuiTestCase {
}
@Test
- public void testIconNotAnimatedWhenAllowAnimationsFalse() {
+ public void testIconStartedAndStoppedWhenAllowAnimationsFalse() {
ImageView iv = new ImageView(mContext);
AnimatedVectorDrawable d = mock(AnimatedVectorDrawable.class);
State s = new State();
@@ -148,7 +150,9 @@ public class QSIconViewImplTest extends SysuiTestCase {
mIconView.updateIcon(iv, s, false);
- verify(d, never()).start();
+ InOrder inOrder = Mockito.inOrder(d);
+ inOrder.verify(d).start();
+ inOrder.verify(d).stop();
}
private static Drawable.ConstantState fakeConstantState(Drawable otherDrawable) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
index b652aee0f6aa..cac90a14096e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
@@ -119,6 +119,6 @@ public class QRCodeScannerTileTest extends SysuiTestCase {
when(mController.isEnabledForQuickSettings()).thenReturn(true);
QSTile.State state = new QSTile.State();
mTile.handleUpdateState(state, null);
- assertEquals(state.state, Tile.STATE_ACTIVE);
+ assertEquals(state.state, Tile.STATE_INACTIVE);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 7b1e5c9f7264..05c1f1574d1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -5,6 +5,7 @@ import static android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GREAT;
import static android.telephony.SignalStrength.SIGNAL_STRENGTH_POOR;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_VERTICAL_WEIGHT;
import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX;
@@ -13,6 +14,7 @@ import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -36,6 +38,7 @@ import android.net.wifi.WifiManager;
import android.os.Handler;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
@@ -55,6 +58,8 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.UnreleasedFlag;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -68,12 +73,15 @@ import com.android.systemui.util.time.FakeSystemClock;
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.List;
@@ -84,6 +92,9 @@ import java.util.List;
public class InternetDialogControllerTest extends SysuiTestCase {
private static final int SUB_ID = 1;
+ private static final int SUB_ID2 = 2;
+
+ private MockitoSession mStaticMockSession;
//SystemUIToast
private static final int GRAVITY_FLAGS = Gravity.FILL_HORIZONTAL | Gravity.FILL_VERTICAL;
@@ -150,6 +161,8 @@ public class InternetDialogControllerTest extends SysuiTestCase {
private WifiStateWorker mWifiStateWorker;
@Mock
private SignalStrength mSignalStrength;
+ @Mock
+ private FeatureFlags mFlags;
private TestableResources mTestableResources;
private InternetDialogController mInternetDialogController;
@@ -159,6 +172,10 @@ public class InternetDialogControllerTest extends SysuiTestCase {
@Before
public void setUp() {
+ mStaticMockSession = mockitoSession()
+ .mockStatic(SubscriptionManager.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
MockitoAnnotations.initMocks(this);
mTestableResources = mContext.getOrCreateTestableResources();
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
@@ -175,6 +192,7 @@ public class InternetDialogControllerTest extends SysuiTestCase {
mAccessPoints.add(mWifiEntry1);
when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry);
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{SUB_ID});
+ when(SubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(SUB_ID);
when(mToastFactory.createToast(any(), anyString(), anyString(), anyInt(), anyInt()))
.thenReturn(mSystemUIToast);
when(mSystemUIToast.getView()).thenReturn(mToastView);
@@ -188,7 +206,7 @@ public class InternetDialogControllerTest extends SysuiTestCase {
mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher,
mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController,
mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker,
- mLocationController, mDialogLaunchAnimator, mWifiStateWorker);
+ mLocationController, mDialogLaunchAnimator, mWifiStateWorker, mFlags);
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
mInternetDialogController.mOnSubscriptionsChangedListener);
mInternetDialogController.onStart(mInternetDialogCallback, true);
@@ -197,6 +215,11 @@ public class InternetDialogControllerTest extends SysuiTestCase {
mInternetDialogController.mWifiIconInjector = mWifiIconInjector;
}
+ @After
+ public void tearDown() {
+ mStaticMockSession.finishMocking();
+ }
+
@Test
public void connectCarrierNetwork_mergedCarrierEntryCanConnect_connectAndCreateSysUiToast() {
when(mMergedCarrierEntry.canConnect()).thenReturn(true);
@@ -325,15 +348,45 @@ public class InternetDialogControllerTest extends SysuiTestCase {
@Test
public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
+ when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
fakeAirplaneModeEnabled(false);
when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
- mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+ spyController.onAccessPointsChanged(null /* accessPoints */);
+ doReturn(SUB_ID2).when(spyController).getActiveAutoSwitchNonDdsSubId();
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
doReturn(mServiceState).when(mTelephonyManager).getServiceState();
doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
- assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+ assertFalse(TextUtils.equals(spyController.getSubtitleText(false),
+ getResourcesString("all_network_unavailable")));
+
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ .when(spyController).getActiveAutoSwitchNonDdsSubId();
+ spyController.onAccessPointsChanged(null /* accessPoints */);
+ assertTrue(TextUtils.equals(spyController.getSubtitleText(false),
+ getResourcesString("all_network_unavailable")));
+ }
+
+ @Test
+ public void getSubtitleText_withNoService_returnNoNetworksAvailable_flagOff() {
+ InternetDialogController spyController = spy(mInternetDialogController);
+ fakeAirplaneModeEnabled(false);
+ when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
+ spyController.onAccessPointsChanged(null /* accessPoints */);
+
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+ doReturn(mServiceState).when(mTelephonyManager).getServiceState();
+ doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
+
+ assertTrue(TextUtils.equals(spyController.getSubtitleText(false),
+ getResourcesString("all_network_unavailable")));
+
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ .when(spyController).getActiveAutoSwitchNonDdsSubId();
+ spyController.onAccessPointsChanged(null /* accessPoints */);
+ assertTrue(TextUtils.equals(spyController.getSubtitleText(false),
getResourcesString("all_network_unavailable")));
}
@@ -651,6 +704,108 @@ public class InternetDialogControllerTest extends SysuiTestCase {
}
@Test
+ public void getSignalStrengthIcon_differentSubId() {
+ when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
+ Drawable icons = spyController.getSignalStrengthIcon(SUB_ID, mContext, 1, 1, 0, false);
+ Drawable icons2 = spyController.getSignalStrengthIcon(SUB_ID2, mContext, 1, 1, 0, false);
+
+ assertThat(icons).isNotEqualTo(icons2);
+ }
+
+ @Test
+ public void getActiveAutoSwitchNonDdsSubId() {
+ when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ // active on non-DDS
+ SubscriptionInfo info = mock(SubscriptionInfo.class);
+ doReturn(SUB_ID2).when(info).getSubscriptionId();
+ when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info);
+
+ int subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+ assertThat(subId).isEqualTo(SUB_ID2);
+
+ // active on CBRS
+ doReturn(true).when(info).isOpportunistic();
+ subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+ assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ // active on DDS
+ doReturn(false).when(info).isOpportunistic();
+ doReturn(SUB_ID).when(info).getSubscriptionId();
+ when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info);
+
+ subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+ assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+
+ @Test
+ public void getActiveAutoSwitchNonDdsSubId_flagOff() {
+ // active on non-DDS
+ SubscriptionInfo info = mock(SubscriptionInfo.class);
+ doReturn(SUB_ID2).when(info).getSubscriptionId();
+ when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info);
+
+ int subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+ assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+
+ @Test
+ public void getMobileNetworkSummary() {
+ when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
+ doReturn(SUB_ID2).when(spyController).getActiveAutoSwitchNonDdsSubId();
+ doReturn(true).when(spyController).isMobileDataEnabled();
+ doReturn(true).when(spyController).activeNetworkIsCellular();
+ String dds = spyController.getMobileNetworkSummary(SUB_ID);
+ String nonDds = spyController.getMobileNetworkSummary(SUB_ID2);
+
+ assertThat(dds).contains(mContext.getString(R.string.mobile_data_poor_connection));
+ assertThat(dds).isNotEqualTo(nonDds);
+ }
+
+ @Test
+ public void getMobileNetworkSummary_flagOff() {
+ InternetDialogController spyController = spy(mInternetDialogController);
+ doReturn(true).when(spyController).isMobileDataEnabled();
+ doReturn(true).when(spyController).activeNetworkIsCellular();
+ String dds = spyController.getMobileNetworkSummary(SUB_ID);
+
+ assertThat(dds).contains(mContext.getString(R.string.mobile_data_connection_active));
+ }
+
+ @Test
+ public void launchMobileNetworkSettings_validSubId() {
+ when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
+ doReturn(SUB_ID2).when(spyController).getActiveAutoSwitchNonDdsSubId();
+ spyController.launchMobileNetworkSettings(mDialogLaunchView);
+
+ verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt(),
+ any());
+ }
+
+ @Test
+ public void launchMobileNetworkSettings_invalidSubId() {
+ when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ InternetDialogController spyController = spy(mInternetDialogController);
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ .when(spyController).getActiveAutoSwitchNonDdsSubId();
+ spyController.launchMobileNetworkSettings(mDialogLaunchView);
+
+ verify(mActivityStarter, never())
+ .postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
+ }
+
+ @Test
+ public void setAutoDataSwitchMobileDataPolicy() {
+ when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ mInternetDialogController.setAutoDataSwitchMobileDataPolicy(SUB_ID, true);
+
+ verify(mTelephonyManager).setMobileDataPolicyEnabled(eq(
+ TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH), eq(true));
+ }
+
+ @Test
public void getSignalStrengthDrawableWithLevel_carrierNetworkIsNotActive_useMobileDataLevel() {
// Fake mobile data level as SIGNAL_STRENGTH_POOR(1)
when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_POOR);
@@ -658,9 +813,9 @@ public class InternetDialogControllerTest extends SysuiTestCase {
when(mInternetDialogController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX);
InternetDialogController spyController = spy(mInternetDialogController);
- spyController.getSignalStrengthDrawableWithLevel(false /* isCarrierNetworkActive */);
+ spyController.getSignalStrengthDrawableWithLevel(false /* isCarrierNetworkActive */, 0);
- verify(spyController).getSignalStrengthIcon(any(), eq(SIGNAL_STRENGTH_POOR),
+ verify(spyController).getSignalStrengthIcon(eq(0), any(), eq(SIGNAL_STRENGTH_POOR),
eq(NUM_SIGNAL_STRENGTH_BINS), anyInt(), anyBoolean());
}
@@ -672,9 +827,9 @@ public class InternetDialogControllerTest extends SysuiTestCase {
when(mInternetDialogController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX);
InternetDialogController spyController = spy(mInternetDialogController);
- spyController.getSignalStrengthDrawableWithLevel(true /* isCarrierNetworkActive */);
+ spyController.getSignalStrengthDrawableWithLevel(true /* isCarrierNetworkActive */, 0);
- verify(spyController).getSignalStrengthIcon(any(), eq(WIFI_LEVEL_MAX),
+ verify(spyController).getSignalStrengthIcon(eq(0), any(), eq(WIFI_LEVEL_MAX),
eq(WIFI_LEVEL_MAX + 1), anyInt(), anyBoolean());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index f92247580df0..8c8fdc5bf126 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -8,12 +8,15 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
@@ -31,6 +34,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -72,6 +76,8 @@ public class InternetDialogTest extends SysuiTestCase {
private InternetDialogController mInternetDialogController;
@Mock
private KeyguardStateController mKeyguard;
+ @Mock
+ private DialogLaunchAnimator mDialogLaunchAnimator;
private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
private InternetDialog mInternetDialog;
@@ -100,8 +106,9 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true);
when(mWifiEntries.size()).thenReturn(1);
- when(mInternetDialogController.getMobileNetworkTitle()).thenReturn(MOBILE_NETWORK_TITLE);
- when(mInternetDialogController.getMobileNetworkSummary())
+ when(mInternetDialogController.getMobileNetworkTitle(anyInt()))
+ .thenReturn(MOBILE_NETWORK_TITLE);
+ when(mInternetDialogController.getMobileNetworkSummary(anyInt()))
.thenReturn(MOBILE_NETWORK_SUMMARY);
when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
@@ -115,7 +122,8 @@ public class InternetDialogTest extends SysuiTestCase {
private void createInternetDialog() {
mInternetDialog = new InternetDialog(mContext, mock(InternetDialogFactory.class),
- mInternetDialogController, true, true, true, mock(UiEventLogger.class), mHandler,
+ mInternetDialogController, true, true, true, mock(UiEventLogger.class),
+ mDialogLaunchAnimator, mHandler,
mBgExecutor, mKeyguard);
mInternetDialog.mAdapter = mInternetAdapter;
mInternetDialog.mConnectedWifiEntry = mInternetWifiEntry;
@@ -307,12 +315,18 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
+ mInternetDialog.dismissDialog();
+ doReturn(true).when(mInternetDialogController).hasActiveSubId();
+ createInternetDialog();
// The preconditions WiFi ON and Internet WiFi are already in setUp()
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
- mInternetDialog.updateDialog(false);
+ mInternetDialog.updateDialog(true);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+ LinearLayout secondaryLayout = mDialogView.requireViewById(
+ R.id.secondary_mobile_network_layout);
+ assertThat(secondaryLayout.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -460,6 +474,45 @@ public class InternetDialogTest extends SysuiTestCase {
}
@Test
+ public void updateDialog_showSecondaryDataSub() {
+ mInternetDialog.dismissDialog();
+ doReturn(1).when(mInternetDialogController).getActiveAutoSwitchNonDdsSubId();
+ doReturn(true).when(mInternetDialogController).hasActiveSubId();
+ doReturn(false).when(mInternetDialogController).isAirplaneModeEnabled();
+ createInternetDialog();
+
+ clearInvocations(mInternetDialogController);
+ mInternetDialog.updateDialog(true);
+
+ LinearLayout primaryLayout = mDialogView.requireViewById(
+ R.id.mobile_network_layout);
+ LinearLayout secondaryLayout = mDialogView.requireViewById(
+ R.id.secondary_mobile_network_layout);
+
+ verify(mInternetDialogController).getMobileNetworkSummary(1);
+ assertThat(primaryLayout.getBackground()).isNotEqualTo(secondaryLayout.getBackground());
+
+ // Tap the primary sub info
+ primaryLayout.performClick();
+ ArgumentCaptor<AlertDialog> dialogArgumentCaptor =
+ ArgumentCaptor.forClass(AlertDialog.class);
+ verify(mDialogLaunchAnimator).showFromDialog(dialogArgumentCaptor.capture(),
+ eq(mInternetDialog), eq(null), eq(false));
+ AlertDialog dialog = dialogArgumentCaptor.getValue();
+ dialog.show();
+ dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
+ // TODO(b/253399304)
+ // TestableLooper.get(this).processAllMessages();
+ // verify(mInternetDialogController).setAutoDataSwitchMobileDataPolicy(1, false);
+
+ // Tap the secondary sub info
+ secondaryLayout.performClick();
+ verify(mInternetDialogController).launchMobileNetworkSettings(any(View.class));
+
+ dialog.dismiss();
+ }
+
+ @Test
public void updateDialog_wifiOn_hideWifiScanNotify() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
new file mode 100644
index 000000000000..0aa36218b3a7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenrecord
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import android.widget.Spinner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class ScreenRecordPermissionDialogTest : SysuiTestCase() {
+
+ @Mock private lateinit var starter: ActivityStarter
+ @Mock private lateinit var controller: RecordingController
+ @Mock private lateinit var userContextProvider: UserContextProvider
+ @Mock private lateinit var flags: FeatureFlags
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Mock private lateinit var onStartRecordingClicked: Runnable
+
+ private lateinit var dialog: ScreenRecordPermissionDialog
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ dialog =
+ ScreenRecordPermissionDialog(
+ context,
+ controller,
+ starter,
+ dialogLaunchAnimator,
+ userContextProvider,
+ onStartRecordingClicked
+ )
+ dialog.onCreate(null)
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
+ }
+
+ @After
+ fun teardown() {
+ if (::dialog.isInitialized) {
+ dialog.dismiss()
+ }
+ }
+
+ @Test
+ fun testShowDialog_partialScreenSharingEnabled_optionsSpinnerIsVisible() {
+ dialog.show()
+
+ val visibility = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner).visibility
+ assertThat(visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun testShowDialog_singleAppSelected_showTapsIsGone() {
+ dialog.show()
+ onSpinnerItemSelected(SINGLE_APP)
+
+ val visibility = dialog.requireViewById<View>(R.id.show_taps).visibility
+ assertThat(visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun testShowDialog_entireScreenSelected_showTapsIsVisible() {
+ dialog.show()
+ onSpinnerItemSelected(ENTIRE_SCREEN)
+
+ val visibility = dialog.requireViewById<View>(R.id.show_taps).visibility
+ assertThat(visibility).isEqualTo(View.VISIBLE)
+ }
+
+ private fun onSpinnerItemSelected(position: Int) {
+ val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
+ spinner.onItemSelectedListener.onItemSelected(spinner, mock(), position, /* id= */ 0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index 0ce9056dc1d1..bc17c19df8f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -24,6 +24,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -320,6 +321,64 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() {
assertThat(changes.largeScreenConstraintsChanges).isNull()
}
+ @Test
+ fun testRelevantViewsAreNotMatchConstraints() {
+ val views = mapOf(
+ R.id.clock to "clock",
+ R.id.date to "date",
+ R.id.statusIcons to "icons",
+ R.id.privacy_container to "privacy",
+ R.id.carrier_group to "carriers",
+ R.id.batteryRemainingIcon to "battery",
+ )
+ views.forEach { (id, name) ->
+ assertWithMessage("$name has 0 height in qqs")
+ .that(qqsConstraint.getConstraint(id).layout.mHeight).isNotEqualTo(0)
+ assertWithMessage("$name has 0 width in qqs")
+ .that(qqsConstraint.getConstraint(id).layout.mWidth).isNotEqualTo(0)
+ assertWithMessage("$name has 0 height in qs")
+ .that(qsConstraint.getConstraint(id).layout.mHeight).isNotEqualTo(0)
+ assertWithMessage("$name has 0 width in qs")
+ .that(qsConstraint.getConstraint(id).layout.mWidth).isNotEqualTo(0)
+ }
+ }
+
+ @Test
+ fun testCheckViewsDontChangeSizeBetweenAnimationConstraints() {
+ val views = mapOf(
+ R.id.clock to "clock",
+ R.id.date to "date",
+ R.id.statusIcons to "icons",
+ R.id.privacy_container to "privacy",
+ R.id.carrier_group to "carriers",
+ R.id.batteryRemainingIcon to "battery",
+ )
+ views.forEach { (id, name) ->
+ assertWithMessage("$name changes height")
+ .that(qqsConstraint.getConstraint(id).layout.mHeight)
+ .isEqualTo(qsConstraint.getConstraint(id).layout.mHeight)
+ assertWithMessage("$name changes width")
+ .that(qqsConstraint.getConstraint(id).layout.mWidth)
+ .isEqualTo(qsConstraint.getConstraint(id).layout.mWidth)
+ }
+ }
+
+ @Test
+ fun testEmptyCutoutDateIconsAreConstrainedWidth() {
+ CombinedShadeHeadersConstraintManagerImpl.emptyCutoutConstraints()()
+
+ assertThat(qqsConstraint.getConstraint(R.id.date).layout.constrainedWidth).isTrue()
+ assertThat(qqsConstraint.getConstraint(R.id.statusIcons).layout.constrainedWidth).isTrue()
+ }
+
+ @Test
+ fun testCenterCutoutDateIconsAreConstrainedWidth() {
+ CombinedShadeHeadersConstraintManagerImpl.centerCutoutConstraints(false, 10)()
+
+ assertThat(qqsConstraint.getConstraint(R.id.date).layout.constrainedWidth).isTrue()
+ assertThat(qqsConstraint.getConstraint(R.id.statusIcons).layout.constrainedWidth).isTrue()
+ }
+
private operator fun ConstraintsChanges.invoke() {
qqsConstraintsChanges?.invoke(qqsConstraint)
qsConstraintsChanges?.invoke(qsConstraint)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index c98c1f2bc97e..7d2251e20021 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -94,7 +94,6 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.camera.CameraGestureHelper;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.doze.DozeLog;
@@ -308,7 +307,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
SystemClock systemClock = new FakeSystemClock();
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
- mInteractionJankMonitor);
+ mInteractionJankMonitor, mShadeExpansionStateManager);
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -379,7 +378,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mDumpManager,
mock(HeadsUpManagerPhone.class),
new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
- mInteractionJankMonitor),
+ mInteractionJankMonitor, mShadeExpansionStateManager),
mKeyguardBypassController,
mDozeParameters,
mScreenOffAnimationController);
@@ -492,9 +491,9 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mUnlockedScreenOffAnimationController,
mShadeTransitionController,
systemClock,
- mock(CameraGestureHelper.class),
mKeyguardBottomAreaViewModel,
- mKeyguardBottomAreaInteractor);
+ mKeyguardBottomAreaInteractor,
+ mDumpManager);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
() -> {},
@@ -854,7 +853,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId(),
null);
- verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true);
}
@Test
@@ -864,7 +863,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId(),
null);
- verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 95cf9d60b511..d7d17b5132cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -239,9 +239,9 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Test
public void setPanelExpanded_notFocusable_altFocusable_whenPanelIsOpen() {
- mNotificationShadeWindowController.setPanelExpanded(true);
+ mNotificationShadeWindowController.onShadeExpansionFullyChanged(true);
clearInvocations(mWindowManager);
- mNotificationShadeWindowController.setPanelExpanded(true);
+ mNotificationShadeWindowController.onShadeExpansionFullyChanged(true);
verifyNoMoreInteractions(mWindowManager);
mNotificationShadeWindowController.setNotificationShadeFocusable(true);
@@ -313,7 +313,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
verifyNoMoreInteractions(mWindowManager);
clearInvocations(mWindowManager);
- mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
+ mNotificationShadeWindowController.batchApplyWindowLayoutParams(() -> {
mNotificationShadeWindowController.setForceDozeBrightness(false);
verify(mWindowManager, never()).updateViewLayout(any(), any());
});
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index a4a7995ae3c8..a6c80ab649e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -152,7 +152,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(true);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we should intercept touch
@@ -165,7 +165,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
// WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(false);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we shouldn't intercept touch
@@ -178,7 +178,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateBouncer()).thenReturn(true);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we should handle the touch
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 9b17cc296081..2487cafe0d90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -87,6 +87,7 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.logging.KeyguardLogger;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -271,7 +272,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mUserManager, mExecutor, mExecutor, mFalsingManager,
mAuthController, mLockPatternUtils, mScreenLifecycle,
mKeyguardBypassController, mAccessibilityManager,
- mFaceHelpMessageDeferral);
+ mFaceHelpMessageDeferral, mock(KeyguardLogger.class));
mController.init();
mController.setIndicationArea(mIndicationArea);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
@@ -1064,7 +1065,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// GIVEN a trust granted message but trust isn't granted
final String trustGrantedMsg = "testing trust granted message";
- mController.getKeyguardCallback().showTrustGrantedMessage(trustGrantedMsg);
+ mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, trustGrantedMsg);
verifyHideIndication(INDICATION_TYPE_TRUST);
@@ -1088,7 +1089,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// WHEN the showTrustGranted method is called
final String trustGrantedMsg = "testing trust granted message";
- mController.getKeyguardCallback().showTrustGrantedMessage(trustGrantedMsg);
+ mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, trustGrantedMsg);
// THEN verify the trust granted message shows
verifyIndicationMessage(
@@ -1105,7 +1106,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
// WHEN the showTrustGranted method is called with a null message
- mController.getKeyguardCallback().showTrustGrantedMessage(null);
+ mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, null);
// THEN verify the default trust granted message shows
verifyIndicationMessage(
@@ -1122,7 +1123,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
// WHEN the showTrustGranted method is called with an EMPTY string
- mController.getKeyguardCallback().showTrustGrantedMessage("");
+ mController.getKeyguardCallback().onTrustGrantedWithFlags(0, 0, "");
// THEN verify NO trust message is shown
verifyNoMessage(INDICATION_TYPE_TRUST);
@@ -1418,6 +1419,21 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
+ public void onBiometricError_faceLockedOutSecondTimeOnBouncer_showsUnavailableMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+
+ onFaceLockoutError("second lockout");
+
+ verify(mStatusBarKeyguardViewManager)
+ .setKeyguardMessage(
+ eq(mContext.getString(R.string.keyguard_face_unlock_unavailable)),
+ any());
+ }
+
+ @Test
public void onBiometricError_faceLockedOutSecondTimeButUdfpsActive_showsNoMessage() {
createController();
onFaceLockoutError("first lockout");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 1d8e5dec5c50..5124eb992dc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -25,6 +25,7 @@ import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeExpansionStateManager
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -48,6 +49,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
@Mock private lateinit var mockDarkAnimator: ObjectAnimator
+ @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
private lateinit var controller: StatusBarStateControllerImpl
private lateinit var uiEventLogger: UiEventLoggerFake
@@ -62,7 +64,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
controller = object : StatusBarStateControllerImpl(
uiEventLogger,
mock(DumpManager::class.java),
- interactionJankMonitor
+ interactionJankMonitor, shadeExpansionStateManager
) {
override fun createDarkAnimator(): ObjectAnimator { return mockDarkAnimator }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt
new file mode 100644
index 000000000000..62b4e7b79f5e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.connectivity
+
+import android.content.res.Resources
+import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
+
+typealias CarrierId = Int
+
+typealias NetworkType = String
+
+typealias ResId = Int
+
+class MobileIconCarrierIdOverridesFake : MobileIconCarrierIdOverrides {
+ /** Backing for [carrierIdEntryExists] */
+ var overriddenIds = mutableSetOf<Int>()
+
+ /** Backing for [getOverrideFor]. Map should be Map< CarrierId < NetworkType, ResId>> */
+ var overridesByCarrierId = mutableMapOf<CarrierId, Map<NetworkType, ResId>>()
+
+ override fun getOverrideFor(
+ carrierId: CarrierId,
+ networkType: NetworkType,
+ resources: Resources
+ ): ResId {
+ if (!overriddenIds.contains(carrierId)) return 0
+
+ return overridesByCarrierId[carrierId]?.get(networkType) ?: 0
+ }
+
+ override fun carrierIdEntryExists(carrierId: Int): Boolean {
+ return overriddenIds.contains(carrierId)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.java
deleted file mode 100644
index 7ddfde370afa..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.connectivity;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-
-import com.android.settingslib.mobile.TelephonyIcons;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class MobileStateTest extends SysuiTestCase {
-
- private final MobileState mState = new MobileState();
-
- @Before
- public void setUp() {
- }
-
- @Test
- public void testIsDataDisabledOrNotDefault_dataDisabled() {
- mState.iconGroup = TelephonyIcons.DATA_DISABLED;
- mState.userSetup = true;
-
- assertTrue(mState.isDataDisabledOrNotDefault());
- }
-
- @Test
- public void testIsDataDisabledOrNotDefault_notDefaultData() {
- mState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA;
- mState.userSetup = true;
-
- assertTrue(mState.isDataDisabledOrNotDefault());
- }
-
- @Test
- public void testIsDataDisabledOrNotDefault_notDisabled() {
- mState.iconGroup = TelephonyIcons.G;
- mState.userSetup = true;
-
- assertFalse(mState.isDataDisabledOrNotDefault());
- }
-
- @Test
- public void testHasActivityIn_noData_noActivity() {
- mState.dataConnected = false;
- mState.carrierNetworkChangeMode = false;
- mState.activityIn = false;
-
- assertFalse(mState.hasActivityIn());
- }
-
- @Test
- public void testHasActivityIn_noData_activityIn() {
- mState.dataConnected = false;
- mState.carrierNetworkChangeMode = false;
- mState.activityIn = true;
-
- assertFalse(mState.hasActivityIn());
- }
-
- @Test
- public void testHasActivityIn_dataConnected_activityIn() {
- mState.dataConnected = true;
- mState.carrierNetworkChangeMode = false;
- mState.activityIn = true;
-
- assertTrue(mState.hasActivityIn());
- }
-
- @Test
- public void testHasActivityIn_carrierNetworkChange() {
- mState.dataConnected = true;
- mState.carrierNetworkChangeMode = true;
- mState.activityIn = true;
-
- assertFalse(mState.hasActivityIn());
- }
-
- @Test
- public void testHasActivityOut_noData_noActivity() {
- mState.dataConnected = false;
- mState.carrierNetworkChangeMode = false;
- mState.activityOut = false;
-
- assertFalse(mState.hasActivityOut());
- }
-
- @Test
- public void testHasActivityOut_noData_activityOut() {
- mState.dataConnected = false;
- mState.carrierNetworkChangeMode = false;
- mState.activityOut = true;
-
- assertFalse(mState.hasActivityOut());
- }
-
- @Test
- public void testHasActivityOut_dataConnected_activityOut() {
- mState.dataConnected = true;
- mState.carrierNetworkChangeMode = false;
- mState.activityOut = true;
-
- assertTrue(mState.hasActivityOut());
- }
-
- @Test
- public void testHasActivityOut_carrierNetworkChange() {
- mState.dataConnected = true;
- mState.carrierNetworkChangeMode = true;
- mState.activityOut = true;
-
- assertFalse(mState.hasActivityOut());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt
new file mode 100644
index 000000000000..a226ded06111
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.connectivity
+
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import com.android.settingslib.mobile.TelephonyIcons
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class MobileStateTest : SysuiTestCase() {
+
+ private val state = MobileState()
+ @Before fun setUp() {}
+
+ @Test
+ fun testIsDataDisabledOrNotDefault_dataDisabled() {
+ state.iconGroup = TelephonyIcons.DATA_DISABLED
+ state.userSetup = true
+ assertTrue(state.isDataDisabledOrNotDefault)
+ }
+
+ @Test
+ fun testIsDataDisabledOrNotDefault_notDefaultData() {
+ state.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA
+ state.userSetup = true
+ assertTrue(state.isDataDisabledOrNotDefault)
+ }
+
+ @Test
+ fun testIsDataDisabledOrNotDefault_notDisabled() {
+ state.iconGroup = TelephonyIcons.G
+ state.userSetup = true
+ assertFalse(state.isDataDisabledOrNotDefault)
+ }
+
+ @Test
+ fun testHasActivityIn_noData_noActivity() {
+ state.dataConnected = false
+ state.carrierNetworkChangeMode = false
+ state.activityIn = false
+ assertFalse(state.hasActivityIn())
+ }
+
+ @Test
+ fun testHasActivityIn_noData_activityIn() {
+ state.dataConnected = false
+ state.carrierNetworkChangeMode = false
+ state.activityIn = true
+ assertFalse(state.hasActivityIn())
+ }
+
+ @Test
+ fun testHasActivityIn_dataConnected_activityIn() {
+ state.dataConnected = true
+ state.carrierNetworkChangeMode = false
+ state.activityIn = true
+ assertTrue(state.hasActivityIn())
+ }
+
+ @Test
+ fun testHasActivityIn_carrierNetworkChange() {
+ state.dataConnected = true
+ state.carrierNetworkChangeMode = true
+ state.activityIn = true
+ assertFalse(state.hasActivityIn())
+ }
+
+ @Test
+ fun testHasActivityOut_noData_noActivity() {
+ state.dataConnected = false
+ state.carrierNetworkChangeMode = false
+ state.activityOut = false
+ assertFalse(state.hasActivityOut())
+ }
+
+ @Test
+ fun testHasActivityOut_noData_activityOut() {
+ state.dataConnected = false
+ state.carrierNetworkChangeMode = false
+ state.activityOut = true
+ assertFalse(state.hasActivityOut())
+ }
+
+ @Test
+ fun testHasActivityOut_dataConnected_activityOut() {
+ state.dataConnected = true
+ state.carrierNetworkChangeMode = false
+ state.activityOut = true
+ assertTrue(state.hasActivityOut())
+ }
+
+ @Test
+ fun testHasActivityOut_carrierNetworkChange() {
+ state.dataConnected = true
+ state.carrierNetworkChangeMode = true
+ state.activityOut = true
+ assertFalse(state.hasActivityOut())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index 9c65fac1af45..9c870b5aa363 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -71,6 +71,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.log.LogBuffer;
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -125,6 +126,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
protected CarrierConfigTracker mCarrierConfigTracker;
protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
protected Handler mMainHandler;
+ // Use a real mobile mappings object since lots of tests rely on it
+ protected FakeMobileMappingsProxy mMobileMappingsProxy = new FakeMobileMappingsProxy();
protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
protected MobileSignalControllerFactory mMobileFactory;
@@ -219,10 +222,13 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
+ // Most of these tests rely on the actual MobileMappings behavior
+ mMobileMappingsProxy.setUseRealImpl(true);
mMobileFactory = new MobileSignalControllerFactory(
mContext,
mCallbackHandler,
- mCarrierConfigTracker
+ mCarrierConfigTracker,
+ mMobileMappingsProxy
);
mNetworkController = new NetworkControllerImpl(mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index 4bed4a19b3d9..1d112262765e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -18,12 +18,21 @@ package com.android.systemui.statusbar.connectivity;
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import static android.telephony.TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED;
+import static android.telephony.TelephonyManager.EXTRA_CARRIER_ID;
+import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;
+import static com.android.settingslib.mobile.TelephonyIcons.NR_5G_PLUS;
+
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.content.Intent;
import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.Looper;
@@ -35,6 +44,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import com.android.settingslib.SignalIcon.MobileIconGroup;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.dump.DumpManager;
@@ -45,6 +55,8 @@ import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashMap;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -329,6 +341,57 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
assertFalse(mNetworkController.isMobileDataNetworkInService());
}
+ @Test
+ public void mobileSignalController_getsCarrierId() {
+ when(mMockTm.getSimCarrierId()).thenReturn(1);
+ setupDefaultSignal();
+
+ assertEquals(1, mMobileSignalController.getState().getCarrierId());
+ }
+
+ @Test
+ public void mobileSignalController_updatesCarrierId_onChange() {
+ when(mMockTm.getSimCarrierId()).thenReturn(1);
+ setupDefaultSignal();
+
+ // Updates are sent down through this broadcast, we can send the intent directly
+ Intent intent = new Intent(ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
+ intent.putExtra(EXTRA_SUBSCRIPTION_ID, mSubId);
+ intent.putExtra(EXTRA_CARRIER_ID, 2);
+
+ mMobileSignalController.handleBroadcast(intent);
+
+ assertEquals(2, mMobileSignalController.getState().getCarrierId());
+ }
+
+ @Test
+ public void networkTypeIcon_hasCarrierIdOverride() {
+ int fakeCarrier = 1;
+ int fakeIconOverride = 12345;
+ int testDataNetType = 100;
+ String testDataString = "100";
+ HashMap<String, MobileIconGroup> testMap = new HashMap<>();
+ testMap.put(testDataString, NR_5G_PLUS);
+
+ // Pretend that there is an override for this icon, and this carrier ID
+ NetworkTypeResIdCache mockCache = mock(NetworkTypeResIdCache.class);
+ when(mockCache.get(eq(NR_5G_PLUS), eq(fakeCarrier), any())).thenReturn(fakeIconOverride);
+
+ // Turn off the default mobile mapping, so we can override
+ mMobileMappingsProxy.setUseRealImpl(false);
+ mMobileMappingsProxy.setIconMap(testMap);
+ // Use the mocked cache
+ mMobileSignalController.mCurrentState.setNetworkTypeResIdCache(mockCache);
+ // Rebuild the network map
+ mMobileSignalController.setConfiguration(mConfig);
+ when(mMockTm.getSimCarrierId()).thenReturn(fakeCarrier);
+
+ setupDefaultSignal();
+ updateDataConnectionState(TelephonyManager.DATA_CONNECTED, testDataNetType);
+
+ verifyDataIndicators(fakeIconOverride);
+ }
+
private void testDataActivity(int direction, boolean in, boolean out) {
updateDataActivity(direction);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt
new file mode 100644
index 000000000000..9e73487972e8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.connectivity
+
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import com.android.settingslib.SignalIcon.MobileIconGroup
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class NetworkTypeResIdCacheTest : SysuiTestCase() {
+ private lateinit var cache: NetworkTypeResIdCache
+ private var overrides = MobileIconCarrierIdOverridesFake()
+
+ @Before
+ fun setUp() {
+ cache = NetworkTypeResIdCache(overrides)
+ }
+
+ @Test
+ fun carrier1_noOverride_usesDefault() {
+ assertThat(cache.get(group1, CARRIER_1, context)).isEqualTo(iconDefault1)
+ }
+
+ @Test
+ fun carrier1_overridden_usesOverride() {
+ overrides.overriddenIds.add(CARRIER_1)
+ overrides.overridesByCarrierId[CARRIER_1] = mapOf(NET_TYPE_1 to iconOverride1)
+
+ assertThat(cache.get(group1, CARRIER_1, context)).isEqualTo(iconOverride1)
+ }
+
+ @Test
+ fun carrier1_override_carrier2UsesDefault() {
+ overrides.overriddenIds.add(CARRIER_1)
+ overrides.overridesByCarrierId[CARRIER_1] = mapOf(NET_TYPE_1 to iconOverride1)
+
+ assertThat(cache.get(group1, CARRIER_2, context)).isEqualTo(iconDefault1)
+ }
+
+ @Test
+ fun carrier1_overrideType1_type2UsesDefault() {
+ overrides.overriddenIds.add(CARRIER_1)
+ overrides.overridesByCarrierId[CARRIER_1] = mapOf(NET_TYPE_1 to iconOverride1)
+
+ assertThat(cache.get(group2, CARRIER_1, context)).isEqualTo(iconDefault2)
+ }
+
+ companion object {
+ // Simplified icon overrides here
+ const val CARRIER_1 = 1
+ const val CARRIER_2 = 2
+
+ const val NET_TYPE_1 = "one"
+ const val iconDefault1 = 123
+ const val iconOverride1 = 321
+ val group1 = MobileIconGroup(NET_TYPE_1, /* dataContentDesc */ 0, iconDefault1)
+
+ const val NET_TYPE_2 = "two"
+ const val iconDefault2 = 234
+
+ val group2 = MobileIconGroup(NET_TYPE_2, /* dataContentDesc*/ 0, iconDefault2)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index b2dc842bc942..7117c233c8c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -41,6 +41,7 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
@@ -88,6 +89,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NotificationListener mListener;
+ @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
private NotificationEntry mEntry;
private TestableNotificationLogger mLogger;
@@ -118,6 +120,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
mVisibilityProvider,
mNotifPipeline,
mock(StatusBarStateControllerImpl.class),
+ mShadeExpansionStateManager,
mBarService,
mExpansionStateLogger
);
@@ -152,7 +155,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+ mLogger.onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -162,7 +165,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
// |mEntry| won't change visibility, so it shouldn't be reported again:
Mockito.reset(mBarService);
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+ mLogger.onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -174,7 +177,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
throws Exception {
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+ mLogger.onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
Mockito.reset(mBarService);
@@ -189,13 +192,13 @@ public class NotificationLoggerTest extends SysuiTestCase {
}
private void setStateAsleep() {
- mLogger.onPanelExpandedChanged(true);
+ mLogger.onShadeExpansionFullyChanged(true);
mLogger.onDozingChanged(true);
mLogger.onStateChanged(StatusBarState.KEYGUARD);
}
private void setStateAwake() {
- mLogger.onPanelExpandedChanged(false);
+ mLogger.onShadeExpansionFullyChanged(false);
mLogger.onDozingChanged(false);
mLogger.onStateChanged(StatusBarState.SHADE);
}
@@ -221,7 +224,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAwake();
// Now expand panel
- mLogger.onPanelExpandedChanged(true);
+ mLogger.onShadeExpansionFullyChanged(true);
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen);
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
@@ -263,6 +266,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateControllerImpl statusBarStateController,
+ ShadeExpansionStateManager shadeExpansionStateManager,
IStatusBarService barService,
ExpansionStateLogger expansionStateLogger) {
super(
@@ -272,6 +276,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
visibilityProvider,
notifPipeline,
statusBarStateController,
+ shadeExpansionStateManager,
expansionStateLogger,
mNotificationPanelLoggerFake
);
@@ -280,9 +285,5 @@ public class NotificationLoggerTest extends SysuiTestCase {
// Make this on the current thread so we can wait for it during tests.
mHandler = Handler.createAsync(Looper.myLooper());
}
-
- OnChildLocationsChangedListener getChildLocationsChangedListenerForTest() {
- return mNotificationLocationsChangedListener;
- }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 7478e4c1f878..ab4ae6a9216b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -56,6 +56,7 @@ import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -151,7 +152,8 @@ public class NotificationTestHelper {
mock(ConfigurationControllerImpl.class),
new Handler(mTestLooper.getLooper()),
mock(AccessibilityManagerWrapper.class),
- mock(UiEventLogger.class)
+ mock(UiEventLogger.class),
+ mock(ShadeExpansionStateManager.class)
);
mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
mHeadsUpManager.mHandler = new Handler(mTestLooper.getLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 40aec82f5a85..743e7d69cfc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -118,7 +118,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
@Test
fun resetViewStates_expansionChanging_notificationBecomesTransparent() {
- whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
+ whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = 0.25f,
expectedAlpha = 0.0f
@@ -127,7 +127,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
@Test
fun resetViewStates_expansionChangingWhileBouncerInTransit_viewBecomesTransparent() {
- whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(true)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = 0.85f,
expectedAlpha = 0.0f
@@ -136,7 +136,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
@Test
fun resetViewStates_expansionChanging_notificationAlphaUpdated() {
- whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
+ whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = 0.6f,
expectedAlpha = getContentAlpha(0.6f)
@@ -145,7 +145,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
@Test
fun resetViewStates_expansionChangingWhileBouncerInTransit_notificationAlphaUpdated() {
- whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(true)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = 0.95f,
expectedAlpha = aboutToShowBouncerProgress(0.95f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index b17747ad2d92..75a3b21724ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -147,24 +147,24 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
}
@Test
- public void onBiometricAuthenticated_whenFingerprintAndBiometricsDisallowed_showBouncer() {
+ public void onBiometricAuthenticated_fingerprintAndBiometricsDisallowed_showPrimaryBouncer() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */))
.thenReturn(false);
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
}
@Test
- public void onBiometricAuthenticated_whenFingerprint_nonStrongBioDisallowed_showBouncer() {
+ public void onBiometricAuthenticated_fingerprint_nonStrongBioDisallowed_showPrimaryBouncer() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */))
.thenReturn(false);
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, false /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
assertThat(mBiometricUnlockController.getBiometricType())
@@ -214,7 +214,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_UNLOCK_COLLAPSING);
@@ -223,7 +223,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
@Test
public void onBiometricAuthenticated_whenFingerprintOnBouncer_dismissBouncer() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
- when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
// the value of isStrongBiometric doesn't matter here since we only care about the returned
// value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
@@ -283,7 +283,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
}
@Test
- public void onBiometricAuthenticated_whenFace_andBypass_encrypted_showBouncer() {
+ public void onBiometricAuthenticated_whenFace_andBypass_encrypted_showPrimaryBouncer() {
reset(mUpdateMonitor);
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
@@ -294,7 +294,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FACE, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
}
@@ -330,7 +330,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FACE, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
anyBoolean(), anyFloat());
assertThat(mBiometricUnlockController.getMode())
@@ -340,7 +340,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
@Test
public void onBiometricAuthenticated_whenFaceOnBouncer_dismissBouncer() {
when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
- when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
// the value of isStrongBiometric doesn't matter here since we only care about the returned
// value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
@@ -360,7 +360,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean()))
.thenReturn(true);
- when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
// the value of isStrongBiometric doesn't matter here since we only care about the returned
// value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
@@ -389,23 +389,23 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
}
@Test
- public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() {
+ public void onUdfpsConsecutivelyFailedThreeTimes_showPrimaryBouncer() {
// GIVEN UDFPS is supported
when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
// WHEN udfps fails once - then don't show the bouncer yet
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
- verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
// WHEN udfps fails the second time - then don't show the bouncer yet
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
- verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
// WHEN udpfs fails the third time
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
// THEN show the bouncer
- verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 57fb976c03ec..bf31eb287579 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -41,6 +41,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
@@ -60,6 +61,8 @@ import org.mockito.stubbing.Answer;
import java.util.Optional;
+import dagger.Lazy;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@@ -84,6 +87,7 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@Mock private Vibrator mVibrator;
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock private SystemBarAttributesListener mSystemBarAttributesListener;
+ @Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@@ -115,7 +119,8 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
Optional.of(mVibrator),
new DisableFlagsLogger(),
DEFAULT_DISPLAY,
- mSystemBarAttributesListener);
+ mSystemBarAttributesListener,
+ mCameraLauncherLazy);
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
when(mRemoteInputQuickSettingsDisabler.adjustDisableFlags(anyInt()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 7ce3a67ce835..41912f51db56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -112,6 +112,7 @@ import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.brightness.BrightnessSliderController;
+import com.android.systemui.shade.CameraLauncher;
import com.android.systemui.shade.NotificationPanelView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
@@ -220,6 +221,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private StatusBarStateControllerImpl mStatusBarStateController;
+ @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private BatteryController mBatteryController;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private StatusBarNotificationPresenter mNotificationPresenter;
@@ -287,6 +289,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private DeviceStateManager mDeviceStateManager;
@Mock private WiredChargingRippleController mWiredChargingRippleController;
+ @Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
+ @Mock private CameraLauncher mCameraLauncher;
/**
* The process of registering/unregistering a predictive back callback requires a
* ViewRootImpl, which is present IRL, but may be missing during a Mockito unit test.
@@ -339,6 +343,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mVisibilityProvider,
mock(NotifPipeline.class),
mStatusBarStateController,
+ mShadeExpansionStateManager,
mExpansionStateLogger,
new NotificationPanelLoggerFake()
);
@@ -378,6 +383,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
+ when(mCameraLauncherLazy.get()).thenReturn(mCameraLauncher);
when(mStatusBarComponentFactory.create()).thenReturn(mCentralSurfacesComponent);
when(mCentralSurfacesComponent.getNotificationShadeWindowViewController()).thenReturn(
@@ -479,7 +485,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mActivityLaunchAnimator,
mJankMonitor,
mDeviceStateManager,
- mWiredChargingRippleController, mDreamManager) {
+ mWiredChargingRippleController,
+ mDreamManager,
+ mCameraLauncherLazy) {
@Override
protected ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
@@ -891,7 +899,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
- when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(true);
+ when(mCameraLauncher.isLaunchingAffordance()).thenReturn(true);
mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
}
@@ -927,7 +935,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
- when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(false);
+ when(mCameraLauncher.isLaunchingAffordance()).thenReturn(false);
mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
}
@@ -1025,7 +1033,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void collapseShade_callsAnimateCollapsePanels_whenExpanded() {
// GIVEN the shade is expanded
- mCentralSurfaces.setPanelExpanded(true);
+ mCentralSurfaces.onShadeExpansionFullyChanged(true);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
// WHEN collapseShade is called
@@ -1038,7 +1046,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void collapseShade_doesNotCallAnimateCollapsePanels_whenCollapsed() {
// GIVEN the shade is collapsed
- mCentralSurfaces.setPanelExpanded(false);
+ mCentralSurfaces.onShadeExpansionFullyChanged(false);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
// WHEN collapseShade is called
@@ -1051,7 +1059,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void collapseShadeForBugReport_callsAnimateCollapsePanels_whenFlagDisabled() {
// GIVEN the shade is expanded & flag enabled
- mCentralSurfaces.setPanelExpanded(true);
+ mCentralSurfaces.onShadeExpansionFullyChanged(true);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
mFeatureFlags.set(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, false);
@@ -1065,7 +1073,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void collapseShadeForBugReport_doesNotCallAnimateCollapsePanels_whenFlagEnabled() {
// GIVEN the shade is expanded & flag enabled
- mCentralSurfaces.setPanelExpanded(true);
+ mCentralSurfaces.onShadeExpansionFullyChanged(true);
mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
mFeatureFlags.set(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index e252401d328c..780e0c56a239 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -67,6 +68,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
@Mock private KeyguardBypassController mBypassController;
@Mock private ConfigurationControllerImpl mConfigurationController;
@Mock private AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+ @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private UiEventLogger mUiEventLogger;
private boolean mLivesPastNormalTime;
@@ -81,7 +83,8 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
ConfigurationController configurationController,
Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger
+ UiEventLogger uiEventLogger,
+ ShadeExpansionStateManager shadeExpansionStateManager
) {
super(
context,
@@ -93,7 +96,8 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
configurationController,
handler,
accessibilityManagerWrapper,
- uiEventLogger
+ uiEventLogger,
+ shadeExpansionStateManager
);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -125,7 +129,8 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
mConfigurationController,
mTestHandler,
mAccessibilityManagerWrapper,
- mUiEventLogger
+ mUiEventLogger,
+ mShadeExpansionStateManager
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index ab209d130b2f..d3b541899635 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -58,7 +58,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
+import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Assert;
@@ -86,7 +86,7 @@ public class KeyguardBouncerTest extends SysuiTestCase {
@Mock
private KeyguardHostViewController mKeyguardHostViewController;
@Mock
- private BouncerExpansionCallback mExpansionCallback;
+ private KeyguardBouncer.PrimaryBouncerExpansionCallback mExpansionCallback;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
@@ -476,7 +476,8 @@ public class KeyguardBouncerTest extends SysuiTestCase {
mBouncer.ensureView();
mBouncer.setExpansion(0.5f);
- final BouncerExpansionCallback callback = mock(BouncerExpansionCallback.class);
+ final PrimaryBouncerExpansionCallback callback =
+ mock(PrimaryBouncerExpansionCallback.class);
mBouncer.addBouncerExpansionCallback(callback);
mBouncer.setExpansion(EXPANSION_HIDDEN);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 696775a24074..de71e2c250c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.statusbar.phone.ScrimController.KEYGUARD_SCRIM_ALPHA;
import static com.android.systemui.statusbar.phone.ScrimController.OPAQUE;
import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT;
@@ -59,7 +58,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -119,7 +117,6 @@ public class ScrimControllerTest extends SysuiTestCase {
// TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
// event-dispatch-on-registration pattern caused some of these unit tests to fail.)
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- @Mock private KeyguardViewMediator mKeyguardViewMediator;
private static class AnimatorListener implements Animator.AnimatorListener {
private int mNumStarts;
@@ -233,8 +230,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
- mStatusBarKeyguardViewManager,
- mKeyguardViewMediator);
+ mStatusBarKeyguardViewManager);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
@@ -243,8 +239,6 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController.setWallpaperSupportsAmbientMode(false);
mScrimController.transitionTo(ScrimState.KEYGUARD);
finishAnimationsImmediately();
-
- mScrimController.setLaunchingAffordanceWithPreview(false);
}
@After
@@ -858,8 +852,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
- mStatusBarKeyguardViewManager,
- mKeyguardViewMediator);
+ mStatusBarKeyguardViewManager);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
@@ -1308,7 +1301,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void qsExpansion_BehindTint_shadeLocked_bouncerActive_usesBouncerProgress() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
// clipping doesn't change tested logic but allows to assert scrims more in line with
// their expected large screen behaviour
mScrimController.setClipsQsScrim(false);
@@ -1324,7 +1317,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void expansionNotificationAlpha_shadeLocked_bouncerActive_usesBouncerInterpolator() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
mScrimController.transitionTo(SHADE_LOCKED);
@@ -1340,7 +1333,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void expansionNotificationAlpha_shadeLocked_bouncerNotActive_usesShadeInterpolator() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
mScrimController.transitionTo(SHADE_LOCKED);
@@ -1355,7 +1348,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void notificationAlpha_unnocclusionAnimating_bouncerActive_usesKeyguardNotifAlpha() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
mScrimController.setClipsQsScrim(true);
mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -1377,7 +1370,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void notificationAlpha_unnocclusionAnimating_bouncerNotActive_usesKeyguardNotifAlpha() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.setUnocclusionAnimationRunning(true);
@@ -1398,7 +1391,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void notificationAlpha_inKeyguardState_bouncerActive_usesInvertedBouncerInterpolator() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
mScrimController.setClipsQsScrim(true);
mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -1418,7 +1411,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void notificationAlpha_inKeyguardState_bouncerNotActive_usesInvertedShadeInterpolator() {
- when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
mScrimController.setClipsQsScrim(true);
mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -1437,6 +1430,17 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
+ public void behindTint_inKeyguardState_bouncerNotActive_usesKeyguardBehindTint() {
+ when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
+ mScrimController.setClipsQsScrim(false);
+
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ finishAnimationsImmediately();
+ assertThat(mScrimBehind.getTint())
+ .isEqualTo(ScrimState.KEYGUARD.getBehindTint());
+ }
+
+ @Test
public void testNotificationTransparency_followsTransitionToFullShade() {
mScrimController.transitionTo(SHADE_LOCKED);
mScrimController.setRawPanelExpansionFraction(1.0f);
@@ -1627,30 +1631,6 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimAlpha(mScrimBehind, 0);
}
- @Test
- public void keyguardAlpha_whenUnlockedForOcclusion_ifPlayingOcclusionAnimation() {
- mScrimController.transitionTo(ScrimState.KEYGUARD);
-
- when(mKeyguardViewMediator.isOccludeAnimationPlaying()).thenReturn(true);
-
- mScrimController.transitionTo(ScrimState.UNLOCKED);
- finishAnimationsImmediately();
-
- assertScrimAlpha(mNotificationsScrim, (int) (KEYGUARD_SCRIM_ALPHA * 255f));
- }
-
- @Test
- public void keyguardAlpha_whenUnlockedForLaunch_ifLaunchingAffordance() {
- mScrimController.transitionTo(ScrimState.KEYGUARD);
- when(mKeyguardViewMediator.isOccludeAnimationPlaying()).thenReturn(true);
- mScrimController.setLaunchingAffordanceWithPreview(true);
-
- mScrimController.transitionTo(ScrimState.UNLOCKED);
- finishAnimationsImmediately();
-
- assertScrimAlpha(mNotificationsScrim, (int) (KEYGUARD_SCRIM_ALPHA * 255f));
- }
-
private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
mScrimController.setRawPanelExpansionFraction(expansion);
finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index ec8d71136452..9f70565749df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -56,8 +56,8 @@ import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.data.BouncerView;
import com.android.systemui.keyguard.data.BouncerViewDelegate;
-import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
-import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -105,8 +105,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock private KeyguardBouncer.Factory mKeyguardBouncerFactory;
@Mock private KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
@Mock private KeyguardMessageAreaController mKeyguardMessageAreaController;
- @Mock private KeyguardBouncer mBouncer;
- @Mock private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor;
+ @Mock private KeyguardBouncer mPrimaryBouncer;
+ @Mock private StatusBarKeyguardViewManager.AlternateBouncer mAlternateBouncer;
@Mock private KeyguardMessageArea mKeyguardMessageArea;
@Mock private ShadeController mShadeController;
@Mock private SysUIUnfoldComponent mSysUiUnfoldComponent;
@@ -114,13 +114,13 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock private LatencyTracker mLatencyTracker;
@Mock private FeatureFlags mFeatureFlags;
@Mock private KeyguardSecurityModel mKeyguardSecurityModel;
- @Mock private BouncerCallbackInteractor mBouncerCallbackInteractor;
- @Mock private BouncerInteractor mBouncerInteractor;
+ @Mock private PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
+ @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock private BouncerView mBouncerView;
@Mock private BouncerViewDelegate mBouncerViewDelegate;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
+ private KeyguardBouncer.PrimaryBouncerExpansionCallback mBouncerExpansionCallback;
private FakeKeyguardStateController mKeyguardStateController =
spy(new FakeKeyguardStateController());
@@ -134,8 +134,9 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mKeyguardBouncerFactory.create(
- any(ViewGroup.class), any(KeyguardBouncer.BouncerExpansionCallback.class)))
- .thenReturn(mBouncer);
+ any(ViewGroup.class),
+ any(KeyguardBouncer.PrimaryBouncerExpansionCallback.class)))
+ .thenReturn(mPrimaryBouncer);
when(mCentralSurfaces.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
@@ -163,8 +164,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mLatencyTracker,
mKeyguardSecurityModel,
mFeatureFlags,
- mBouncerCallbackInteractor,
- mBouncerInteractor,
+ mPrimaryBouncerCallbackInteractor,
+ mPrimaryBouncerInteractor,
mBouncerView) {
@Override
public ViewRootImpl getViewRootImpl() {
@@ -181,8 +182,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mNotificationContainer,
mBypassController);
mStatusBarKeyguardViewManager.show(null);
- ArgumentCaptor<KeyguardBouncer.BouncerExpansionCallback> callbackArgumentCaptor =
- ArgumentCaptor.forClass(KeyguardBouncer.BouncerExpansionCallback.class);
+ ArgumentCaptor<KeyguardBouncer.PrimaryBouncerExpansionCallback> callbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardBouncer.PrimaryBouncerExpansionCallback.class);
verify(mKeyguardBouncerFactory).create(any(ViewGroup.class),
callbackArgumentCaptor.capture());
mBouncerExpansionCallback = callbackArgumentCaptor.getValue();
@@ -194,86 +195,86 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
Runnable cancelAction = () -> {};
mStatusBarKeyguardViewManager.dismissWithAction(
action, cancelAction, false /* afterKeyguardGone */);
- verify(mBouncer).showWithDismissAction(eq(action), eq(cancelAction));
+ verify(mPrimaryBouncer).showWithDismissAction(eq(action), eq(cancelAction));
}
@Test
public void showBouncer_onlyWhenShowing() {
mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
- verify(mBouncer, never()).show(anyBoolean(), anyBoolean());
- verify(mBouncer, never()).show(anyBoolean());
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
+ verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean());
+ verify(mPrimaryBouncer, never()).show(anyBoolean());
}
@Test
public void showBouncer_notWhenBouncerAlreadyShowing() {
mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
- when(mBouncer.isSecure()).thenReturn(true);
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
- verify(mBouncer, never()).show(anyBoolean(), anyBoolean());
- verify(mBouncer, never()).show(anyBoolean());
+ when(mPrimaryBouncer.isSecure()).thenReturn(true);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
+ verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean());
+ verify(mPrimaryBouncer, never()).show(anyBoolean());
}
@Test
public void showBouncer_showsTheBouncer() {
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
- verify(mBouncer).show(anyBoolean(), eq(true));
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
+ verify(mPrimaryBouncer).show(anyBoolean(), eq(true));
}
@Test
- public void onPanelExpansionChanged_neverHidesScrimmedBouncer() {
- when(mBouncer.isShowing()).thenReturn(true);
- when(mBouncer.isScrimmed()).thenReturn(true);
+ public void onPanelExpansionChanged_neverHidesFullscreenBouncer() {
+ when(mPrimaryBouncer.isShowing()).thenReturn(true);
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.SimPuk);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE));
+ verify(mPrimaryBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE));
+
+ reset(mPrimaryBouncer);
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.SimPin);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
+ verify(mPrimaryBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE));
}
@Test
public void onPanelExpansionChanged_neverShowsDuringHintAnimation() {
when(mNotificationPanelView.isUnlockHintRunning()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mPrimaryBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
}
@Test
public void onPanelExpansionChanged_propagatesToBouncer() {
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(0.5f));
+ verify(mPrimaryBouncer).setExpansion(eq(0.5f));
}
@Test
public void onPanelExpansionChanged_hideBouncer_afterKeyguardHidden() {
mStatusBarKeyguardViewManager.hide(0, 0);
- when(mBouncer.inTransit()).thenReturn(true);
+ when(mPrimaryBouncer.inTransit()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mPrimaryBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
}
@Test
public void onPanelExpansionChanged_showsBouncerWhenSwiping() {
mKeyguardStateController.setCanDismissLockScreen(false);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).show(eq(false), eq(false));
+ verify(mPrimaryBouncer).show(eq(false), eq(false));
// But not when it's already visible
- reset(mBouncer);
- when(mBouncer.isShowing()).thenReturn(true);
+ reset(mPrimaryBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer, never()).show(eq(false), eq(false));
+ verify(mPrimaryBouncer, never()).show(eq(false), eq(false));
// Or animating away
- reset(mBouncer);
- when(mBouncer.isAnimatingAway()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer, never()).show(eq(false), eq(false));
- }
-
- @Test
- public void onPanelExpansionChanged_neverTranslatesBouncerWhenOccluded() {
- mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animate */);
+ reset(mPrimaryBouncer);
+ when(mPrimaryBouncer.isAnimatingAway()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer, never()).setExpansion(eq(0.5f));
+ verify(mPrimaryBouncer, never()).show(eq(false), eq(false));
}
@Test
@@ -285,7 +286,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
- verify(mBouncer, never()).setExpansion(anyFloat());
+ verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
}
@Test
@@ -302,7 +303,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
- verify(mBouncer, never()).setExpansion(anyFloat());
+ verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
}
@Test
@@ -313,7 +314,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
/* expanded= */ true,
/* tracking= */ false));
- verify(mBouncer, never()).setExpansion(anyFloat());
+ verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
}
@Test
@@ -321,7 +322,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
verify(mCentralSurfaces).animateKeyguardUnoccluding();
- when(mBouncer.isShowing()).thenReturn(true);
+ when(mPrimaryBouncer.isShowing()).thenReturn(true);
clearInvocations(mCentralSurfaces);
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
verify(mCentralSurfaces, never()).animateKeyguardUnoccluding();
@@ -372,7 +373,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager.dismissWithAction(
action, cancelAction, true /* afterKeyguardGone */);
- when(mBouncer.isShowing()).thenReturn(false);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
mStatusBarKeyguardViewManager.hideBouncer(true);
mStatusBarKeyguardViewManager.hide(0, 30);
verify(action, never()).onDismiss();
@@ -386,7 +387,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager.dismissWithAction(
action, cancelAction, true /* afterKeyguardGone */);
- when(mBouncer.isShowing()).thenReturn(false);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
mStatusBarKeyguardViewManager.hideBouncer(true);
verify(action, never()).onDismiss();
@@ -407,9 +408,9 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void testShowing_whenAlternateAuthShowing() {
- mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- when(mBouncer.isShowing()).thenReturn(false);
- when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
+ mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
+ when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true);
assertTrue(
"Is showing not accurate when alternative auth showing",
mStatusBarKeyguardViewManager.isBouncerShowing());
@@ -417,93 +418,93 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void testWillBeShowing_whenAlternateAuthShowing() {
- mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- when(mBouncer.isShowing()).thenReturn(false);
- when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
+ mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
+ when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true);
assertTrue(
"Is or will be showing not accurate when alternative auth showing",
- mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing());
+ mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing());
}
@Test
- public void testHideAltAuth_onShowBouncer() {
+ public void testHideAlternateBouncer_onShowBouncer() {
// GIVEN alt auth is showing
- mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- when(mBouncer.isShowing()).thenReturn(false);
- when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
- reset(mAlternateAuthInterceptor);
+ mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
+ when(mAlternateBouncer.isShowingAlternateBouncer()).thenReturn(true);
+ reset(mAlternateBouncer);
// WHEN showBouncer is called
- mStatusBarKeyguardViewManager.showBouncer(true);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true);
// THEN alt bouncer should be hidden
- verify(mAlternateAuthInterceptor).hideAlternateAuthBouncer();
+ verify(mAlternateBouncer).hideAlternateBouncer();
}
@Test
public void testBouncerIsOrWillBeShowing_whenBouncerIsInTransit() {
- when(mBouncer.isShowing()).thenReturn(false);
- when(mBouncer.inTransit()).thenReturn(true);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
+ when(mPrimaryBouncer.inTransit()).thenReturn(true);
assertTrue(
"Is or will be showing should be true when bouncer is in transit",
- mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing());
+ mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing());
}
@Test
public void testShowAltAuth_unlockingWithBiometricNotAllowed() {
// GIVEN alt auth exists, unlocking with biometric isn't allowed
- mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- when(mBouncer.isShowing()).thenReturn(false);
+ mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
.thenReturn(false);
// WHEN showGenericBouncer is called
final boolean scrimmed = true;
- mStatusBarKeyguardViewManager.showGenericBouncer(scrimmed);
+ mStatusBarKeyguardViewManager.showBouncer(scrimmed);
// THEN regular bouncer is shown
- verify(mBouncer).show(anyBoolean(), eq(scrimmed));
- verify(mAlternateAuthInterceptor, never()).showAlternateAuthBouncer();
+ verify(mPrimaryBouncer).show(anyBoolean(), eq(scrimmed));
+ verify(mAlternateBouncer, never()).showAlternateBouncer();
}
@Test
- public void testShowAltAuth_unlockingWithBiometricAllowed() {
+ public void testShowAlternateBouncer_unlockingWithBiometricAllowed() {
// GIVEN alt auth exists, unlocking with biometric is allowed
- mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- when(mBouncer.isShowing()).thenReturn(false);
+ mStatusBarKeyguardViewManager.setAlternateBouncer(mAlternateBouncer);
+ when(mPrimaryBouncer.isShowing()).thenReturn(false);
when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
// WHEN showGenericBouncer is called
- mStatusBarKeyguardViewManager.showGenericBouncer(true);
+ mStatusBarKeyguardViewManager.showBouncer(true);
// THEN alt auth bouncer is shown
- verify(mAlternateAuthInterceptor).showAlternateAuthBouncer();
- verify(mBouncer, never()).show(anyBoolean(), anyBoolean());
+ verify(mAlternateBouncer).showAlternateBouncer();
+ verify(mPrimaryBouncer, never()).show(anyBoolean(), anyBoolean());
}
@Test
public void testUpdateResources_delegatesToBouncer() {
mStatusBarKeyguardViewManager.updateResources();
- verify(mBouncer).updateResources();
+ verify(mPrimaryBouncer).updateResources();
}
@Test
public void updateKeyguardPosition_delegatesToBouncer() {
mStatusBarKeyguardViewManager.updateKeyguardPosition(1.0f);
- verify(mBouncer).updateKeyguardPosition(1.0f);
+ verify(mPrimaryBouncer).updateKeyguardPosition(1.0f);
}
@Test
public void testIsBouncerInTransit() {
- when(mBouncer.inTransit()).thenReturn(true);
- Truth.assertThat(mStatusBarKeyguardViewManager.isBouncerInTransit()).isTrue();
- when(mBouncer.inTransit()).thenReturn(false);
- Truth.assertThat(mStatusBarKeyguardViewManager.isBouncerInTransit()).isFalse();
- mBouncer = null;
- Truth.assertThat(mStatusBarKeyguardViewManager.isBouncerInTransit()).isFalse();
+ when(mPrimaryBouncer.inTransit()).thenReturn(true);
+ Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isTrue();
+ when(mPrimaryBouncer.inTransit()).thenReturn(false);
+ Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isFalse();
+ mPrimaryBouncer = null;
+ Truth.assertThat(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).isFalse();
}
private static ShadeExpansionChangeEvent expansionEvent(
@@ -534,7 +535,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
mOnBackInvokedCallback.capture());
- when(mBouncer.isShowing()).thenReturn(true);
+ when(mPrimaryBouncer.isShowing()).thenReturn(true);
when(mCentralSurfaces.shouldKeyguardHideImmediately()).thenReturn(true);
/* invoke the back callback directly */
mOnBackInvokedCallback.getValue().onBackInvoked();
@@ -567,6 +568,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
public void flag_off_DoesNotCallBouncerInteractor() {
when(mFeatureFlags.isEnabled(MODERN_BOUNCER)).thenReturn(false);
mStatusBarKeyguardViewManager.hideBouncer(false);
- verify(mBouncerInteractor, never()).hide();
+ verify(mPrimaryBouncerInteractor, never()).hide();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index c3a7e65f4697..613238f7752e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -99,6 +99,6 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
mRemoteInputCallback.onLockedRemoteInput(
mock(ExpandableNotificationRow.class), mock(View.class));
- verify(mStatusBarKeyguardViewManager).showGenericBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showBouncer(true);
}
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
index 6d8d902615de..a052008d4832 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
@@ -16,31 +16,59 @@
package com.android.systemui.statusbar.pipeline.mobile.util
+import android.telephony.TelephonyDisplayInfo
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings.Config
import com.android.settingslib.mobile.TelephonyIcons
class FakeMobileMappingsProxy : MobileMappingsProxy {
+ // The old [NetworkControllerDataTest] infra requires us to be able to use the real
+ // impl sometimes
+ var useRealImpl = false
+
+ private var realImpl = MobileMappingsProxyImpl()
private var iconMap = mapOf<String, MobileIconGroup>()
private var defaultIcons = TelephonyIcons.THREE_G
fun setIconMap(map: Map<String, MobileIconGroup>) {
iconMap = map
}
- override fun mapIconSets(config: Config): Map<String, MobileIconGroup> = iconMap
+ override fun mapIconSets(config: Config): Map<String, MobileIconGroup> {
+ if (useRealImpl) {
+ return realImpl.mapIconSets(config)
+ }
+ return iconMap
+ }
fun getIconMap() = iconMap
fun setDefaultIcons(group: MobileIconGroup) {
defaultIcons = group
}
- override fun getDefaultIcons(config: Config): MobileIconGroup = defaultIcons
+ override fun getDefaultIcons(config: Config): MobileIconGroup {
+ if (useRealImpl) {
+ return realImpl.getDefaultIcons(config)
+ }
+ return defaultIcons
+ }
+
+ /** This is only used in the old pipeline, use the real impl always */
+ override fun getIconKey(displayInfo: TelephonyDisplayInfo): String {
+ return realImpl.getIconKey(displayInfo)
+ }
+
fun getDefaultIcons(): MobileIconGroup = defaultIcons
override fun toIconKey(networkType: Int): String {
+ if (useRealImpl) {
+ return realImpl.toIconKeyOverride(networkType)
+ }
return networkType.toString()
}
override fun toIconKeyOverride(networkType: Int): String {
+ if (useRealImpl) {
+ return realImpl.toIconKeyOverride(networkType)
+ }
return toIconKey(networkType) + "_override"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index 43d0fe9559b6..1eee08c22187 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -221,4 +221,33 @@ public class BatteryControllerTest extends SysuiTestCase {
Assert.assertFalse(mBatteryController.isChargingSourceDock());
}
+
+ @Test
+ public void batteryStateChanged_healthNotOverheated_outputsFalse() {
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_GOOD);
+
+ mBatteryController.onReceive(getContext(), intent);
+
+ Assert.assertFalse(mBatteryController.isOverheated());
+ }
+
+ @Test
+ public void batteryStateChanged_healthOverheated_outputsTrue() {
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_OVERHEAT);
+
+ mBatteryController.onReceive(getContext(), intent);
+
+ Assert.assertTrue(mBatteryController.isOverheated());
+ }
+
+ @Test
+ public void batteryStateChanged_noHealthGiven_outputsFalse() {
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+
+ mBatteryController.onReceive(getContext(), intent);
+
+ Assert.assertFalse(mBatteryController.isOverheated());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/MultiRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
index 05512e5bf1ce..0d19ab1db390 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ripple/MultiRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.graphics.Color
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.ripple.MultiRippleController.Companion.MAX_RIPPLE_NUMBER
+import com.android.systemui.surfaceeffects.ripple.MultiRippleController.Companion.MAX_RIPPLE_NUMBER
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleViewTest.kt
new file mode 100644
index 000000000000..2024d53b0212
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleViewTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.surfaceeffects.ripple
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class MultiRippleViewTest : SysuiTestCase() {
+ private val fakeSystemClock = FakeSystemClock()
+ // FakeExecutor is needed to run animator.
+ private val fakeExecutor = FakeExecutor(fakeSystemClock)
+
+ @Test
+ fun onRippleFinishes_triggersRippleFinished() {
+ val multiRippleView = MultiRippleView(context, null)
+ val multiRippleController = MultiRippleController(multiRippleView)
+ val rippleAnimationConfig = RippleAnimationConfig(duration = 1000L)
+
+ var isTriggered = false
+ val listener =
+ object : MultiRippleView.Companion.RipplesFinishedListener {
+ override fun onRipplesFinish() {
+ isTriggered = true
+ }
+ }
+ multiRippleView.addRipplesFinishedListener(listener)
+
+ fakeExecutor.execute {
+ val rippleAnimation = RippleAnimation(rippleAnimationConfig)
+ multiRippleController.play(rippleAnimation)
+
+ fakeSystemClock.advanceTime(rippleAnimationConfig.duration)
+
+ assertThat(isTriggered).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleAnimationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
index 7662282a04f4..756397a30e43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleAnimationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.graphics.Color
import android.testing.AndroidTestingRunner
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
index 2d2f4cc9edd2..1e5ab7e25599 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -21,12 +21,10 @@ import com.android.systemui.SysuiTestCase
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
@SmallTest
@RunWith(AndroidTestingRunner::class)
class RippleViewTest : SysuiTestCase() {
- @Mock
private lateinit var rippleView: RippleView
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
new file mode 100644
index 000000000000..d25c8c1a5899
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.surfaceeffects.turbulencenoise
+
+import android.graphics.Color
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TurbulenceNoiseControllerTest : SysuiTestCase() {
+ private val fakeSystemClock = FakeSystemClock()
+ // FakeExecutor is needed to run animator.
+ private val fakeExecutor = FakeExecutor(fakeSystemClock)
+
+ @Test
+ fun play_playsTurbulenceNoise() {
+ val config = TurbulenceNoiseAnimationConfig(duration = 1000f)
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+
+ val turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView)
+
+ fakeExecutor.execute {
+ turbulenceNoiseController.play(config)
+
+ assertThat(turbulenceNoiseView.isPlaying).isTrue()
+
+ fakeSystemClock.advanceTime(config.duration.toLong())
+
+ assertThat(turbulenceNoiseView.isPlaying).isFalse()
+ }
+ }
+
+ @Test
+ fun updateColor_updatesCorrectColor() {
+ val config = TurbulenceNoiseAnimationConfig(duration = 1000f, color = Color.WHITE)
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+ val expectedColor = Color.RED
+
+ val turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView)
+
+ fakeExecutor.execute {
+ turbulenceNoiseController.play(config)
+
+ turbulenceNoiseView.updateColor(expectedColor)
+
+ fakeSystemClock.advanceTime(config.duration.toLong())
+
+ assertThat(config.color).isEqualTo(expectedColor)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt
new file mode 100644
index 000000000000..633aac076502
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.surfaceeffects.turbulencenoise
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TurbulenceNoiseViewTest : SysuiTestCase() {
+
+ private val fakeSystemClock = FakeSystemClock()
+ // FakeExecutor is needed to run animator.
+ private val fakeExecutor = FakeExecutor(fakeSystemClock)
+
+ @Test
+ fun play_viewHasCorrectVisibility() {
+ val config = TurbulenceNoiseAnimationConfig(duration = 1000f)
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+
+ fakeExecutor.execute {
+ turbulenceNoiseView.play(config)
+
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.VISIBLE)
+
+ fakeSystemClock.advanceTime(config.duration.toLong())
+
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+ }
+ }
+
+ @Test
+ fun play_playsAnimation() {
+ val config = TurbulenceNoiseAnimationConfig(duration = 1000f)
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+
+ fakeExecutor.execute {
+ turbulenceNoiseView.play(config)
+
+ assertThat(turbulenceNoiseView.isPlaying).isTrue()
+ }
+ }
+
+ @Test
+ fun play_onEnd_triggersOnAnimationEnd() {
+ var animationEnd = false
+ val config =
+ TurbulenceNoiseAnimationConfig(
+ duration = 1000f,
+ onAnimationEnd = { animationEnd = true }
+ )
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+
+ fakeExecutor.execute {
+ turbulenceNoiseView.play(config)
+
+ assertThat(turbulenceNoiseView.isPlaying).isTrue()
+
+ fakeSystemClock.advanceTime(config.duration.toLong())
+
+ assertThat(animationEnd).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 9dea48e3b47c..09f0d4a10410 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -119,27 +119,27 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
)
)
- verify(logger).logViewAddition("Fake Window Title")
+ verify(logger).logViewAddition("id", "Fake Window Title")
}
@Test
- fun displayView_screenOff_wakeLockAcquired() {
+ fun displayView_wakeLockAcquired() {
underTest.displayView(getState())
assertThat(fakeWakeLock.isHeld).isTrue()
}
@Test
- fun displayView_screenAlreadyOn_wakeLockNotAcquired() {
+ fun displayView_screenAlreadyOn_wakeLockAcquired() {
whenever(powerManager.isScreenOn).thenReturn(true)
underTest.displayView(getState())
- assertThat(fakeWakeLock.isHeld).isFalse()
+ assertThat(fakeWakeLock.isHeld).isTrue()
}
@Test
- fun displayView_screenOff_wakeLockCanBeReleasedAfterTimeOut() {
+ fun displayView_wakeLockCanBeReleasedAfterTimeOut() {
underTest.displayView(getState())
assertThat(fakeWakeLock.isHeld).isTrue()
@@ -149,6 +149,16 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
}
@Test
+ fun displayView_removeView_wakeLockCanBeReleased() {
+ underTest.displayView(getState())
+ assertThat(fakeWakeLock.isHeld).isTrue()
+
+ underTest.removeView("id", "test reason")
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
fun displayView_twice_viewNotAddedTwice() {
underTest.displayView(getState())
reset(windowManager)
@@ -253,21 +263,143 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
}
@Test
+ fun multipleViewsWithDifferentIds_recentActiveViewIsDisplayed() {
+ underTest.displayView(ViewInfo("First name", id = "id1"))
+
+ verify(windowManager).addView(any(), any())
+
+ reset(windowManager)
+ underTest.displayView(ViewInfo("Second name", id = "id2"))
+ underTest.removeView("id2", "test reason")
+
+ verify(windowManager).removeView(any())
+
+ fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+ assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
+
+ reset(windowManager)
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ verify(windowManager).removeView(any())
+ assertThat(underTest.activeViews.size).isEqualTo(0)
+ }
+
+ @Test
+ fun multipleViewsWithDifferentIds_oldViewRemoved_recentViewIsDisplayed() {
+ underTest.displayView(ViewInfo("First name", id = "id1"))
+
+ verify(windowManager).addView(any(), any())
+
+ reset(windowManager)
+ underTest.displayView(ViewInfo("Second name", id = "id2"))
+ underTest.removeView("id1", "test reason")
+
+ verify(windowManager, never()).removeView(any())
+ assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
+
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ verify(windowManager).removeView(any())
+ assertThat(underTest.activeViews.size).isEqualTo(0)
+ }
+
+ @Test
+ fun multipleViewsWithDifferentIds_threeDifferentViews_recentActiveViewIsDisplayed() {
+ underTest.displayView(ViewInfo("First name", id = "id1"))
+ underTest.displayView(ViewInfo("Second name", id = "id2"))
+ underTest.displayView(ViewInfo("Third name", id = "id3"))
+
+ verify(windowManager).addView(any(), any())
+
+ reset(windowManager)
+ underTest.removeView("id3", "test reason")
+
+ verify(windowManager).removeView(any())
+
+ fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+ assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
+
+ reset(windowManager)
+ underTest.removeView("id2", "test reason")
+
+ verify(windowManager).removeView(any())
+
+ fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+ assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
+
+ reset(windowManager)
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ verify(windowManager).removeView(any())
+ assertThat(underTest.activeViews.size).isEqualTo(0)
+ }
+
+ @Test
+ fun multipleViewsWithDifferentIds_oneViewStateChanged_stackHasRecentState() {
+ underTest.displayView(ViewInfo("First name", id = "id1"))
+ underTest.displayView(ViewInfo("New name", id = "id1"))
+
+ verify(windowManager).addView(any(), any())
+
+ reset(windowManager)
+ underTest.displayView(ViewInfo("Second name", id = "id2"))
+ underTest.removeView("id2", "test reason")
+
+ verify(windowManager).removeView(any())
+
+ fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+ assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("New name")
+ assertThat(underTest.activeViews[0].second.name).isEqualTo("New name")
+
+ reset(windowManager)
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ verify(windowManager).removeView(any())
+ assertThat(underTest.activeViews.size).isEqualTo(0)
+ }
+
+ @Test
+ fun multipleViewsWithDifferentIds_viewsTimeouts_noViewLeftToDisplay() {
+ underTest.displayView(ViewInfo("First name", id = "id1"))
+ fakeClock.advanceTime(TIMEOUT_MS / 3)
+ underTest.displayView(ViewInfo("Second name", id = "id2"))
+ fakeClock.advanceTime(TIMEOUT_MS / 3)
+ underTest.displayView(ViewInfo("Third name", id = "id3"))
+
+ reset(windowManager)
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ verify(windowManager).removeView(any())
+ verify(windowManager, never()).addView(any(), any())
+ assertThat(underTest.activeViews.size).isEqualTo(0)
+ }
+
+ @Test
fun removeView_viewRemovedAndRemovalLogged() {
// First, add the view
underTest.displayView(getState())
// Then, remove it
val reason = "test reason"
- underTest.removeView(reason)
+ val deviceId = "id"
+ underTest.removeView(deviceId, reason)
verify(windowManager).removeView(any())
- verify(logger).logViewRemoval(reason)
+ verify(logger).logViewRemoval(deviceId, reason)
}
@Test
fun removeView_noAdd_viewNotRemoved() {
- underTest.removeView("reason")
+ underTest.removeView("id", "reason")
verify(windowManager, never()).removeView(any())
}
@@ -319,7 +451,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
val name: String,
override val windowTitle: String = "Window Title",
override val wakeReason: String = "WAKE_REASON",
- override val timeoutMs: Int = 1
+ override val timeoutMs: Int = 1,
+ override val id: String = "id",
) : TemporaryViewInfo()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
index d155050ce932..116b8fe62b37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
@@ -44,7 +44,7 @@ class TemporaryViewLoggerTest : SysuiTestCase() {
@Test
fun logViewAddition_bufferHasLog() {
- logger.logViewAddition("Test Window Title")
+ logger.logViewAddition("test id", "Test Window Title")
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
@@ -57,7 +57,8 @@ class TemporaryViewLoggerTest : SysuiTestCase() {
@Test
fun logViewRemoval_bufferHasTagAndReason() {
val reason = "test reason"
- logger.logViewRemoval(reason)
+ val deviceId = "test id"
+ logger.logViewRemoval(deviceId, reason)
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
@@ -65,6 +66,7 @@ class TemporaryViewLoggerTest : SysuiTestCase() {
assertThat(actualString).contains(TAG)
assertThat(actualString).contains(reason)
+ assertThat(actualString).contains(deviceId)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 8e37aa292240..47c84ab48093 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -377,6 +377,7 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
windowTitle = WINDOW_TITLE,
wakeReason = WAKE_REASON,
timeoutMs = TIMEOUT,
+ id = DEVICE_ID,
)
}
@@ -401,3 +402,4 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
private const val TIMEOUT = 10000
private const val WINDOW_TITLE = "Test Chipbar Window Title"
private const val WAKE_REASON = "TEST_CHIPBAR_WAKE_REASON"
+private const val DEVICE_ID = "id"
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index a35427f5ec3a..6c82cef22ddb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -27,7 +27,7 @@ class FakeFeatureFlags : FeatureFlags {
private val listenerFlagIds = mutableMapOf<FlagListenable.Listener, MutableSet<Int>>()
init {
- Flags.flagFields.forEach { entry: Map.Entry<String, Flag<*>> ->
+ FlagsFactory.knownFlags.forEach { entry: Map.Entry<String, Flag<*>> ->
knownFlagNames[entry.value.id] = entry.key
}
}
@@ -87,8 +87,6 @@ class FakeFeatureFlags : FeatureFlags {
override fun isEnabled(flag: ResourceBooleanFlag): Boolean = requireBooleanValue(flag.id)
- override fun isEnabled(flag: DeviceConfigBooleanFlag): Boolean = requireBooleanValue(flag.id)
-
override fun isEnabled(flag: SysPropBooleanFlag): Boolean = requireBooleanValue(flag.id)
override fun getString(flag: StringFlag): String = requireStringValue(flag.id)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 627bd096143e..a798f403c73a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -44,6 +44,9 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _isDozing = MutableStateFlow(false)
override val isDozing: Flow<Boolean> = _isDozing
+ private val _isDreaming = MutableStateFlow(false)
+ override val isDreaming: Flow<Boolean> = _isDreaming
+
private val _dozeAmount = MutableStateFlow(0f)
override val dozeAmount: Flow<Float> = _dozeAmount
@@ -53,9 +56,14 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
+ private val _isUdfpsSupported = MutableStateFlow(false)
+
private val _isBouncerShowing = MutableStateFlow(false)
override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing
+ private val _isKeyguardGoingAway = MutableStateFlow(false)
+ override val isKeyguardGoingAway: Flow<Boolean> = _isKeyguardGoingAway
+
private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE)
override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState
@@ -86,4 +94,8 @@ class FakeKeyguardRepository : KeyguardRepository {
fun setDozeAmount(dozeAmount: Float) {
_dozeAmount.value = dozeAmount
}
+
+ override fun isUdfpsSupported(): Boolean {
+ return _isUdfpsSupported.value
+ }
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index c128b5ead406..4f1efd627e40 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -528,7 +528,7 @@ public class AccountManagerService
private Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
List<String> accountTypes, Integer callingUid, UserAccounts accounts) {
if (!packageExistsForUser(packageName, accounts.userId)) {
- Log.d(TAG, "Package not found " + packageName);
+ Log.w(TAG, "getAccountsAndVisibilityForPackage#Package not found " + packageName);
return new LinkedHashMap<>();
}
@@ -677,7 +677,7 @@ public class AccountManagerService
restoreCallingIdentity(identityToken);
}
} catch (NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ Log.w(TAG, "resolveAccountVisibility#Package not found " + e.getMessage());
return AccountManager.VISIBILITY_NOT_VISIBLE;
}
@@ -756,7 +756,7 @@ public class AccountManagerService
}
return true;
} catch (NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ Log.w(TAG, "isPreOApplication#Package not found " + e.getMessage());
return true;
}
}
@@ -4063,7 +4063,7 @@ public class AccountManagerService
int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
return hasAccountAccess(account, packageName, uid);
} catch (NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ Log.w(TAG, "hasAccountAccess#Package not found " + e.getMessage());
return false;
}
}
@@ -4195,7 +4195,7 @@ public class AccountManagerService
}
final long token = Binder.clearCallingIdentity();
try {
- AccountAndUser[] allAccounts = getAllAccounts();
+ AccountAndUser[] allAccounts = getAllAccountsForSystemProcess();
for (int i = allAccounts.length - 1; i >= 0; i--) {
if (allAccounts[i].account.equals(account)) {
return true;
@@ -4345,10 +4345,11 @@ public class AccountManagerService
/**
* Returns accounts for all running users, ignores visibility values.
*
+ * Should only be called by System process.
* @hide
*/
@NonNull
- public AccountAndUser[] getRunningAccounts() {
+ public AccountAndUser[] getRunningAccountsForSystem() {
final int[] runningUserIds;
try {
runningUserIds = ActivityManager.getService().getRunningUserIds();
@@ -4356,26 +4357,34 @@ public class AccountManagerService
// Running in system_server; should never happen
throw new RuntimeException(e);
}
- return getAccounts(runningUserIds);
+ return getAccountsForSystem(runningUserIds);
}
/**
* Returns accounts for all users, ignores visibility values.
*
+ * Should only be called by system process
+ *
* @hide
*/
@NonNull
- public AccountAndUser[] getAllAccounts() {
+ public AccountAndUser[] getAllAccountsForSystemProcess() {
final List<UserInfo> users = getUserManager().getAliveUsers();
final int[] userIds = new int[users.size()];
for (int i = 0; i < userIds.length; i++) {
userIds[i] = users.get(i).id;
}
- return getAccounts(userIds);
+ return getAccountsForSystem(userIds);
}
+ /**
+ * Returns all accounts for the given user, ignores all visibility checks.
+ * This should only be called by system process.
+ *
+ * @hide
+ */
@NonNull
- private AccountAndUser[] getAccounts(int[] userIds) {
+ private AccountAndUser[] getAccountsForSystem(int[] userIds) {
final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
for (int userId : userIds) {
UserAccounts userAccounts = getUserAccounts(userId);
@@ -4384,7 +4393,7 @@ public class AccountManagerService
userAccounts,
null /* type */,
Binder.getCallingUid(),
- null /* packageName */,
+ "android"/* packageName */,
false /* include managed not visible*/);
for (Account account : accounts) {
runningAccounts.add(new AccountAndUser(account, userId));
@@ -5355,7 +5364,7 @@ public class AccountManagerService
}
} else {
Account[] accounts = getAccountsFromCache(userAccounts, null /* type */,
- Process.SYSTEM_UID, null /* packageName */, false);
+ Process.SYSTEM_UID, "android" /* packageName */, false);
fout.println("Accounts: " + accounts.length);
for (Account account : accounts) {
fout.println(" " + account.toString());
@@ -5550,7 +5559,7 @@ public class AccountManagerService
return true;
}
} catch (PackageManager.NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ Log.w(TAG, "isPrivileged#Package not found " + e.getMessage());
}
}
} finally {
@@ -6074,7 +6083,7 @@ public class AccountManagerService
}
}
} catch (NameNotFoundException e) {
- Log.d(TAG, "Package not found " + e.getMessage());
+ Log.w(TAG, "filterSharedAccounts#Package not found " + e.getMessage());
}
Map<Account, Integer> filtered = new LinkedHashMap<>();
for (Map.Entry<Account, Integer> entry : unfiltered.entrySet()) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 85d6f293852c..fde96b96cb9c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3755,22 +3755,27 @@ public class ActivityManagerService extends IActivityManager.Stub
finishForceStopPackageLocked(packageName, appInfo.uid);
}
}
- final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
- Uri.fromParts("package", packageName, null));
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- intent.putExtra(Intent.EXTRA_UID, (appInfo != null) ? appInfo.uid : -1);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
- final int[] visibilityAllowList =
- mPackageManagerInt.getVisibilityAllowList(packageName, resolvedUserId);
- if (isInstantApp) {
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
- null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null,
- false, false, resolvedUserId, false, null, visibilityAllowList);
- } else {
- broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
- null, null, 0, null, null, null, null, false, false, resolvedUserId,
- false, null, visibilityAllowList);
+
+ if (succeeded) {
+ final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
+ Uri.fromParts("package", packageName, null /* fragment */));
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ intent.putExtra(Intent.EXTRA_UID,
+ (appInfo != null) ? appInfo.uid : INVALID_UID);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
+ if (isInstantApp) {
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ }
+ final int[] visibilityAllowList = mPackageManagerInt.getVisibilityAllowList(
+ packageName, resolvedUserId);
+
+ broadcastIntentInPackage("android", null /* featureId */, SYSTEM_UID,
+ uid, pid, intent, null /* resolvedType */, null /* resultTo */,
+ 0 /* resultCode */, null /* resultData */, null /* resultExtras */,
+ isInstantApp ? permission.ACCESS_INSTANT_APPS : null,
+ null /* bOptions */, false /* serialized */, false /* sticky */,
+ resolvedUserId, false /* allowBackgroundActivityStarts */,
+ null /* backgroundActivityStartsToken */, visibilityAllowList);
}
if (observer != null) {
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 453385938aca..212793a3a7b5 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -80,6 +80,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.RescueParty;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.FileDescriptor;
@@ -162,7 +163,7 @@ public class ContentProviderHelper {
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
- ContentProviderRecord cpr;
+ ContentProviderRecord cpr = null;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;
@@ -184,8 +185,21 @@ public class ContentProviderHelper {
checkTime(startTime, "getContentProviderImpl: getProviderByName");
- // First check if this content provider has been published...
- cpr = mProviderMap.getProviderByName(name, userId);
+ UserManagerService userManagerService = UserManagerService.getInstance();
+
+ /*
+ For clone user profile and allowed authority, skipping finding provider and redirecting
+ it to owner profile. Ideally clone profile should not have MediaProvider instance
+ installed and mProviderMap would not have entry for clone user. This is just fallback
+ check to ensure even if MediaProvider is installed in Clone Profile, it should not be
+ used and redirect to owner user's MediaProvider.
+ */
+ //todo(b/236121588) MediaProvider should not be installed in clone profile.
+ if (!isAuthorityRedirectedForCloneProfile(name)
+ || !userManagerService.isMediaSharedWithParent(userId)) {
+ // First check if this content provider has been published...
+ cpr = mProviderMap.getProviderByName(name, userId);
+ }
// If that didn't work, check if it exists for user 0 and then
// verify that it's a singleton provider before using it.
if (cpr == null && userId != UserHandle.USER_SYSTEM) {
@@ -200,11 +214,9 @@ public class ContentProviderHelper {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else if (isAuthorityRedirectedForCloneProfile(name)) {
- UserManagerInternal umInternal = LocalServices.getService(
- UserManagerInternal.class);
- UserInfo userInfo = umInternal.getUserInfo(userId);
-
- if (userInfo != null && userInfo.isCloneProfile()) {
+ if (userManagerService.isMediaSharedWithParent(userId)) {
+ UserManagerInternal umInternal = LocalServices.getService(
+ UserManagerInternal.class);
userId = umInternal.getProfileParentId(userId);
checkCrossUser = false;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 9aecf783a881..05e83da6a107 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -92,6 +92,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
private long mSideFpsLastAcquireStartTime;
private Runnable mAuthSuccessRunnable;
private final Clock mClock;
+ private boolean mDidFinishSfps;
FingerprintAuthenticationClient(
@NonNull Context context,
@@ -197,8 +198,9 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
@Override
protected void handleLifecycleAfterAuth(boolean authenticated) {
- if (authenticated) {
+ if (authenticated && !mDidFinishSfps) {
mCallback.onClientFinished(this, true /* success */);
+ mDidFinishSfps = true;
}
}
@@ -490,11 +492,16 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
if (mSensorProps.isAnySidefpsType()) {
Slog.i(TAG, "(sideFPS): onPowerPressed");
mHandler.post(() -> {
+ if (mDidFinishSfps) {
+ return;
+ }
Slog.i(TAG, "(sideFPS): finishing auth");
// Ignore auths after a power has been detected
mHandler.removeMessages(MESSAGE_AUTH_SUCCESS);
// Do not call onError() as that will send an additional callback to coex.
+ mDidFinishSfps = true;
onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0, true);
+ stopHalOperation();
mSensorOverlays.hide(getSensorId());
});
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 73afa60e8510..eb81e70363d4 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2215,7 +2215,8 @@ public class SyncManager {
pw.print("Storage low: "); pw.println(storageLowIntent != null);
pw.print("Clock valid: "); pw.println(mSyncStorageEngine.isClockValid());
- final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
+ final AccountAndUser[] accounts =
+ AccountManagerService.getSingleton().getAllAccountsForSystemProcess();
pw.print("Accounts: ");
if (accounts != INITIAL_ACCOUNTS_ARRAY) {
@@ -3274,7 +3275,8 @@ public class SyncManager {
private void updateRunningAccountsH(EndPoint syncTargets) {
synchronized (mAccountsLock) {
AccountAndUser[] oldAccounts = mRunningAccounts;
- mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
+ mRunningAccounts =
+ AccountManagerService.getSingleton().getRunningAccountsForSystem();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "Accounts list: ");
for (AccountAndUser acc : mRunningAccounts) {
@@ -3316,7 +3318,8 @@ public class SyncManager {
}
// Cancel all jobs from non-existent accounts.
- AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts();
+ AccountAndUser[] allAccounts =
+ AccountManagerService.getSingleton().getAllAccountsForSystemProcess();
List<SyncOperation> ops = getAllPendingSyncs();
for (int i = 0, opsSize = ops.size(); i < opsSize; i++) {
SyncOperation op = ops.get(i);
diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
index d74c7025ae33..7c9a48431a24 100644
--- a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
+++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
@@ -27,6 +27,7 @@ import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -287,6 +288,14 @@ public class AmbientBrightnessStatsTracker {
localDate)) {
return lastBrightnessStats;
} else {
+ // It is a new day, and we have available data, so log data. The daily boundary
+ // might not be right if the user changes timezones but that is fine, since it
+ // won't be that frequent.
+ if (lastBrightnessStats != null) {
+ FrameworkStatsLog.write(FrameworkStatsLog.AMBIENT_BRIGHTNESS_STATS_REPORTED,
+ lastBrightnessStats.getStats(),
+ lastBrightnessStats.getBucketBoundaries());
+ }
AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(localDate,
BUCKET_BOUNDARIES_FOR_NEW_STATS);
if (userStats.size() == MAX_DAYS_TO_TRACK) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 7806ece9e65f..ef1baf62eecf 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -123,6 +123,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
@@ -146,6 +147,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
+
/**
* Manages attached displays.
* <p>
@@ -1681,7 +1683,20 @@ public final class DisplayManagerService extends SystemService {
final Point userPreferredResolution =
mPersistentDataStore.getUserPreferredResolution(device);
final float refreshRate = mPersistentDataStore.getUserPreferredRefreshRate(device);
- if (userPreferredResolution == null && Float.isNaN(refreshRate)) {
+ // If value in persistentDataStore is null, preserving the mode from systemPreferredMode.
+ // This is required because in some devices, user-preferred mode was not stored in
+ // persistentDataStore, but was stored in a config which is returned through
+ // systemPreferredMode.
+ if ((userPreferredResolution == null && Float.isNaN(refreshRate))
+ || (userPreferredResolution.equals(0, 0) && refreshRate == 0.0f)) {
+ Display.Mode systemPreferredMode = device.getSystemPreferredDisplayModeLocked();
+ if (systemPreferredMode == null) {
+ return;
+ }
+ storeModeInPersistentDataStoreLocked(
+ display.getDisplayIdLocked(), systemPreferredMode.getPhysicalWidth(),
+ systemPreferredMode.getPhysicalHeight(), systemPreferredMode.getRefreshRate());
+ device.setUserPreferredDisplayModeLocked(systemPreferredMode);
return;
}
Display.Mode.Builder modeBuilder = new Display.Mode.Builder();
@@ -1867,6 +1882,14 @@ public final class DisplayManagerService extends SystemService {
if (displayDevice == null) {
return;
}
+ if (mLogicalDisplayMapper.getDisplayLocked(displayDevice)
+ .getDisplayInfoLocked().type == Display.TYPE_INTERNAL) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BRIGHTNESS_CONFIGURATION_UPDATED,
+ c.getCurve().first,
+ c.getCurve().second,
+ // should not be logged for virtual displays
+ uniqueId);
+ }
mPersistentDataStore.setBrightnessConfigurationForDisplayLocked(c, displayDevice,
userSerial, packageName);
} finally {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 69c890d90d03..36bff20e0d54 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2810,18 +2810,22 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
float appliedPowerFactor = event.isLowPowerModeSet() ? event.powerFactor : -1f;
- FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
- convertToNits(event.initialBrightness),
- convertToNits(event.brightness),
- event.slowAmbientLux,
- event.physicalDisplayId,
- event.isShortTermModelActive(),
- appliedPowerFactor,
- appliedRbcStrength,
- appliedHbmMaxNits,
- appliedThermalCapNits,
- event.automaticBrightnessEnabled,
- FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL);
+ if (mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
+ && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
+ .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL) {
+ FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
+ convertToNits(event.initialBrightness),
+ convertToNits(event.brightness),
+ event.slowAmbientLux,
+ event.physicalDisplayId,
+ event.isShortTermModelActive(),
+ appliedPowerFactor,
+ appliedRbcStrength,
+ appliedHbmMaxNits,
+ appliedThermalCapNits,
+ event.automaticBrightnessEnabled,
+ FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL);
+ }
}
class BrightnessEvent {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 2a219289cf10..efb2cb7a3283 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -411,11 +411,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// For a new display, we need to initialize the default mode ID.
if (mDefaultModeId == INVALID_MODE_ID) {
- mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID
- ? mSystemPreferredModeId : activeRecord.mMode.getModeId();
- mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID
- ? preferredSfDisplayMode.group
- : mActiveSfDisplayMode.group;
+ mDefaultModeId = activeRecord.mMode.getModeId();
+ mDefaultModeGroup = mActiveSfDisplayMode.group;
} else if (modesAdded && activeModeChanged) {
Slog.d(TAG, "New display modes are added and the active mode has changed, "
+ "use active mode as default mode.");
@@ -897,6 +894,13 @@ final class LocalDisplayAdapter extends DisplayAdapter {
public void setUserPreferredDisplayModeLocked(Display.Mode mode) {
final int oldModeId = getPreferredModeId();
mUserPreferredMode = mode;
+ // When clearing the user preferred mode we need to also reset the default mode. This is
+ // used by DisplayModeDirector to determine the default resolution, so if we don't clear
+ // it then the resolution won't reset to what it would've been prior to setting a user
+ // preferred display mode.
+ if (mode == null && mSystemPreferredModeId != INVALID_MODE_ID) {
+ mDefaultModeId = mSystemPreferredModeId;
+ }
if (mode != null && (mode.isRefreshRateSet() || mode.isResolutionSet())) {
Display.Mode matchingSupportedMode;
matchingSupportedMode = findMode(mode.getPhysicalWidth(),
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index e4aa5e574955..b9511c45771b 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -23,12 +23,14 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -39,6 +41,8 @@ import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.input.InputManagerInternal;
+import android.net.Uri;
+import android.os.BatteryManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -72,6 +76,8 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -88,6 +94,15 @@ public final class DreamManagerService extends SystemService {
private static final String DOZE_WAKE_LOCK_TAG = "dream:doze";
private static final String DREAM_WAKE_LOCK_TAG = "dream:dream";
+ /** Constants for the when to activate dreams. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({DREAM_ON_DOCK, DREAM_ON_CHARGE, DREAM_ON_DOCK_OR_CHARGE})
+ public @interface WhenToDream {}
+ private static final int DREAM_DISABLED = 0x0;
+ private static final int DREAM_ON_DOCK = 0x1;
+ private static final int DREAM_ON_CHARGE = 0x2;
+ private static final int DREAM_ON_DOCK_OR_CHARGE = 0x3;
+
private final Object mLock = new Object();
private final Context mContext;
@@ -101,12 +116,20 @@ public final class DreamManagerService extends SystemService {
private final DreamUiEventLogger mDreamUiEventLogger;
private final ComponentName mAmbientDisplayComponent;
private final boolean mDismissDreamOnActivityStart;
+ private final boolean mDreamsOnlyEnabledForSystemUser;
+ private final boolean mDreamsEnabledByDefaultConfig;
+ private final boolean mDreamsActivatedOnChargeByDefault;
+ private final boolean mDreamsActivatedOnDockByDefault;
@GuardedBy("mLock")
private DreamRecord mCurrentDream;
private boolean mForceAmbientDisplayEnabled;
- private final boolean mDreamsOnlyEnabledForSystemUser;
+ private SettingsObserver mSettingsObserver;
+ private boolean mDreamsEnabledSetting;
+ @WhenToDream private int mWhenToDream;
+ private boolean mIsDocked;
+ private boolean mIsCharging;
// A temporary dream component that, when present, takes precedence over user configured dream
// component.
@@ -144,6 +167,37 @@ public final class DreamManagerService extends SystemService {
}
};
+ private final BroadcastReceiver mChargingReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mIsCharging = BatteryManager.ACTION_CHARGING.equals(intent.getAction());
+ }
+ };
+
+ private final BroadcastReceiver mDockStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
+ final int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ mIsDocked = dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ }
+ }
+ };
+
+ private final class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ synchronized (mLock) {
+ updateWhenToDreamSettings();
+ }
+ }
+ }
+
public DreamManagerService(Context context) {
super(context);
mContext = context;
@@ -164,6 +218,14 @@ public final class DreamManagerService extends SystemService {
mContext.getResources().getBoolean(R.bool.config_dreamsOnlyEnabledForSystemUser);
mDismissDreamOnActivityStart = mContext.getResources().getBoolean(
R.bool.config_dismissDreamOnActivityStart);
+
+ mDreamsEnabledByDefaultConfig = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dreamsEnabledByDefault);
+ mDreamsActivatedOnChargeByDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
+ mDreamsActivatedOnDockByDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
+ mSettingsObserver = new SettingsObserver(mHandler);
}
@Override
@@ -197,6 +259,30 @@ public final class DreamManagerService extends SystemService {
DREAM_MANAGER_ORDERED_ID,
mActivityInterceptorCallback);
}
+
+ mContext.registerReceiver(
+ mDockStateReceiver, new IntentFilter(Intent.ACTION_DOCK_EVENT));
+ IntentFilter chargingIntentFilter = new IntentFilter();
+ chargingIntentFilter.addAction(BatteryManager.ACTION_CHARGING);
+ chargingIntentFilter.addAction(BatteryManager.ACTION_DISCHARGING);
+ mContext.registerReceiver(mChargingReceiver, chargingIntentFilter);
+
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SCREENSAVER_ENABLED),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+
+ // We don't get an initial broadcast for the batter state, so we have to initialize
+ // directly from BatteryManager.
+ mIsCharging = mContext.getSystemService(BatteryManager.class).isCharging();
+
+ updateWhenToDreamSettings();
}
}
@@ -207,6 +293,14 @@ public final class DreamManagerService extends SystemService {
pw.println("mCurrentDream=" + mCurrentDream);
pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled);
pw.println("mDreamsOnlyEnabledForSystemUser=" + mDreamsOnlyEnabledForSystemUser);
+ pw.println("mDreamsEnabledSetting=" + mDreamsEnabledSetting);
+ pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled);
+ pw.println("mDreamsOnlyEnabledForSystemUser=" + mDreamsOnlyEnabledForSystemUser);
+ pw.println("mDreamsActivatedOnDockByDefault=" + mDreamsActivatedOnDockByDefault);
+ pw.println("mDreamsActivatedOnChargeByDefault=" + mDreamsActivatedOnChargeByDefault);
+ pw.println("mIsDocked=" + mIsDocked);
+ pw.println("mIsCharging=" + mIsCharging);
+ pw.println("mWhenToDream=" + mWhenToDream);
pw.println("getDozeComponent()=" + getDozeComponent());
pw.println();
@@ -214,7 +308,28 @@ public final class DreamManagerService extends SystemService {
}
}
- /** Whether a real dream is occurring. */
+ private void updateWhenToDreamSettings() {
+ synchronized (mLock) {
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ final int activateWhenCharging = (Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+ mDreamsActivatedOnChargeByDefault ? 1 : 0,
+ UserHandle.USER_CURRENT) != 0) ? DREAM_ON_CHARGE : DREAM_DISABLED;
+ final int activateWhenDocked = (Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+ mDreamsActivatedOnDockByDefault ? 1 : 0,
+ UserHandle.USER_CURRENT) != 0) ? DREAM_ON_DOCK : DREAM_DISABLED;
+ mWhenToDream = activateWhenCharging + activateWhenDocked;
+
+ mDreamsEnabledSetting = (Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SCREENSAVER_ENABLED,
+ mDreamsEnabledByDefaultConfig ? 1 : 0,
+ UserHandle.USER_CURRENT) != 0);
+ }
+ }
+
+ /** Whether a real dream is occurring. */
private boolean isDreamingInternal() {
synchronized (mLock) {
return mCurrentDream != null && !mCurrentDream.isPreview
@@ -236,6 +351,30 @@ public final class DreamManagerService extends SystemService {
}
}
+ /** Whether dreaming can start given user settings and the current dock/charge state. */
+ private boolean canStartDreamingInternal(boolean isScreenOn) {
+ synchronized (mLock) {
+ // Can't start dreaming if we are already dreaming.
+ if (isScreenOn && isDreamingInternal()) {
+ return false;
+ }
+
+ if (!mDreamsEnabledSetting) {
+ return false;
+ }
+
+ if ((mWhenToDream & DREAM_ON_CHARGE) == DREAM_ON_CHARGE) {
+ return mIsCharging;
+ }
+
+ if ((mWhenToDream & DREAM_ON_DOCK) == DREAM_ON_DOCK) {
+ return mIsDocked;
+ }
+
+ return false;
+ }
+ }
+
protected void requestStartDreamFromShell() {
requestDreamInternal();
}
@@ -352,10 +491,6 @@ public final class DreamManagerService extends SystemService {
}
}
- private ComponentName getActiveDreamComponentInternal(boolean doze) {
- return chooseDreamForUser(doze, ActivityManager.getCurrentUser());
- }
-
/**
* If doze is true, returns the doze component for the user.
* Otherwise, returns the system dream component, if present.
@@ -508,7 +643,7 @@ public final class DreamManagerService extends SystemService {
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, DREAM_WAKE_LOCK_TAG);
final Binder dreamToken = mCurrentDream.token;
mHandler.post(wakeLock.wrap(() -> {
- mAtmInternal.notifyDreamStateChanged(true);
+ mAtmInternal.notifyActiveDreamChanged(name);
mController.startDream(dreamToken, name, isPreviewMode, canDoze, userId, wakeLock,
mDreamOverlayServiceName, reason);
}));
@@ -533,7 +668,7 @@ public final class DreamManagerService extends SystemService {
@GuardedBy("mLock")
private void cleanupDreamLocked() {
- mHandler.post(() -> mAtmInternal.notifyDreamStateChanged(false /*dreaming*/));
+ mHandler.post(() -> mAtmInternal.notifyActiveDreamChanged(null));
if (mCurrentDream == null) {
return;
@@ -869,8 +1004,8 @@ public final class DreamManagerService extends SystemService {
}
@Override
- public ComponentName getActiveDreamComponent(boolean doze) {
- return getActiveDreamComponentInternal(doze);
+ public boolean canStartDreaming(boolean isScreenOn) {
+ return canStartDreamingInternal(isScreenOn);
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 37f980d699bd..c2df904d07ce 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4966,16 +4966,7 @@ public class NotificationManagerService extends SystemService {
}
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
- // If the caller is system, take the package name from the rule's owner rather than
- // from the caller's package.
- String rulePkg = pkg;
- if (isCallingUidSystem()) {
- if (automaticZenRule.getOwner() != null) {
- rulePkg = automaticZenRule.getOwner().getPackageName();
- }
- }
-
- return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
+ return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule,
"addAutomaticZenRule");
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4c23ab84a14f..d42667951608 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -326,7 +326,7 @@ public class ZenModeHelper {
public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
String reason) {
- if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
+ if (!isSystemRule(automaticZenRule)) {
PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
if (component == null) {
component = getActivityInfo(automaticZenRule.getConfigurationActivity());
@@ -582,6 +582,11 @@ public class ZenModeHelper {
}
}
+ private boolean isSystemRule(AutomaticZenRule rule) {
+ return rule.getOwner() != null
+ && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
+ }
+
private ServiceInfo getServiceInfo(ComponentName owner) {
Intent queryIntent = new Intent();
queryIntent.setComponent(owner);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 37bfbb11948a..06458d1bf50c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -366,6 +366,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private boolean mStageDirInUse = false;
+ /**
+ * True if the installation is already in progress. This is used to prevent the caller
+ * from {@link #commit(IntentSender, boolean) committing} the session again while the
+ * installation is still in progress.
+ */
+ @GuardedBy("mLock")
+ private boolean mInstallationInProgress = false;
+
/** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
@GuardedBy("mLock")
private boolean mPermissionsManuallyAccepted = false;
@@ -1661,6 +1669,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ synchronized (mLock) {
+ if (mInstallationInProgress) {
+ throw new IllegalStateException("Installation is already in progress. Don't "
+ + "commit session=" + sessionId + " again.");
+ }
+ mInstallationInProgress = true;
+ }
+
dispatchSessionSealed();
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 0ae92b4ee846..32ee21cf1658 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -616,6 +616,10 @@ final class DefaultPermissionGrantPolicy {
grantPermissionsToSystemPackage(pm, getDefaultCaptivePortalLoginPackage(), userId,
NOTIFICATION_PERMISSIONS);
+ // Dock Manager
+ grantPermissionsToSystemPackage(pm, getDefaultDockManagerPackage(), userId,
+ NOTIFICATION_PERMISSIONS);
+
// Camera
grantPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackage(pm, MediaStore.ACTION_IMAGE_CAPTURE, userId),
@@ -932,6 +936,10 @@ final class DefaultPermissionGrantPolicy {
return mContext.getString(R.string.config_defaultCaptivePortalLoginPackageName);
}
+ private String getDefaultDockManagerPackage() {
+ return mContext.getString(R.string.config_defaultDockManagerPackageName);
+ }
+
@SafeVarargs
private final void grantPermissionToEachSystemPackage(PackageManagerWrapper pm,
ArrayList<String> packages, int userId, Set<String>... permissions) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fa0d41c5ac2f..352d4be6c7ce 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -256,6 +256,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final int SHORT_PRESS_POWER_GO_HOME = 4;
static final int SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME = 5;
static final int SHORT_PRESS_POWER_LOCK_OR_SLEEP = 6;
+ static final int SHORT_PRESS_POWER_DREAM_OR_SLEEP = 7;
// must match: config_LongPressOnPowerBehavior in config.xml
static final int LONG_PRESS_POWER_NOTHING = 0;
@@ -971,7 +972,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
} else if (count > 3 && count <= getMaxMultiPressPowerCount()) {
Slog.d(TAG, "No behavior defined for power press count " + count);
- } else if (count == 1 && interactive && !beganFromNonInteractive) {
+ } else if (count == 1 && interactive) {
+ if (beganFromNonInteractive) {
+ // The screen off case, where we might want to start dreaming on power button press.
+ attemptToDreamFromShortPowerButtonPress(false, () -> {});
+ return;
+ }
if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) {
Slog.i(TAG, "Suppressing power key because the user is interacting with the "
+ "fingerprint sensor");
@@ -1020,11 +1026,39 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
break;
}
+ case SHORT_PRESS_POWER_DREAM_OR_SLEEP: {
+ attemptToDreamFromShortPowerButtonPress(
+ true,
+ () -> sleepDefaultDisplayFromPowerButton(eventTime, 0));
+ break;
+ }
}
}
}
/**
+ * Attempt to dream from a power button press.
+ *
+ * @param isScreenOn Whether the screen is currently on.
+ * @param noDreamAction The action to perform if dreaming is not possible.
+ */
+ private void attemptToDreamFromShortPowerButtonPress(
+ boolean isScreenOn, Runnable noDreamAction) {
+ if (mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_SLEEP) {
+ noDreamAction.run();
+ return;
+ }
+
+ final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
+ if (dreamManagerInternal == null || !dreamManagerInternal.canStartDreaming(isScreenOn)) {
+ noDreamAction.run();
+ return;
+ }
+
+ dreamManagerInternal.requestDream();
+ }
+
+ /**
* Sends the default display to sleep as a result of a power button press.
*
* @return {@code true} if the device was sent to sleep, {@code false} if the device did not
@@ -1595,7 +1629,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// If there's a dream running then use home to escape the dream
// but don't actually go home.
- if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) {
+ final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
+ if (dreamManagerInternal != null && dreamManagerInternal.isDreaming()) {
mDreamManagerInternal.stopDream(false /*immediate*/, "short press on home" /*reason*/);
return;
}
@@ -2483,6 +2518,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+ private DreamManagerInternal getDreamManagerInternal() {
+ if (mDreamManagerInternal == null) {
+ // If mDreamManagerInternal is null, attempt to re-fetch it.
+ mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
+ }
+
+ return mDreamManagerInternal;
+ }
+
private void updateWakeGestureListenerLp() {
if (shouldEnableWakeGestureLp()) {
mWakeGestureListener.requestWakeUpTrigger();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 725fb3fec616..377a651eb031 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1033,7 +1033,7 @@ public final class PowerManagerService extends SystemService
super(context);
mContext = context;
- mBinderService = new BinderService();
+ mBinderService = new BinderService(mContext);
mLocalService = new LocalService();
mNativeWrapper = injector.createNativeWrapper();
mSystemProperties = injector.createSystemPropertiesWrapper();
@@ -5465,12 +5465,17 @@ public final class PowerManagerService extends SystemService
@VisibleForTesting
final class BinderService extends IPowerManager.Stub {
+ private final PowerManagerShellCommand mShellCommand;
+
+ BinderService(Context context) {
+ mShellCommand = new PowerManagerShellCommand(context, this);
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
- (new PowerManagerShellCommand(this)).exec(
- this, in, out, err, args, callback, resultReceiver);
+ mShellCommand.exec(this, in, out, err, args, callback, resultReceiver);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/power/PowerManagerShellCommand.java b/services/core/java/com/android/server/power/PowerManagerShellCommand.java
index a9b33ed58ef7..9439b762fde0 100644
--- a/services/core/java/com/android/server/power/PowerManagerShellCommand.java
+++ b/services/core/java/com/android/server/power/PowerManagerShellCommand.java
@@ -16,10 +16,15 @@
package com.android.server.power;
+import android.content.Context;
import android.content.Intent;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.ShellCommand;
+import android.util.SparseArray;
+import android.view.Display;
import java.io.PrintWriter;
import java.util.List;
@@ -27,9 +32,13 @@ import java.util.List;
class PowerManagerShellCommand extends ShellCommand {
private static final int LOW_POWER_MODE_ON = 1;
- final PowerManagerService.BinderService mService;
+ private final Context mContext;
+ private final PowerManagerService.BinderService mService;
- PowerManagerShellCommand(PowerManagerService.BinderService service) {
+ private SparseArray<WakeLock> mProxWakelocks = new SparseArray<>();
+
+ PowerManagerShellCommand(Context context, PowerManagerService.BinderService service) {
+ mContext = context;
mService = service;
}
@@ -52,6 +61,8 @@ class PowerManagerShellCommand extends ShellCommand {
return runSuppressAmbientDisplay();
case "list-ambient-display-suppression-tokens":
return runListAmbientDisplaySuppressionTokens();
+ case "set-prox":
+ return runSetProx();
default:
return handleDefaultCommands(cmd);
}
@@ -117,6 +128,56 @@ class PowerManagerShellCommand extends ShellCommand {
return 0;
}
+
+ /** TODO: Consider updating this code to support all wakelock types. */
+ private int runSetProx() throws RemoteException {
+ PrintWriter pw = getOutPrintWriter();
+ final boolean acquire;
+ switch (getNextArgRequired().toLowerCase()) {
+ case "list":
+ pw.println("Wakelocks:");
+ pw.println(mProxWakelocks);
+ return 0;
+ case "acquire":
+ acquire = true;
+ break;
+ case "release":
+ acquire = false;
+ break;
+ default:
+ pw.println("Error: Allowed options are 'list' 'enable' and 'disable'.");
+ return -1;
+ }
+
+ int displayId = Display.INVALID_DISPLAY;
+ String displayOption = getNextArg();
+ if ("-d".equals(displayOption)) {
+ String idStr = getNextArg();
+ displayId = Integer.parseInt(idStr);
+ if (displayId < 0) {
+ pw.println("Error: Specified displayId (" + idStr + ") must a non-negative int.");
+ return -1;
+ }
+ }
+
+ int wakelockIndex = displayId + 1; // SparseArray doesn't support negative indexes
+ WakeLock wakelock = mProxWakelocks.get(wakelockIndex);
+ if (wakelock == null) {
+ PowerManager pm = mContext.getSystemService(PowerManager.class);
+ wakelock = pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,
+ "PowerManagerShellCommand[" + displayId + "]", displayId);
+ mProxWakelocks.put(wakelockIndex, wakelock);
+ }
+
+ if (acquire) {
+ wakelock.acquire();
+ } else {
+ wakelock.release();
+ }
+ pw.println(wakelock);
+ return 0;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -138,6 +199,11 @@ class PowerManagerShellCommand extends ShellCommand {
pw.println(" ambient display");
pw.println(" list-ambient-display-suppression-tokens");
pw.println(" prints the tokens used to suppress ambient display");
+ pw.println(" set-prox [list|acquire|release] (-d <display_id>)");
+ pw.println(" Acquires the proximity sensor wakelock. Wakelock is associated with");
+ pw.println(" a specific display if specified. 'list' lists wakelocks previously");
+ pw.println(" created by set-prox including their held status.");
+
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 414d927ec509..dd870a8905cc 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1157,6 +1157,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.w(TAG, "WallpaperService is not connected yet");
return;
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.connectLocked-" + wallpaper.wallpaperComponent);
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
null /* options */);
@@ -1173,6 +1175,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
false /* fromUser */, wallpaper, null /* reply */);
}
}
+ t.traceEnd();
}
void disconnectLocked() {
@@ -1322,6 +1325,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.onServiceConnected-" + name);
synchronized (mLock) {
if (mWallpaper.connection == this) {
mService = IWallpaperService.Stub.asInterface(service);
@@ -1337,6 +1342,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mContext.getMainThreadHandler().removeCallbacks(mTryToRebindRunnable);
}
}
+ t.traceEnd();
}
@Override
@@ -1544,6 +1550,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
public void engineShown(IWallpaperEngine engine) {
synchronized (mLock) {
if (mReply != null) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.mReply.sendResult");
final long ident = Binder.clearCallingIdentity();
try {
mReply.sendResult(null);
@@ -1551,6 +1559,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Binder.restoreCallingIdentity(ident);
Slog.d(TAG, "failed to send callback!", e);
}
+ t.traceEnd();
mReply = null;
}
}
@@ -3049,6 +3058,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return true;
}
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.bindWallpaperComponentLocked-" + componentName);
try {
if (componentName == null) {
componentName = mDefaultWallpaperComponent;
@@ -3181,6 +3192,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
Slog.w(TAG, msg);
return false;
+ } finally {
+ t.traceEnd();
}
return true;
}
@@ -3225,7 +3238,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.attachServiceLocked");
conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
+ t.traceEnd();
}
private void notifyCallbacksLocked(WallpaperData wallpaper) {
@@ -3351,6 +3367,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
void saveSettingsLocked(int userId) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.saveSettingsLocked-" + userId);
JournaledFile journal = makeJournaledFile(userId);
FileOutputStream fstream = null;
try {
@@ -3379,6 +3397,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
IoUtils.closeQuietly(fstream);
journal.rollback();
}
+ t.traceEnd();
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ccab96888e8f..27f8c5c2aa11 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1750,6 +1750,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
prevDc.mClosingApps.remove(this);
+ prevDc.getDisplayPolicy().removeRelaunchingApp(this);
if (prevDc.mFocusedApp == this) {
prevDc.setFocusedApp(null);
@@ -3969,6 +3970,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void startRelaunching() {
if (mPendingRelaunchCount == 0) {
mRelaunchStartTime = SystemClock.elapsedRealtime();
+ if (mVisibleRequested) {
+ mDisplayContent.getDisplayPolicy().addRelaunchingApp(this);
+ }
}
clearAllDrawn();
@@ -3982,7 +3986,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mPendingRelaunchCount--;
if (mPendingRelaunchCount == 0 && !isClientVisible()) {
// Don't count if the client won't report drawn.
- mRelaunchStartTime = 0;
+ finishOrAbortReplacingWindow();
}
} else {
// Update keyguard flags upon finishing relaunch.
@@ -4003,7 +4007,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
mPendingRelaunchCount = 0;
+ finishOrAbortReplacingWindow();
+ }
+
+ void finishOrAbortReplacingWindow() {
mRelaunchStartTime = 0;
+ mDisplayContent.getDisplayPolicy().removeRelaunchingApp(this);
}
/**
@@ -5112,6 +5121,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */);
}
logAppCompatState();
+ if (!visible) {
+ finishOrAbortReplacingWindow();
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index ed50c1e979ad..2387e25c270c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2247,12 +2247,6 @@ class ActivityStarter {
? targetTask.getTopNonFinishingActivity()
: targetTaskTop;
- // At this point we are certain we want the task moved to the front. If we need to dismiss
- // any other always-on-top root tasks, now is the time to do it.
- if (targetTaskTop.canTurnScreenOn() && mService.isDreaming()) {
- targetTaskTop.mTaskSupervisor.wakeUp("recycleTask#turnScreenOnFlag");
- }
-
if (mMovedToFront) {
// We moved the task to front, use starting window to hide initial drawn delay.
targetTaskTop.showStartingWindow(true /* taskSwitch */);
@@ -2264,6 +2258,12 @@ class ActivityStarter {
// And for paranoia, make sure we have correctly resumed the top activity.
resumeTargetRootTaskIfNeeded();
+ // This is moving an existing task to front. But since dream activity has a higher z-order
+ // to cover normal activities, it needs the awakening event to be dismissed.
+ if (mService.isDreaming() && targetTaskTop.canTurnScreenOn()) {
+ targetTaskTop.mTaskSupervisor.wakeUp("recycleTask#turnScreenOnFlag");
+ }
+
mLastStartActivityRecord = targetTaskTop;
return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
}
@@ -3037,7 +3037,12 @@ class ActivityStarter {
newParent = candidateTf;
}
}
- newParent.mTransitionController.collect(newParent);
+ if (newParent.asTask() == null) {
+ // only collect task-fragments.
+ // TODO(b/258095975): we probably shouldn't ever collect the parent here since it isn't
+ // changing. The logic that changes it should collect it.
+ newParent.mTransitionController.collect(newParent);
+ }
if (mStartActivity.getTaskFragment() == null
|| mStartActivity.getTaskFragment() == newParent) {
newParent.addChild(mStartActivity, POSITION_TOP);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index e7b62b0d66d3..2792f4265caa 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -287,8 +287,10 @@ public abstract class ActivityTaskManagerInternal {
/**
* Called when the device changes its dreaming state.
+ *
+ * @param activeDreamComponent The currently active dream. If null, the device is not dreaming.
*/
- public abstract void notifyDreamStateChanged(boolean dreaming);
+ public abstract void notifyActiveDreamChanged(@Nullable ComponentName activeDreamComponent);
/**
* Set a uid that is allowed to bypass stopped app switches, launching an app
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0398cc84f9db..1ba7e687a553 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -208,7 +208,6 @@ import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
import android.service.dreams.DreamActivity;
-import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.sysprop.DisplayProperties;
@@ -669,11 +668,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private volatile boolean mSleeping;
/**
- * The mDreaming state is set by the {@link DreamManagerService} when it receives a request to
- * start/stop the dream. It is set to true shortly before the {@link DreamService} is started.
- * It is set to false after the {@link DreamService} is stopped.
+ * The mActiveDreamComponent state is set by the {@link DreamManagerService} when it receives a
+ * request to start/stop the dream. It is set to the active dream shortly before the
+ * {@link DreamService} is started. It is set to null after the {@link DreamService} is stopped.
*/
- private volatile boolean mDreaming;
+ @Nullable
+ private volatile ComponentName mActiveDreamComponent;
/**
* The process state used for processes that are running the top activities.
@@ -1442,31 +1442,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
boolean isDreaming() {
- return mDreaming;
+ return mActiveDreamComponent != null;
}
boolean canLaunchDreamActivity(String packageName) {
- if (!mDreaming || packageName == null) {
+ if (mActiveDreamComponent == null || packageName == null) {
ProtoLog.e(WM_DEBUG_DREAM, "Cannot launch dream activity due to invalid state. "
- + "dreaming: %b packageName: %s", mDreaming, packageName);
+ + "dream component: %s packageName: %s", mActiveDreamComponent, packageName);
return false;
}
- final DreamManagerInternal dreamManager =
- LocalServices.getService(DreamManagerInternal.class);
- // Verify that the package is the current active dream or doze component. The
- // getActiveDreamComponent() call path does not acquire the DreamManager lock and thus
- // is safe to use.
- final ComponentName activeDream = dreamManager.getActiveDreamComponent(false /* doze */);
- if (activeDream != null && packageName.equals(activeDream.getPackageName())) {
- return true;
- }
- final ComponentName activeDoze = dreamManager.getActiveDreamComponent(true /* doze */);
- if (activeDoze != null && packageName.equals(activeDoze.getPackageName())) {
+ if (packageName.equals(mActiveDreamComponent.getPackageName())) {
return true;
}
ProtoLog.e(WM_DEBUG_DREAM,
- "Dream packageName does not match active dream. Package %s does not match %s or %s",
- packageName, String.valueOf(activeDream), String.valueOf(activeDoze));
+ "Dream packageName does not match active dream. Package %s does not match %s",
+ packageName, String.valueOf(mActiveDreamComponent));
return false;
}
@@ -5676,9 +5666,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void notifyDreamStateChanged(boolean dreaming) {
+ public void notifyActiveDreamChanged(@Nullable ComponentName dreamComponent) {
synchronized (mGlobalLock) {
- mDreaming = dreaming;
+ mActiveDreamComponent = dreamComponent;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index fe691c61a96b..54664f5fbe3d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -27,6 +27,7 @@ import static android.app.ActivityManager.START_FLAG_DEBUG;
import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WaitResult.INVALID_DELAY;
@@ -2592,6 +2593,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// Apply options to prevent pendingOptions be taken when scheduling
// activity lifecycle transaction to make sure the override pending app
// transition will be applied immediately.
+ if (activityOptions.getAnimationType() == ANIM_REMOTE_ANIMATION) {
+ targetActivity.mPendingRemoteAnimation =
+ activityOptions.getRemoteAnimationAdapter();
+ }
targetActivity.applyOptionsAnimation();
if (activityOptions != null && activityOptions.getLaunchCookie() != null) {
targetActivity.mLaunchCookie = activityOptions.getLaunchCookie();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 2688ff757f64..2c289c99d3da 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -282,6 +282,12 @@ public class DisplayPolicy {
private final ArraySet<WindowState> mInsetsSourceWindowsExceptIme = new ArraySet<>();
+ /** Apps which are controlling the appearance of system bars */
+ private final ArraySet<ActivityRecord> mSystemBarColorApps = new ArraySet<>();
+
+ /** Apps which are relaunching and were controlling the appearance of system bars */
+ private final ArraySet<ActivityRecord> mRelaunchingSystemBarColorApps = new ArraySet<>();
+
private boolean mIsFreeformWindowOverlappingWithNavBar;
private boolean mLastImmersiveMode;
@@ -1550,6 +1556,7 @@ public class DisplayPolicy {
mStatusBarBackgroundWindows.clear();
mStatusBarColorCheckedBounds.setEmpty();
mStatusBarBackgroundCheckedBounds.setEmpty();
+ mSystemBarColorApps.clear();
mAllowLockscreenWhenOn = false;
mShowingDream = false;
@@ -1626,6 +1633,7 @@ public class DisplayPolicy {
win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
new Rect(win.getFrame())));
mStatusBarColorCheckedBounds.union(sTmpRect);
+ addSystemBarColorApp(win);
}
}
@@ -1638,6 +1646,7 @@ public class DisplayPolicy {
if (isOverlappingWithNavBar) {
if (mNavBarColorWindowCandidate == null) {
mNavBarColorWindowCandidate = win;
+ addSystemBarColorApp(win);
}
if (mNavBarBackgroundWindow == null) {
mNavBarBackgroundWindow = win;
@@ -1656,9 +1665,11 @@ public class DisplayPolicy {
}
} else if (win.isDimming()) {
if (mStatusBar != null) {
- addStatusBarAppearanceRegionsForDimmingWindow(
+ if (addStatusBarAppearanceRegionsForDimmingWindow(
win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
- mStatusBar.getFrame(), win.getBounds(), win.getFrame());
+ mStatusBar.getFrame(), win.getBounds(), win.getFrame())) {
+ addSystemBarColorApp(win);
+ }
}
if (isOverlappingWithNavBar && mNavBarColorWindowCandidate == null) {
mNavBarColorWindowCandidate = win;
@@ -1666,18 +1677,21 @@ public class DisplayPolicy {
}
}
- private void addStatusBarAppearanceRegionsForDimmingWindow(int appearance, Rect statusBarFrame,
- Rect winBounds, Rect winFrame) {
+ /**
+ * Returns true if mStatusBarAppearanceRegionList is changed.
+ */
+ private boolean addStatusBarAppearanceRegionsForDimmingWindow(
+ int appearance, Rect statusBarFrame, Rect winBounds, Rect winFrame) {
if (!sTmpRect.setIntersect(winBounds, statusBarFrame)) {
- return;
+ return false;
}
if (mStatusBarColorCheckedBounds.contains(sTmpRect)) {
- return;
+ return false;
}
if (appearance == 0 || !sTmpRect2.setIntersect(winFrame, statusBarFrame)) {
mStatusBarAppearanceRegionList.add(new AppearanceRegion(0, new Rect(winBounds)));
mStatusBarColorCheckedBounds.union(sTmpRect);
- return;
+ return true;
}
// A dimming window can divide status bar into different appearance regions (up to 3).
// +---------+-------------+---------+
@@ -1706,6 +1720,14 @@ public class DisplayPolicy {
// We don't have vertical status bar yet, so we don't handle the other orientation.
}
mStatusBarColorCheckedBounds.union(sTmpRect);
+ return true;
+ }
+
+ private void addSystemBarColorApp(WindowState win) {
+ final ActivityRecord app = win.mActivityRecord;
+ if (app != null) {
+ mSystemBarColorApps.add(app);
+ }
}
/**
@@ -2202,6 +2224,25 @@ public class DisplayPolicy {
return mDisplayContent.getInsetsPolicy();
}
+ /**
+ * Called when an app has started replacing its main window.
+ */
+ void addRelaunchingApp(ActivityRecord app) {
+ if (mSystemBarColorApps.contains(app)) {
+ mRelaunchingSystemBarColorApps.add(app);
+ }
+ }
+
+ /**
+ * Called when an app has finished replacing its main window or aborted.
+ */
+ void removeRelaunchingApp(ActivityRecord app) {
+ final boolean removed = mRelaunchingSystemBarColorApps.remove(app);
+ if (removed & mRelaunchingSystemBarColorApps.isEmpty()) {
+ updateSystemBarAttributes();
+ }
+ }
+
void resetSystemBarAttributes() {
mLastDisableFlags = 0;
updateSystemBarAttributes();
@@ -2244,6 +2285,11 @@ public class DisplayPolicy {
final int displayId = getDisplayId();
final int disableFlags = win.getDisableFlags();
final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
+ if (!mRelaunchingSystemBarColorApps.isEmpty()) {
+ // The appearance of system bars might change while relaunching apps. We don't report
+ // the intermediate state to system UI. Otherwise, it might trigger redundant effects.
+ return;
+ }
final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
final boolean isNavbarColorManagedByIme =
@@ -2707,6 +2753,14 @@ public class DisplayPolicy {
pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState=");
pw.println(mTopFullscreenOpaqueWindowState);
}
+ if (!mSystemBarColorApps.isEmpty()) {
+ pw.print(prefix); pw.print("mSystemBarColorApps=");
+ pw.println(mSystemBarColorApps);
+ }
+ if (!mRelaunchingSystemBarColorApps.isEmpty()) {
+ pw.print(prefix); pw.print("mRelaunchingSystemBarColorApps=");
+ pw.println(mRelaunchingSystemBarColorApps);
+ }
if (mNavBarColorWindowCandidate != null) {
pw.print(prefix); pw.print("mNavBarColorWindowCandidate=");
pw.println(mNavBarColorWindowCandidate);
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index ea82417a2389..74a236bd862c 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -501,12 +501,16 @@ final class LetterboxUiController {
if (hasVisibleTaskbar(mainWindow)) {
cropBounds = new Rect(mActivityRecord.getBounds());
+
+ // Rounded corners should be displayed above the taskbar.
+ // It is important to call adjustBoundsForTaskbarUnchecked before offsetTo
+ // because taskbar bounds are in screen coordinates
+ adjustBoundsForTaskbarUnchecked(mainWindow, cropBounds);
+
// Activity bounds are in screen coordinates while (0,0) for activity's surface
// control is at the top left corner of an app window so offsetting bounds
// accordingly.
cropBounds.offsetTo(0, 0);
- // Rounded corners should be displayed above the taskbar.
- adjustBoundsForTaskbarUnchecked(mainWindow, cropBounds);
}
transaction
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 377c5b41fe35..93fcc202368c 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -289,6 +289,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
private final IBinder mFragmentToken;
/**
+ * Whether to delay the call to {@link #updateOrganizedTaskFragmentSurface()} when there is a
+ * configuration change.
+ */
+ private boolean mDelayOrganizedTaskFragmentSurfaceUpdate;
+
+ /**
* Whether to delay the last activity of TaskFragment being immediately removed while finishing.
* This should only be set on a embedded TaskFragment, where the organizer can have the
* opportunity to perform animations and finishing the adjacent TaskFragment.
@@ -2273,35 +2279,41 @@ class TaskFragment extends WindowContainer<WindowContainer> {
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
- // Task will animate differently.
- if (mTaskFragmentOrganizer != null) {
- mTmpPrevBounds.set(getBounds());
- }
-
super.onConfigurationChanged(newParentConfig);
- final boolean shouldStartChangeTransition = shouldStartChangeTransition(mTmpPrevBounds);
- if (shouldStartChangeTransition) {
- initializeChangeTransition(mTmpPrevBounds);
- }
if (mTaskFragmentOrganizer != null) {
- if (mTransitionController.isShellTransitionsEnabled()
- && !mTransitionController.isCollecting(this)) {
- // TaskFragmentOrganizer doesn't have access to the surface for security reasons, so
- // update the surface here if it is not collected by Shell transition.
- updateOrganizedTaskFragmentSurface();
- } else if (!mTransitionController.isShellTransitionsEnabled()
- && !shouldStartChangeTransition) {
- // Update the surface here instead of in the organizer so that we can make sure
- // it can be synced with the surface freezer for legacy app transition.
- updateOrganizedTaskFragmentSurface();
- }
+ updateOrganizedTaskFragmentSurface();
}
sendTaskFragmentInfoChanged();
}
+ void deferOrganizedTaskFragmentSurfaceUpdate() {
+ mDelayOrganizedTaskFragmentSurfaceUpdate = true;
+ }
+
+ void continueOrganizedTaskFragmentSurfaceUpdate() {
+ mDelayOrganizedTaskFragmentSurfaceUpdate = false;
+ updateOrganizedTaskFragmentSurface();
+ }
+
private void updateOrganizedTaskFragmentSurface() {
+ if (mDelayOrganizedTaskFragmentSurfaceUpdate) {
+ return;
+ }
+ if (mTransitionController.isShellTransitionsEnabled()
+ && !mTransitionController.isCollecting(this)) {
+ // TaskFragmentOrganizer doesn't have access to the surface for security reasons, so
+ // update the surface here if it is not collected by Shell transition.
+ updateOrganizedTaskFragmentSurfaceUnchecked();
+ } else if (!mTransitionController.isShellTransitionsEnabled() && !isAnimating()) {
+ // Update the surface here instead of in the organizer so that we can make sure
+ // it can be synced with the surface freezer for legacy app transition.
+ updateOrganizedTaskFragmentSurfaceUnchecked();
+ }
+ }
+
+ private void updateOrganizedTaskFragmentSurfaceUnchecked() {
final SurfaceControl.Transaction t = getSyncTransaction();
updateSurfacePosition(t);
updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */);
@@ -2355,7 +2367,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
/** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */
- private boolean shouldStartChangeTransition(Rect startBounds) {
+ boolean shouldStartChangeTransition(Rect startBounds) {
if (mTaskFragmentOrganizer == null || !canStartChangeTransition()) {
return false;
}
@@ -2375,7 +2387,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
void setSurfaceControl(SurfaceControl sc) {
super.setSurfaceControl(sc);
if (mTaskFragmentOrganizer != null) {
- updateOrganizedTaskFragmentSurface();
+ updateOrganizedTaskFragmentSurfaceUnchecked();
// If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to
// emit the callbacks now.
sendTaskFragmentAppeared();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9c9d751244e6..9db5170897a8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1883,7 +1883,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Make this invalid which indicates a null attached frame.
outAttachedFrame.set(0, 0, -1, -1);
}
- outSizeCompatScale[0] = win.getSizeCompatScale();
+ outSizeCompatScale[0] = win.getSizeCompatScaleForClient();
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 32a110ea530e..007628f49651 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -48,6 +48,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANI
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
@@ -146,6 +147,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
@VisibleForTesting
final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
+ private final Rect mTmpBounds = new Rect();
+
WindowOrganizerController(ActivityTaskManagerService atm) {
mService = atm;
mGlobalLock = atm.mGlobalLock;
@@ -710,7 +713,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
- int effects = 0;
+ int effects = applyChanges(tr, c, null /* errorCallbackToken */);
final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
@@ -725,6 +728,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
effects = TRANSACT_EFFECTS_LIFECYCLE;
}
+ if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING) != 0) {
+ tr.setDragResizing(c.getDragResizing(), DRAG_RESIZE_MODE_FREEFORM);
+ }
+
final int childWindowingMode = c.getActivityWindowingMode();
if (childWindowingMode > -1) {
tr.setActivityWindowingMode(childWindowingMode);
@@ -767,6 +774,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
private int applyDisplayAreaChanges(DisplayArea displayArea,
WindowContainerTransaction.Change c) {
final int[] effects = new int[1];
+ effects[0] = applyChanges(displayArea, c, null /* errorCallbackToken */);
if ((c.getChangeMask()
& WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
@@ -787,6 +795,27 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return effects[0];
}
+ private int applyTaskFragmentChanges(@NonNull TaskFragment taskFragment,
+ @NonNull WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) {
+ if (taskFragment.isEmbeddedTaskFragmentInPip()) {
+ // No override from organizer for embedded TaskFragment in a PIP Task.
+ return 0;
+ }
+
+ // When the TaskFragment is resized, we may want to create a change transition for it, for
+ // which we want to defer the surface update until we determine whether or not to start
+ // change transition.
+ mTmpBounds.set(taskFragment.getBounds());
+ taskFragment.deferOrganizedTaskFragmentSurfaceUpdate();
+ final int effects = applyChanges(taskFragment, c, errorCallbackToken);
+ if (taskFragment.shouldStartChangeTransition(mTmpBounds)) {
+ taskFragment.initializeChangeTransition(mTmpBounds);
+ }
+ taskFragment.continueOrganizedTaskFragmentSurfaceUpdate();
+ mTmpBounds.set(0, 0, 0, 0);
+ return effects;
+ }
+
private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
@NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,
@@ -1452,20 +1481,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
private int applyWindowContainerChange(WindowContainer wc,
WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) {
sanitizeWindowContainer(wc);
- if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbeddedTaskFragmentInPip()) {
- // No override from organizer for embedded TaskFragment in a PIP Task.
- return 0;
- }
-
- int effects = applyChanges(wc, c, errorCallbackToken);
-
- if (wc instanceof DisplayArea) {
- effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c);
- } else if (wc instanceof Task) {
- effects |= applyTaskChanges(wc.asTask(), c);
+ if (wc.asDisplayArea() != null) {
+ return applyDisplayAreaChanges(wc.asDisplayArea(), c);
+ } else if (wc.asTask() != null) {
+ return applyTaskChanges(wc.asTask(), c);
+ } else if (wc.asTaskFragment() != null) {
+ return applyTaskFragmentChanges(wc.asTaskFragment(), c, errorCallbackToken);
+ } else {
+ return applyChanges(wc, c, errorCallbackToken);
}
-
- return effects;
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d4c1abfa8d24..45606f965858 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1265,8 +1265,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mGlobalScale = mInvGlobalScale = mSizeCompatScale = 1f;
}
- float getSizeCompatScale() {
- return mSizeCompatScale;
+ float getSizeCompatScaleForClient() {
+ // If the size compat scale is because of the size compat bounds, we only scale down its
+ // coordinates at the server side without letting the client know.
+ return mToken.hasSizeCompatBounds() ? 1f : mSizeCompatScale;
}
/**
@@ -1535,10 +1537,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mWmService.makeWindowFreezingScreenIfNeededLocked(this);
// If the orientation is changing, or we're starting or ending a drag resizing action,
- // then we need to hold off on unfreezing the display until this window has been
- // redrawn; to do that, we need to go through the process of getting informed by the
- // application when it has finished drawing.
- if (getOrientationChanging() || dragResizingChanged) {
+ // or we're resizing an embedded Activity, then we need to hold off on unfreezing the
+ // display until this window has been redrawn; to do that, we need to go through the
+ // process of getting informed by the application when it has finished drawing.
+ if (getOrientationChanging() || dragResizingChanged
+ || isEmbeddedActivityResizeChanged()) {
if (dragResizingChanged) {
ProtoLog.v(WM_DEBUG_RESIZE,
"Resize start waiting for draw, "
@@ -3863,7 +3866,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
outFrames.attachedFrame.scale(mInvGlobalScale);
}
}
- outFrames.sizeCompatScale = mSizeCompatScale;
+
+ outFrames.sizeCompatScale = getSizeCompatScaleForClient();
// Note: in the cases where the window is tied to an activity, we should not send a
// configuration update when the window has requested to be hidden. Doing so can lead to
@@ -4144,6 +4148,20 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mActivityRecord == null || mActivityRecord.isFullyTransparentBarAllowed(frame);
}
+ /**
+ * Whether this window belongs to a resizing embedded activity.
+ */
+ private boolean isEmbeddedActivityResizeChanged() {
+ if (mActivityRecord == null || !isVisibleRequested()) {
+ // No need to update if the window is in the background.
+ return false;
+ }
+
+ final TaskFragment embeddedTaskFragment = mActivityRecord.getOrganizedTaskFragment();
+ return embeddedTaskFragment != null
+ && mDisplayContent.mChangingContainers.contains(embeddedTaskFragment);
+ }
+
boolean isDragResizeChanged() {
return mDragResizing != computeDragResizing();
}
@@ -6021,7 +6039,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final long duration =
SystemClock.elapsedRealtime() - mActivityRecord.mRelaunchStartTime;
Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
- mActivityRecord.mRelaunchStartTime = 0;
+ mActivityRecord.finishOrAbortReplacingWindow();
}
if (mActivityRecord != null && mAttrs.type == TYPE_APPLICATION_STARTING) {
mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
@@ -6113,8 +6131,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mRedrawForSyncReported) {
return false;
}
- // TODO(b/233286785): Remove mIsWallpaper once WallpaperService handles syncId of relayout.
- if (mInRelayout && !mIsWallpaper) {
+ if (mInRelayout && mPrepareSyncSeqId > 0) {
// The last sync seq id will return to the client, so there is no need to request the
// client to redraw.
return false;
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 9c615d140e85..ed369c016770 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -764,11 +764,13 @@ public class LocalDisplayAdapterTest {
@Test
public void testGetSystemPreferredDisplayMode() throws Exception {
SurfaceControl.DisplayMode displayMode1 = createFakeDisplayMode(0, 1920, 1080, 60f);
- // preferred mode
+ // system preferred mode
SurfaceControl.DisplayMode displayMode2 = createFakeDisplayMode(1, 3840, 2160, 60f);
+ // user preferred mode
+ SurfaceControl.DisplayMode displayMode3 = createFakeDisplayMode(2, 1920, 1080, 30f);
SurfaceControl.DisplayMode[] modes =
- new SurfaceControl.DisplayMode[]{displayMode1, displayMode2};
+ new SurfaceControl.DisplayMode[]{displayMode1, displayMode2, displayMode3};
FakeDisplay display = new FakeDisplay(PORT_A, modes, 0, 1);
setUpDisplay(display);
updateAvailableDisplays();
@@ -780,24 +782,43 @@ public class LocalDisplayAdapterTest {
DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
0).getDisplayDeviceInfoLocked();
-
assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
-
Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
+ assertThat(matches(defaultMode, displayMode1)).isTrue();
+
+ // Set the user preferred display mode
+ mListener.addedDisplays.get(0).setUserPreferredDisplayModeLocked(
+ new Display.Mode(
+ displayMode3.width, displayMode3.height, displayMode3.refreshRate));
+ updateAvailableDisplays();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ displayDeviceInfo = mListener.addedDisplays.get(
+ 0).getDisplayDeviceInfoLocked();
+ defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
+ assertThat(matches(defaultMode, displayMode3)).isTrue();
+
+ // clear the user preferred mode
+ mListener.addedDisplays.get(0).setUserPreferredDisplayModeLocked(null);
+ updateAvailableDisplays();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ displayDeviceInfo = mListener.addedDisplays.get(
+ 0).getDisplayDeviceInfoLocked();
+ defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
assertThat(matches(defaultMode, displayMode2)).isTrue();
- // Change the display and add new preferred mode
- SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(2, 2340, 1080, 60f);
- modes = new SurfaceControl.DisplayMode[]{displayMode1, displayMode2, addedDisplayInfo};
+ // Change the display and add new system preferred mode
+ SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3, 2340, 1080, 20f);
+ modes = new SurfaceControl.DisplayMode[]{
+ displayMode1, displayMode2, displayMode3, addedDisplayInfo};
display.dynamicInfo.supportedDisplayModes = modes;
- display.dynamicInfo.preferredBootDisplayMode = 2;
+ display.dynamicInfo.preferredBootDisplayMode = 3;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
assertTrue(mListener.traversalRequested);
assertThat(mListener.addedDisplays.size()).isEqualTo(1);
- assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays.size()).isEqualTo(3);
DisplayDevice displayDevice = mListener.changedDisplays.get(0);
displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index cd4af0a581f8..666d4010e921 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -372,6 +372,7 @@ public class FingerprintAuthenticationClientTest {
@Test
public void fingerprintPowerIgnoresAuthInWindow() throws Exception {
when(mSensorProps.isAnySidefpsType()).thenReturn(true);
+ when(mHal.authenticate(anyLong())).thenReturn(mCancellationSignal);
final FingerprintAuthenticationClient client = createClient(1);
client.start(mCallback);
@@ -382,11 +383,13 @@ public class FingerprintAuthenticationClientTest {
mLooper.dispatchAll();
verify(mCallback).onClientFinished(any(), eq(false));
+ verify(mCancellationSignal).cancel();
}
@Test
public void fingerprintAuthIgnoredWaitingForPower() throws Exception {
when(mSensorProps.isAnySidefpsType()).thenReturn(true);
+ when(mHal.authenticate(anyLong())).thenReturn(mCancellationSignal);
final FingerprintAuthenticationClient client = createClient(1);
client.start(mCallback);
@@ -397,11 +400,13 @@ public class FingerprintAuthenticationClientTest {
mLooper.dispatchAll();
verify(mCallback).onClientFinished(any(), eq(false));
+ verify(mCancellationSignal).cancel();
}
@Test
- public void fingerprintAuthSucceedsAfterPowerWindow() throws Exception {
+ public void fingerprintAuthFailsWhenAuthAfterPower() throws Exception {
when(mSensorProps.isAnySidefpsType()).thenReturn(true);
+ when(mHal.authenticate(anyLong())).thenReturn(mCancellationSignal);
final FingerprintAuthenticationClient client = createClient(1);
client.start(mCallback);
@@ -415,7 +420,9 @@ public class FingerprintAuthenticationClientTest {
mLooper.moveTimeForward(1000);
mLooper.dispatchAll();
- verify(mCallback).onClientFinished(any(), eq(true));
+ verify(mCallback, never()).onClientFinished(any(), eq(true));
+ verify(mCallback).onClientFinished(any(), eq(false));
+ when(mHal.authenticateWithContext(anyLong(), any())).thenReturn(mCancellationSignal);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index beaa6e066d7d..7df4b57b6cf8 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -7549,43 +7549,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception {
- mService.isSystemUid = true;
- ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
- when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
- .thenReturn(true);
- mService.setZenHelper(mockZenModeHelper);
- ComponentName owner = new ComponentName("android", "ProviderName");
- ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
- boolean isEnabled = true;
- AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
- zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "com.android.settings");
-
- // verify that zen mode helper gets passed in a package name of "android"
- verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString());
- }
-
- @Test
- public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception {
- mService.isSystemUid = false;
- ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
- when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
- .thenReturn(true);
- mService.setZenHelper(mockZenModeHelper);
- ComponentName owner = new ComponentName("android", "ProviderName");
- ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
- boolean isEnabled = true;
- AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
- zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "another.package");
-
- // verify that zen mode helper gets passed in the package name from the arg, not the owner
- verify(mockZenModeHelper).addAutomaticZenRule(
- eq("another.package"), eq(rule), anyString());
- }
-
- @Test
public void testAreNotificationsEnabledForPackage() throws Exception {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 2ccdcaace8bf..4550b56f6fd0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1672,36 +1672,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- public void testAddAutomaticZenRule_claimedSystemOwner() {
- // Make sure anything that claims to have a "system" owner but not actually part of the
- // system package still gets limited on number of rules
- for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) {
- ScheduleInfo si = new ScheduleInfo();
- si.startHour = i;
- AutomaticZenRule zenRule = new AutomaticZenRule("name" + i,
- new ComponentName("android", "ScheduleConditionProvider" + i),
- null, // configuration activity
- ZenModeConfig.toScheduleConditionId(si),
- new ZenPolicy.Builder().build(),
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
- assertNotNull(id);
- }
- try {
- AutomaticZenRule zenRule = new AutomaticZenRule("name",
- new ComponentName("android", "ScheduleConditionProviderFinal"),
- null, // configuration activity
- ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
- new ZenPolicy.Builder().build(),
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
- fail("allowed too many rules to be created");
- } catch (IllegalArgumentException e) {
- // yay
- }
- }
-
- @Test
public void testAddAutomaticZenRule_CA() {
AutomaticZenRule zenRule = new AutomaticZenRule("name",
null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 496f6817bb08..6fe2d2cbe9d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1131,6 +1131,26 @@ public class ActivityStarterTests extends WindowTestsBase {
}
@Test
+ public void testRecycleTaskWakeUpWhenDreaming() {
+ doNothing().when(mWm.mAtmService.mTaskSupervisor).wakeUp(anyString());
+ doReturn(true).when(mWm.mAtmService).isDreaming();
+ final ActivityStarter starter = prepareStarter(0 /* flags */);
+ final ActivityRecord target = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ starter.mStartActivity = target;
+ target.mVisibleRequested = false;
+ target.setTurnScreenOn(true);
+ // Assume the flag was consumed by relayout.
+ target.setCurrentLaunchCanTurnScreenOn(false);
+ startActivityInner(starter, target, null /* source */, null /* options */,
+ null /* inTask */, null /* inTaskFragment */);
+ // The flag should be set again when resuming (from recycleTask) the target as top.
+ assertTrue(target.currentLaunchCanTurnScreenOn());
+ // In real case, dream activity has a higher priority (TaskDisplayArea#getPriority) that
+ // will be put at a higher z-order. So it relies on wakeUp() to be dismissed.
+ verify(mWm.mAtmService.mTaskSupervisor).wakeUp(anyString());
+ }
+
+ @Test
public void testTargetTaskInSplitScreen() {
final ActivityStarter starter =
prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetRootTask */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 181e81d70dfa..06eea298600c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -25,6 +25,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
@@ -69,6 +70,7 @@ import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.times;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -84,6 +86,7 @@ import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
+import android.view.InsetsSource;
import android.view.InsetsVisibilities;
import android.view.WindowManager;
@@ -103,6 +106,9 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.List;
/**
* Tests for Size Compatibility mode.
@@ -2369,6 +2375,48 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testLetterboxDetailsForTaskBar_letterboxNotOverlappingTaskBar() {
+ mAtm.mDevEnableNonResizableMultiWindow = true;
+ final int screenHeight = 2200;
+ final int screenWidth = 1400;
+ final int taskbarHeight = 200;
+ setUpDisplaySizeWithApp(screenWidth, screenHeight);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+ // Move first activity to split screen which takes half of the screen.
+ organizer.mPrimary.setBounds(0, screenHeight / 2, screenWidth, screenHeight);
+ organizer.putTaskToPrimary(mTask, true);
+
+ final InsetsSource navSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ navSource.setFrame(new Rect(0, screenHeight - taskbarHeight, screenWidth, screenHeight));
+
+ mActivity.mWmService.mLetterboxConfiguration.setLetterboxActivityCornersRadius(15);
+
+ final WindowState w1 = addWindowToActivity(mActivity);
+ w1.mAboveInsetsState.addSource(navSource);
+
+ // Prepare unresizable activity with max aspect ratio
+ prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
+
+ // Refresh the letterboxes
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+
+ final ArgumentCaptor<Rect> cropCapturer = ArgumentCaptor.forClass(Rect.class);
+ verify(mTransaction, times(2)).setWindowCrop(
+ eq(w1.getSurfaceControl()),
+ cropCapturer.capture()
+ );
+ final List<Rect> capturedCrops = cropCapturer.getAllValues();
+
+ final int expectedHeight = screenHeight / 2 - taskbarHeight;
+ assertEquals(2, capturedCrops.size());
+ assertEquals(expectedHeight, capturedCrops.get(0).bottom);
+ assertEquals(expectedHeight, capturedCrops.get(1).bottom);
+ }
+
+ @Test
public void testSplitScreenLetterboxDetailsForStatusBar_twoLetterboxedApps() {
mAtm.mDevEnableNonResizableMultiWindow = true;
setUpDisplaySizeWithApp(2800, 1000);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 83f17897eb62..3ff2c0e0d024 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -118,10 +118,13 @@ public class TaskFragmentTest extends WindowTestsBase {
doReturn(true).when(mTaskFragment).isVisibleRequested();
clearInvocations(mTransaction);
+ mTaskFragment.deferOrganizedTaskFragmentSurfaceUpdate();
mTaskFragment.setBounds(endBounds);
+ assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds));
+ mTaskFragment.initializeChangeTransition(startBounds);
+ mTaskFragment.continueOrganizedTaskFragmentSurfaceUpdate();
// Surface reset when prepare transition.
- verify(mTaskFragment).initializeChangeTransition(startBounds);
verify(mTransaction).setPosition(mLeash, 0, 0);
verify(mTransaction).setWindowCrop(mLeash, 0, 0);
@@ -166,7 +169,7 @@ public class TaskFragmentTest extends WindowTestsBase {
mTaskFragment.setBounds(endBounds);
- verify(mTaskFragment, never()).initializeChangeTransition(any());
+ assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds));
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 9bcc1367f8ab..04d873453b2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -95,6 +95,8 @@ import android.view.InsetsState;
import android.view.InsetsVisibilities;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentOrganizer;
import androidx.test.filters.SmallTest;
@@ -798,6 +800,39 @@ public class WindowStateTests extends WindowTestsBase {
}
@Test
+ public void testEmbeddedActivityResizing_clearAllDrawn() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+ ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
+ final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION, embeddedActivity,
+ "App window");
+ doReturn(true).when(embeddedActivity).isVisible();
+ embeddedActivity.mVisibleRequested = true;
+ makeWindowVisible(win);
+ win.mLayoutSeq = win.getDisplayContent().mLayoutSeq;
+ // Set the bounds twice:
+ // 1. To make sure there is no orientation change after #reportResized, which can also cause
+ // #clearAllDrawn.
+ // 2. Make #isLastConfigReportedToClient to be false after #reportResized, so it can process
+ // to check if we need redraw.
+ embeddedTf.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ embeddedTf.setBounds(0, 0, 1000, 2000);
+ win.reportResized();
+ embeddedTf.setBounds(500, 0, 1000, 2000);
+
+ // Clear all drawn when the embedded TaskFragment is in mDisplayContent.mChangingContainers.
+ win.updateResizingWindowIfNeeded();
+ verify(embeddedActivity, never()).clearAllDrawn();
+
+ mDisplayContent.mChangingContainers.add(embeddedTf);
+ win.updateResizingWindowIfNeeded();
+ verify(embeddedActivity).clearAllDrawn();
+ }
+
+ @Test
public void testCantReceiveTouchWhenAppTokenHiddenRequested() {
final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
win0.mActivityRecord.mVisibleRequested = false;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java
new file mode 100644
index 000000000000..f9211181c924
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.voiceinteraction;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+
+import static com.android.server.voiceinteraction.HotwordDetectionConnection.DEBUG;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.media.permission.Identity;
+import android.os.ParcelFileDescriptor;
+import android.service.voice.HotwordAudioStream;
+import android.service.voice.HotwordDetectedResult;
+import android.util.Pair;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+final class HotwordAudioStreamManager {
+
+ private static final String TAG = "HotwordAudioStreamManager";
+ private static final String OP_MESSAGE = "Streaming hotword audio to VoiceInteractionService";
+ private static final String TASK_ID_PREFIX = "HotwordDetectedResult@";
+ private static final String THREAD_NAME_PREFIX = "Copy-";
+
+ private final AppOpsManager mAppOpsManager;
+ private final Identity mVoiceInteractorIdentity;
+ private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
+
+ HotwordAudioStreamManager(@NonNull AppOpsManager appOpsManager,
+ @NonNull Identity voiceInteractorIdentity) {
+ mAppOpsManager = appOpsManager;
+ mVoiceInteractorIdentity = voiceInteractorIdentity;
+ }
+
+ /**
+ * Starts copying the audio streams in the given {@link HotwordDetectedResult}.
+ * <p>
+ * The returned {@link HotwordDetectedResult} is identical the one that was passed in, except
+ * that the {@link ParcelFileDescriptor}s within {@link HotwordDetectedResult#getAudioStreams()}
+ * are replaced with descriptors from pipes managed by {@link HotwordAudioStreamManager}. The
+ * returned value should be passed on to the client (i.e., the voice interactor).
+ * </p>
+ *
+ * @throws IOException If there was an error creating the managed pipe.
+ */
+ @NonNull
+ public HotwordDetectedResult startCopyingAudioStreams(@NonNull HotwordDetectedResult result)
+ throws IOException {
+ List<HotwordAudioStream> audioStreams = result.getAudioStreams();
+ if (audioStreams.isEmpty()) {
+ return result;
+ }
+
+ List<HotwordAudioStream> newAudioStreams = new ArrayList<>(audioStreams.size());
+ List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> sourcesAndSinks = new ArrayList<>(
+ audioStreams.size());
+ for (HotwordAudioStream audioStream : audioStreams) {
+ ParcelFileDescriptor[] clientPipe = ParcelFileDescriptor.createReliablePipe();
+ ParcelFileDescriptor clientAudioSource = clientPipe[0];
+ ParcelFileDescriptor clientAudioSink = clientPipe[1];
+ HotwordAudioStream newAudioStream =
+ audioStream.buildUpon().setAudioStreamParcelFileDescriptor(
+ clientAudioSource).build();
+ newAudioStreams.add(newAudioStream);
+
+ ParcelFileDescriptor serviceAudioSource =
+ audioStream.getAudioStreamParcelFileDescriptor();
+ sourcesAndSinks.add(new Pair<>(serviceAudioSource, clientAudioSink));
+ }
+
+ String resultTaskId = TASK_ID_PREFIX + System.identityHashCode(result);
+ mExecutorService.execute(new HotwordDetectedResultCopyTask(resultTaskId, sourcesAndSinks));
+
+ return result.buildUpon().setAudioStreams(newAudioStreams).build();
+ }
+
+ private class HotwordDetectedResultCopyTask implements Runnable {
+ private final String mResultTaskId;
+ private final List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> mSourcesAndSinks;
+ private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
+
+ HotwordDetectedResultCopyTask(String resultTaskId,
+ List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> sourcesAndSinks) {
+ mResultTaskId = resultTaskId;
+ mSourcesAndSinks = sourcesAndSinks;
+ }
+
+ @Override
+ public void run() {
+ Thread.currentThread().setName(THREAD_NAME_PREFIX + mResultTaskId);
+ int size = mSourcesAndSinks.size();
+ List<SingleAudioStreamCopyTask> tasks = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ Pair<ParcelFileDescriptor, ParcelFileDescriptor> sourceAndSink =
+ mSourcesAndSinks.get(i);
+ ParcelFileDescriptor serviceAudioSource = sourceAndSink.first;
+ ParcelFileDescriptor clientAudioSink = sourceAndSink.second;
+ String streamTaskId = mResultTaskId + "@" + i;
+ tasks.add(new SingleAudioStreamCopyTask(streamTaskId, serviceAudioSource,
+ clientAudioSink));
+ }
+
+ if (mAppOpsManager.startOpNoThrow(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
+ mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+ mVoiceInteractorIdentity.attributionTag, OP_MESSAGE) == MODE_ALLOWED) {
+ try {
+ // TODO(b/244599891): Set timeout, close after inactivity
+ mExecutorService.invokeAll(tasks);
+ } catch (InterruptedException e) {
+ Slog.e(TAG, mResultTaskId + ": Task was interrupted", e);
+ bestEffortPropagateError(e.getMessage());
+ } finally {
+ mAppOpsManager.finishOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
+ mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+ mVoiceInteractorIdentity.attributionTag);
+ }
+ } else {
+ bestEffortPropagateError(
+ "Failed to obtain RECORD_AUDIO_HOTWORD permission for "
+ + SoundTriggerSessionPermissionsDecorator.toString(
+ mVoiceInteractorIdentity));
+ }
+ }
+
+ private void bestEffortPropagateError(@NonNull String errorMessage) {
+ try {
+ for (Pair<ParcelFileDescriptor, ParcelFileDescriptor> sourceAndSink :
+ mSourcesAndSinks) {
+ ParcelFileDescriptor serviceAudioSource = sourceAndSink.first;
+ ParcelFileDescriptor clientAudioSink = sourceAndSink.second;
+ serviceAudioSource.closeWithError(errorMessage);
+ clientAudioSink.closeWithError(errorMessage);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, mResultTaskId + ": Failed to propagate error", e);
+ }
+ }
+ }
+
+ private static class SingleAudioStreamCopyTask implements Callable<Void> {
+ // TODO: Make this buffer size customizable from updateState()
+ private static final int COPY_BUFFER_LENGTH = 1_024;
+
+ private final String mStreamTaskId;
+ private final ParcelFileDescriptor mAudioSource;
+ private final ParcelFileDescriptor mAudioSink;
+
+ SingleAudioStreamCopyTask(String streamTaskId, ParcelFileDescriptor audioSource,
+ ParcelFileDescriptor audioSink) {
+ mStreamTaskId = streamTaskId;
+ mAudioSource = audioSource;
+ mAudioSink = audioSink;
+ }
+
+ @Override
+ public Void call() throws Exception {
+ Thread.currentThread().setName(THREAD_NAME_PREFIX + mStreamTaskId);
+
+ // Note: We are intentionally NOT using try-with-resources here. If we did,
+ // the ParcelFileDescriptors will be automatically closed WITHOUT errors before we go
+ // into the IOException-catch block. We want to propagate the error while closing the
+ // PFDs.
+ InputStream fis = null;
+ OutputStream fos = null;
+ try {
+ fis = new ParcelFileDescriptor.AutoCloseInputStream(mAudioSource);
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(mAudioSink);
+ byte[] buffer = new byte[COPY_BUFFER_LENGTH];
+ while (true) {
+ if (Thread.interrupted()) {
+ Slog.e(TAG,
+ mStreamTaskId + ": SingleAudioStreamCopyTask task was interrupted");
+ break;
+ }
+
+ int bytesRead = fis.read(buffer);
+ if (bytesRead < 0) {
+ Slog.i(TAG, mStreamTaskId + ": Reached end of audio stream");
+ break;
+ }
+ if (bytesRead > 0) {
+ if (DEBUG) {
+ // TODO(b/244599440): Add proper logging
+ Slog.d(TAG, mStreamTaskId + ": Copied " + bytesRead
+ + " bytes from audio stream. First 20 bytes=" + Arrays.toString(
+ Arrays.copyOfRange(buffer, 0, 20)));
+ }
+ fos.write(buffer, 0, bytesRead);
+ }
+ // TODO(b/244599891): Close PFDs after inactivity
+ }
+ } catch (IOException e) {
+ mAudioSource.closeWithError(e.getMessage());
+ mAudioSink.closeWithError(e.getMessage());
+ Slog.e(TAG, mStreamTaskId + ": Failed to copy audio stream", e);
+ } finally {
+ if (fis != null) {
+ fis.close();
+ }
+ if (fos != null) {
+ fos.close();
+ }
+ }
+
+ return null;
+ }
+ }
+
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index a6e1a3256cb6..ee8070888725 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -59,6 +59,7 @@ import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPH
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.attention.AttentionManagerInternal;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
@@ -137,6 +138,7 @@ final class HotwordDetectionConnection {
// The error codes are used for onError callback
private static final int HOTWORD_DETECTION_SERVICE_DIED = -1;
private static final int CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION = -2;
+ private static final int CALLBACK_ONDETECTED_STREAM_COPY_ERROR = -4;
// Hotword metrics
private static final int METRICS_INIT_UNKNOWN_TIMEOUT =
@@ -168,6 +170,8 @@ final class HotwordDetectionConnection {
// TODO: This may need to be a Handler(looper)
private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
+ private final AppOpsManager mAppOpsManager;
+ private final HotwordAudioStreamManager mHotwordAudioStreamManager;
@Nullable private final ScheduledFuture<?> mCancellationTaskFuture;
private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false);
private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied;
@@ -228,6 +232,9 @@ final class HotwordDetectionConnection {
mContext = context;
mVoiceInteractionServiceUid = voiceInteractionServiceUid;
mVoiceInteractorIdentity = voiceInteractorIdentity;
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+ mHotwordAudioStreamManager = new HotwordAudioStreamManager(mAppOpsManager,
+ mVoiceInteractorIdentity);
mDetectionComponentName = serviceName;
mUser = userId;
mCallback = callback;
@@ -482,13 +489,19 @@ final class HotwordDetectionConnection {
return;
}
saveProximityMetersToBundle(result);
- mSoftwareCallback.onDetected(result, null, null);
- if (result != null) {
- Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result)
- + " bits from hotword trusted process");
- if (mDebugHotwordLogging) {
- Slog.i(TAG, "Egressed detected result: " + result);
- }
+ HotwordDetectedResult newResult;
+ try {
+ newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+ } catch (IOException e) {
+ // TODO: Write event
+ mSoftwareCallback.onError();
+ return;
+ }
+ mSoftwareCallback.onDetected(newResult, null, null);
+ Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult)
+ + " bits from hotword trusted process");
+ if (mDebugHotwordLogging) {
+ Slog.i(TAG, "Egressed detected result: " + newResult);
}
}
}
@@ -660,6 +673,7 @@ final class HotwordDetectionConnection {
try {
enforcePermissionsForDataDelivery();
} catch (SecurityException e) {
+ Slog.i(TAG, "Ignoring #onDetected due to a SecurityException", e);
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
mDetectorType,
METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION);
@@ -667,13 +681,19 @@ final class HotwordDetectionConnection {
return;
}
saveProximityMetersToBundle(result);
- externalCallback.onKeyphraseDetected(recognitionEvent, result);
- if (result != null) {
- Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result)
- + " bits from hotword trusted process");
- if (mDebugHotwordLogging) {
- Slog.i(TAG, "Egressed detected result: " + result);
- }
+ HotwordDetectedResult newResult;
+ try {
+ newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+ } catch (IOException e) {
+ // TODO: Write event
+ externalCallback.onError(CALLBACK_ONDETECTED_STREAM_COPY_ERROR);
+ return;
+ }
+ externalCallback.onKeyphraseDetected(recognitionEvent, newResult);
+ Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult)
+ + " bits from hotword trusted process");
+ if (mDebugHotwordLogging) {
+ Slog.i(TAG, "Egressed detected result: " + newResult);
}
}
}
@@ -757,6 +777,7 @@ final class HotwordDetectionConnection {
}
private void restartProcessLocked() {
+ // TODO(b/244598068): Check HotwordAudioStreamManager first
Slog.v(TAG, "Restarting hotword detection process");
ServiceConnection oldConnection = mRemoteHotwordDetectionService;
HotwordDetectionServiceIdentity previousIdentity = mIdentity;
@@ -991,16 +1012,24 @@ final class HotwordDetectionConnection {
callback.onError();
return;
}
- callback.onDetected(triggerResult, null /* audioFormat */,
+ HotwordDetectedResult newResult;
+ try {
+ newResult =
+ mHotwordAudioStreamManager.startCopyingAudioStreams(
+ triggerResult);
+ } catch (IOException e) {
+ // TODO: Write event
+ callback.onError();
+ return;
+ }
+ callback.onDetected(newResult, null /* audioFormat */,
null /* audioStream */);
- if (triggerResult != null) {
- Slog.i(TAG, "Egressed "
- + HotwordDetectedResult.getUsageSize(triggerResult)
- + " bits from hotword trusted process");
- if (mDebugHotwordLogging) {
- Slog.i(TAG,
- "Egressed detected result: " + triggerResult);
- }
+ Slog.i(TAG, "Egressed "
+ + HotwordDetectedResult.getUsageSize(newResult)
+ + " bits from hotword trusted process");
+ if (mDebugHotwordLogging) {
+ Slog.i(TAG,
+ "Egressed detected result: " + newResult);
}
}
});
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cf6d681d596a..83bc0fc0298f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1180,7 +1180,10 @@ public class CarrierConfigManager {
"carrier_data_call_retry_network_requested_max_count_int";
/**
- * Data call setup permanent failure causes by the carrier
+ * Data call setup permanent failure causes by the carrier.
+ *
+ * This API key was added in mistake and is not used anymore by the telephony data
+ * frameworks.
*/
public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS =
"carrier_data_call_permanent_failure_strings";
@@ -8308,7 +8311,8 @@ public class CarrierConfigManager {
*
* The syntax of the retry rule:
* 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network capabilities
- * are supported.
+ * are supported. If the capabilities are not specified, then the retry rule only applies
+ * to the current failed APN used in setup data call request.
* "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
*
* 2. Retry based on {@link DataFailCause}
@@ -8319,15 +8323,16 @@ public class CarrierConfigManager {
* "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...],
* [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
*
+ * 4. Permanent fail causes (no timer-based retry) on the current failed APN. Retry interval
+ * is specified for retrying the next available APN.
+ * "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|65543|65547|
+ * 2252|2253|2254, retry_interval=2500"
+ *
* For example,
* "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached
* network request is emergency, then retry data network setup every 1 second for up to 20
* times.
*
- * "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|2254
- * , maximum_retries=0" means for those fail causes, never retry with timers. Note that
- * when environment changes, retry can still happen.
- *
* "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
* "5000|10000|15000|20000|40000|60000|120000|240000|600000|1200000|1800000"
* "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
@@ -9205,8 +9210,13 @@ public class CarrierConfigManager {
sDefaults.putStringArray(
KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY, new String[] {
"capabilities=eims, retry_interval=1000, maximum_retries=20",
- "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2252|"
- + "2253|2254, maximum_retries=0", // No retry for those causes
+ // Permanent fail causes. When setup data call fails with the following
+ // fail causes, telephony data frameworks will stop timer-based retry on
+ // the failed APN until power cycle, APM, or some special events. Note that
+ // even though timer-based retry is not performed, condition-based (RAT
+ // changes, registration state changes) retry can still happen.
+ "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|"
+ + "-3|65543|65547|2252|2253|2254, retry_interval=2500",
"capabilities=mms|supl|cbs, retry_interval=2000",
"capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
+ "5000|10000|15000|20000|40000|60000|120000|240000|"
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b6f86527b747..105fc4c38c13 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15380,11 +15380,28 @@ public class TelephonyManager {
public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2;
/**
+ * Allow switching mobile data to the non-default SIM if the non-default SIM has better
+ * availability.
+ *
+ * This is used for temporarily allowing data on the non-default data SIM when on-default SIM
+ * has better availability on DSDS devices, where better availability means strong
+ * signal/connectivity.
+ * If this policy is enabled, data will be temporarily enabled on the non-default data SIM,
+ * including during any voice calls(equivalent to enabling
+ * {@link #MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL}).
+ *
+ * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
+ * @hide
+ */
+ public static final int MOBILE_DATA_POLICY_AUTO_DATA_SWITCH = 3;
+
+ /**
* @hide
*/
@IntDef(prefix = { "MOBILE_DATA_POLICY_" }, value = {
MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL,
MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED,
+ MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface MobileDataPolicy { }
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 73aff4351785..a834e2bbd0d1 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -468,14 +468,14 @@ public final class DataCallResponse implements Parcelable {
final boolean isQosBearerSessionsSame =
(mQosBearerSessions == null || other.mQosBearerSessions == null)
? mQosBearerSessions == other.mQosBearerSessions
- : mQosBearerSessions.size() == other.mQosBearerSessions.size()
- && mQosBearerSessions.containsAll(other.mQosBearerSessions);
+ : (mQosBearerSessions.size() == other.mQosBearerSessions.size()
+ && mQosBearerSessions.containsAll(other.mQosBearerSessions));
final boolean isTrafficDescriptorsSame =
(mTrafficDescriptors == null || other.mTrafficDescriptors == null)
? mTrafficDescriptors == other.mTrafficDescriptors
- : mTrafficDescriptors.size() == other.mTrafficDescriptors.size()
- && mTrafficDescriptors.containsAll(other.mTrafficDescriptors);
+ : (mTrafficDescriptors.size() == other.mTrafficDescriptors.size()
+ && mTrafficDescriptors.containsAll(other.mTrafficDescriptors));
return mCause == other.mCause
&& mSuggestedRetryTime == other.mSuggestedRetryTime
@@ -504,10 +504,35 @@ public final class DataCallResponse implements Parcelable {
@Override
public int hashCode() {
+ // Generate order-independent hashes for lists
+ int addressesHash = mAddresses.stream()
+ .map(LinkAddress::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
+ int dnsAddressesHash = mDnsAddresses.stream()
+ .map(InetAddress::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
+ int gatewayAddressesHash = mGatewayAddresses.stream()
+ .map(InetAddress::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
+ int pcscfAddressesHash = mPcscfAddresses.stream()
+ .map(InetAddress::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
+ int qosBearerSessionsHash = mQosBearerSessions.stream()
+ .map(QosBearerSession::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
+ int trafficDescriptorsHash = mTrafficDescriptors.stream()
+ .map(TrafficDescriptor::hashCode)
+ .mapToInt(Integer::intValue)
+ .sum();
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
- mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
- mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
- mQosBearerSessions, mSliceInfo, mTrafficDescriptors);
+ mInterfaceName, addressesHash, dnsAddressesHash, gatewayAddressesHash,
+ pcscfAddressesHash, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
+ mDefaultQos, qosBearerSessionsHash, mSliceInfo, trafficDescriptorsHash);
}
@Override
@@ -816,8 +841,8 @@ public final class DataCallResponse implements Parcelable {
/**
* Set pdu session id.
* <p/>
- * The id must be between 1 and 15 when linked to a pdu session. If no pdu session
- * exists for the current data call, the id must be set to {@link PDU_SESSION_ID_NOT_SET}.
+ * The id must be between 1 and 15 when linked to a pdu session. If no pdu session
+ * exists for the current data call, the id must be set to {@link #PDU_SESSION_ID_NOT_SET}.
*
* @param pduSessionId Pdu Session Id of the data call.
* @return The same instance of the builder.
@@ -858,6 +883,7 @@ public final class DataCallResponse implements Parcelable {
*/
public @NonNull Builder setQosBearerSessions(
@NonNull List<QosBearerSession> qosBearerSessions) {
+ Objects.requireNonNull(qosBearerSessions);
mQosBearerSessions = qosBearerSessions;
return this;
}
@@ -891,6 +917,7 @@ public final class DataCallResponse implements Parcelable {
*/
public @NonNull Builder setTrafficDescriptors(
@NonNull List<TrafficDescriptor> trafficDescriptors) {
+ Objects.requireNonNull(trafficDescriptors);
mTrafficDescriptors = trafficDescriptors;
return this;
}