summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java42
-rw-r--r--api/api.go6
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java120
-rw-r--r--core/java/android/content/pm/PackageParser.java6
-rw-r--r--core/java/android/content/pm/TEST_MAPPING66
-rw-r--r--core/java/android/content/pm/verify/domain/TEST_MAPPING3
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java7
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java3
-rw-r--r--core/java/android/provider/Settings.java49
-rw-r--r--core/java/android/service/dreams/DreamService.java8
-rw-r--r--core/java/android/view/Choreographer.java25
-rw-r--r--core/java/android/view/ImeInsetsSourceConsumer.java15
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java6
-rw-r--r--core/java/android/view/SurfaceControl.java26
-rw-r--r--core/java/android/view/ViewRootImpl.java3
-rw-r--r--core/java/android/view/WindowManager.java12
-rw-r--r--core/java/android/view/WindowManagerPolicyConstants.java1
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java33
-rw-r--r--core/java/com/android/internal/app/AppLocaleStore.java15
-rw-r--r--core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java2
-rw-r--r--core/jni/android_view_SurfaceControl.cpp5
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java24
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java17
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java43
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java52
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java32
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt21
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt6
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml12
-rw-r--r--packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java6
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt49
-rw-r--r--packages/SystemUI/res-keyguard/layout/fgs_footer.xml11
-rw-r--r--packages/SystemUI/res/layout/media_output_broadcast_update_dialog.xml10
-rw-r--r--packages/SystemUI/res/layout/media_output_list_item.xml8
-rw-r--r--packages/SystemUI/res/values/dimens.xml6
-rw-r--r--packages/SystemUI/res/values/strings.xml5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ILauncherUnlockAnimationController.aidl9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java45
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java22
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java42
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt667
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java (renamed from services/tests/servicestests/aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl)24
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaTimeoutLogger.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserLogger.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java127
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiStatusTrackerFactory.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java4572
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java4551
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java79
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java305
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt142
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/ColorUtil.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/Utils.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt114
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt122
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt167
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SmartspaceMediaDataTest.kt108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesTest.java)8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt168
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt118
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt107
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java12
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java19
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java19
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java23
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java15
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java11
-rw-r--r--services/core/java/com/android/server/am/DropboxRateLimiter.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java26
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java42
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java39
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java38
-rw-r--r--services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java48
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java16
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java18
-rw-r--r--services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java4
-rw-r--r--services/core/java/com/android/server/media/MediaShellCommand.java1
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java18
-rw-r--r--services/core/java/com/android/server/pm/ShortcutLauncher.java10
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java38
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackageItem.java49
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java32
-rw-r--r--services/core/java/com/android/server/pm/ShortcutUser.java27
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java8
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java6
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING3
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyInternal.java5
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java67
-rw-r--r--services/core/java/com/android/server/wm/ActivityInterceptorCallback.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java81
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java50
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java6
-rw-r--r--services/core/java/com/android/server/wm/AppTaskImpl.java16
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java4
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java34
-rw-r--r--services/core/java/com/android/server/wm/DisplayFrames.java2
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java6
-rw-r--r--services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java72
-rw-r--r--services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java49
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java2
-rw-r--r--services/core/java/com/android/server/wm/StartingSurfaceController.java10
-rw-r--r--services/core/java/com/android/server/wm/Task.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java3
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java4
-rw-r--r--services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java27
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java14
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java2
-rw-r--r--services/tests/servicestests/Android.bp7
-rw-r--r--services/tests/servicestests/AndroidTest.xml1
-rw-r--r--services/tests/servicestests/aidl/Android.bp31
-rw-r--r--services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl27
-rw-r--r--services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java420
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java20
-rw-r--r--services/tests/servicestests/test-apps/ConnTestApp/Android.bp40
-rw-r--r--services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml30
-rw-r--r--services/tests/servicestests/test-apps/ConnTestApp/OWNERS1
-rw-r--r--services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/CmdReceiverService.java36
-rw-r--r--services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java146
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java149
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java9
-rw-r--r--telephony/java/Android.bp9
-rw-r--r--telephony/java/android/telephony/AnomalyReporter.java9
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt10
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java20
229 files changed, 9144 insertions, 7338 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 0e4887d27b53..2ea8592e883e 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -2637,15 +2637,18 @@ public class AlarmManagerService extends SystemService {
* Returns true if the given uid can set window to be as small as it wants.
*/
boolean isExemptFromMinWindowRestrictions(int uid) {
- return isExemptFromExactAlarmPermission(uid);
+ return isExemptFromExactAlarmPermissionNoLock(uid);
}
/**
* Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact,
* allow-while-idle alarms.
- * Note: It is ok to call this method without the lock {@link #mLock} held.
+ * <b> Note: This should not be called with {@link #mLock} held.</b>
*/
- boolean isExemptFromExactAlarmPermission(int uid) {
+ boolean isExemptFromExactAlarmPermissionNoLock(int uid) {
+ if (Build.IS_DEBUGGABLE && Thread.holdsLock(mLock)) {
+ Slog.wtfStack(TAG, "Alarm lock held while calling into DeviceIdleController");
+ }
return (UserHandle.isSameApp(mSystemUiUid, uid)
|| UserHandle.isCore(uid)
|| mLocalDeviceIdleController == null
@@ -2747,7 +2750,7 @@ public class AlarmManagerService extends SystemService {
}
if (needsPermission && !hasScheduleExactAlarmInternal(callingPackage, callingUid)
&& !hasUseExactAlarmInternal(callingPackage, callingUid)) {
- if (!isExemptFromExactAlarmPermission(callingUid)) {
+ if (!isExemptFromExactAlarmPermissionNoLock(callingUid)) {
final String errorMessage = "Caller " + callingPackage + " needs to hold "
+ Manifest.permission.SCHEDULE_EXACT_ALARM + " to set "
+ "exact alarms.";
@@ -2810,7 +2813,7 @@ public class AlarmManagerService extends SystemService {
if (!isExactAlarmChangeEnabled(packageName, userId)) {
return true;
}
- return isExemptFromExactAlarmPermission(packageUid)
+ return isExemptFromExactAlarmPermissionNoLock(packageUid)
|| hasScheduleExactAlarmInternal(packageName, packageUid)
|| hasUseExactAlarmInternal(packageName, packageUid);
}
@@ -3862,10 +3865,7 @@ public class AlarmManagerService extends SystemService {
// added: true => package was added to the deny list
// added: false => package was removed from the deny list
if (added) {
- synchronized (mLock) {
- removeExactAlarmsOnPermissionRevokedLocked(uid,
- changedPackage, /*killUid = */ true);
- }
+ removeExactAlarmsOnPermissionRevoked(uid, changedPackage, /*killUid = */ true);
} else {
sendScheduleExactAlarmPermissionStateChangedBroadcast(changedPackage, userId);
}
@@ -3880,9 +3880,8 @@ public class AlarmManagerService extends SystemService {
*
* This is not expected to get called frequently.
*/
- @GuardedBy("mLock")
- void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName, boolean killUid) {
- if (isExemptFromExactAlarmPermission(uid)
+ void removeExactAlarmsOnPermissionRevoked(int uid, String packageName, boolean killUid) {
+ if (isExemptFromExactAlarmPermissionNoLock(uid)
|| !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) {
return;
}
@@ -3891,7 +3890,9 @@ public class AlarmManagerService extends SystemService {
final Predicate<Alarm> whichAlarms = a -> (a.uid == uid && a.packageName.equals(packageName)
&& a.windowLength == 0);
- removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
+ synchronized (mLock) {
+ removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
+ }
if (killUid && mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) {
PermissionManagerService.killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
@@ -4807,10 +4808,7 @@ public class AlarmManagerService extends SystemService {
case REMOVE_EXACT_ALARMS:
int uid = msg.arg1;
String packageName = (String) msg.obj;
- synchronized (mLock) {
- removeExactAlarmsOnPermissionRevokedLocked(uid, packageName, /*killUid = */
- true);
- }
+ removeExactAlarmsOnPermissionRevoked(uid, packageName, /*killUid = */true);
break;
case EXACT_ALARM_DENY_LIST_PACKAGES_ADDED:
handleChangesToExactAlarmDenyList((ArraySet<String>) msg.obj, true);
@@ -4826,10 +4824,7 @@ public class AlarmManagerService extends SystemService {
uid = msg.arg1;
if (!hasScheduleExactAlarmInternal(packageName, uid)
&& !hasUseExactAlarmInternal(packageName, uid)) {
- synchronized (mLock) {
- removeExactAlarmsOnPermissionRevokedLocked(uid,
- packageName, /*killUid = */false);
- }
+ removeExactAlarmsOnPermissionRevoked(uid, packageName, /*killUid = */false);
}
break;
case CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE:
@@ -4849,10 +4844,7 @@ public class AlarmManagerService extends SystemService {
if (defaultDenied) {
if (!hasScheduleExactAlarmInternal(pkg, uid)
&& !hasUseExactAlarmInternal(pkg, uid)) {
- synchronized (mLock) {
- removeExactAlarmsOnPermissionRevokedLocked(uid, pkg,
- true);
- }
+ removeExactAlarmsOnPermissionRevoked(uid, pkg, true);
}
} else if (hasScheduleExactAlarmInternal(pkg, uid)) {
sendScheduleExactAlarmPermissionStateChangedBroadcast(pkg,
diff --git a/api/api.go b/api/api.go
index 9aac879b4eae..ce8cd1426661 100644
--- a/api/api.go
+++ b/api/api.go
@@ -208,12 +208,6 @@ func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) {
props := libraryProps{}
props.Name = proptools.StringPtr("all-framework-module-impl")
props.Static_libs = transformArray(modules, "", ".impl")
- // Media module's impl jar is called "updatable-media"
- for i, v := range props.Static_libs {
- if v == "framework-media.impl" {
- props.Static_libs[i] = "updatable-media"
- }
- }
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(java.LibraryFactory, &props)
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index add891d40d95..b49e571f74e1 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -374,10 +374,6 @@ public class PropertyInvalidatedCache<Query, Result> {
private static final String TAG = "PropertyInvalidatedCache";
private static final boolean DEBUG = false;
private static final boolean VERIFY = false;
- // If this is true, dumpsys will dump the cache entries along with cache statistics.
- // Most of the time this causes dumpsys to fail because the output stream is too
- // large. Only set it to true in development images.
- private static final boolean DETAILED = false;
// Per-Cache performance counters. As some cache instances are declared static,
@GuardedBy("mLock")
@@ -1358,7 +1354,69 @@ public class PropertyInvalidatedCache<Query, Result> {
}
}
- private void dumpContents(PrintWriter pw) {
+ /**
+ * Switches that can be used to control the detail emitted by a cache dump. The
+ * "CONTAINS" switches match if the cache (property) name contains the switch
+ * argument. The "LIKE" switches match if the cache (property) name matches the
+ * switch argument as a regex. The regular expression must match the entire name,
+ * which generally means it may need leading/trailing "." expressions.
+ */
+ final static String NAME_CONTAINS = "-name-has=";
+ final static String NAME_LIKE = "-name-like=";
+ final static String PROPERTY_CONTAINS = "-property-has=";
+ final static String PROPERTY_LIKE = "-property-like=";
+
+ /**
+ * Return true if any argument is a detailed specification switch.
+ */
+ private static boolean anyDetailed(String[] args) {
+ for (String a : args) {
+ if (a.startsWith(NAME_CONTAINS) || a.startsWith(NAME_LIKE)
+ || a.startsWith(PROPERTY_CONTAINS) || a.startsWith(PROPERTY_LIKE)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * A helper method to determine if a string matches a switch.
+ */
+ private static boolean chooses(String arg, String key, String reference, boolean contains) {
+ if (arg.startsWith(key)) {
+ final String value = arg.substring(key.length());
+ if (contains) {
+ return reference.contains(value);
+ } else {
+ return reference.matches(value);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return true if this cache should be dumped in detail. This method is not called
+ * unless it has already been determined that there is at least one match requested.
+ */
+ private boolean showDetailed(String[] args) {
+ for (String a : args) {
+ if (chooses(a, NAME_CONTAINS, cacheName(), true)
+ || chooses(a, NAME_LIKE, cacheName(), false)
+ || chooses(a, PROPERTY_CONTAINS, mPropertyName, true)
+ || chooses(a, PROPERTY_LIKE, mPropertyName, false)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void dumpContents(PrintWriter pw, boolean detailed, String[] args) {
+ // If the user has requested specific caches and this is not one of them, return
+ // immediately.
+ if (detailed && !showDetailed(args)) {
+ return;
+ }
+
long invalidateCount;
long corkedInvalidates;
synchronized (sCorkLock) {
@@ -1386,9 +1444,15 @@ public class PropertyInvalidatedCache<Query, Result> {
mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
pw.println(TextUtils.formatSimple(" Enabled: %s", mDisabled ? "false" : "true"));
pw.println("");
+ pw.flush();
+ // No specific cache was requested. This is the default, and no details
+ // should be dumped.
+ if (!detailed) {
+ return;
+ }
Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
- if (!DETAILED || cacheEntries.size() == 0) {
+ if (cacheEntries.size() == 0) {
return;
}
@@ -1399,17 +1463,34 @@ public class PropertyInvalidatedCache<Query, Result> {
pw.println(TextUtils.formatSimple(" Key: %s\n Value: %s\n", key, value));
}
+ pw.flush();
+ }
+ }
+
+ /**
+ * Dump the corking status.
+ */
+ @GuardedBy("sCorkLock")
+ private static void dumpCorkInfo(PrintWriter pw) {
+ ArrayList<Map.Entry<String, Integer>> activeCorks = getActiveCorks();
+ if (activeCorks.size() > 0) {
+ pw.println(" Corking Status:");
+ for (int i = 0; i < activeCorks.size(); i++) {
+ Map.Entry<String, Integer> entry = activeCorks.get(i);
+ pw.println(TextUtils.formatSimple(" Property Name: %s Count: %d",
+ entry.getKey(), entry.getValue()));
+ }
}
}
/**
- * Dumps contents of every cache in the process to the provided ParcelFileDescriptor.
+ * Without arguments, this dumps statistics from every cache in the process to the
+ * provided ParcelFileDescriptor. Optional switches allow the caller to choose
+ * specific caches (selection is by cache name or property name); if these switches
+ * are used then the output includes both cache statistics and cache entries.
* @hide
*/
public static void dumpCacheInfo(@NonNull ParcelFileDescriptor pfd, @NonNull String[] args) {
- ArrayList<PropertyInvalidatedCache> activeCaches;
- ArrayList<Map.Entry<String, Integer>> activeCorks;
-
try (
FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor());
PrintWriter pw = new FastPrintWriter(fout);
@@ -1419,24 +1500,21 @@ public class PropertyInvalidatedCache<Query, Result> {
return;
}
+ // See if detailed is requested for any cache. If there is a specific detailed request,
+ // then only that cache is reported.
+ boolean detail = anyDetailed(args);
+
+ ArrayList<PropertyInvalidatedCache> activeCaches;
synchronized (sCorkLock) {
activeCaches = getActiveCaches();
- activeCorks = getActiveCorks();
-
- if (activeCorks.size() > 0) {
- pw.println(" Corking Status:");
- for (int i = 0; i < activeCorks.size(); i++) {
- Map.Entry<String, Integer> entry = activeCorks.get(i);
- pw.println(TextUtils.formatSimple(" Property Name: %s Count: %d",
- entry.getKey(), entry.getValue()));
- }
+ if (!detail) {
+ dumpCorkInfo(pw);
}
}
for (int i = 0; i < activeCaches.size(); i++) {
PropertyInvalidatedCache currentCache = activeCaches.get(i);
- currentCache.dumpContents(pw);
- pw.flush();
+ currentCache.dumpContents(pw, detail, args);
}
} catch (IOException e) {
Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances");
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4d4a57db84be..44dc28d2b0fa 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1414,11 +1414,9 @@ public class PackageParser {
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
final ParseResult<android.content.pm.SigningDetails> result;
if (skipVerify) {
- // systemDir APKs are already trusted, save time by not verifying; since the signature
- // is not verified and some system apps can have their V2+ signatures stripped allow
- // pulling the certs from the jar signature.
+ // systemDir APKs are already trusted, save time by not verifying
result = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
- input, apkPath, SigningDetails.SignatureSchemeVersion.JAR);
+ input, apkPath, minSignatureScheme);
} else {
result = ApkSignatureVerifier.verify(input, apkPath, minSignatureScheme);
}
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index cc62d5337c02..1c1f58a19abc 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -168,15 +168,6 @@
"name": "CtsIncrementalInstallHostTestCases"
},
{
- "name": "CtsInstallHostTestCases"
- },
- {
- "name": "CtsStagedInstallHostTestCases"
- },
- {
- "name": "CtsExtractNativeLibsHostTestCases"
- },
- {
"name": "CtsAppSecurityHostTestCases",
"options": [
{
@@ -188,34 +179,47 @@
]
},
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.pm.PackageParserTest"
- }
- ]
- },
- {
- "name": "CtsRollbackManagerHostTestCases"
- },
- {
"name": "CtsContentTestCases",
"options": [
{
"include-filter": "android.content.cts.IntentFilterTest"
}
]
- },
- {
- "name": "CtsAppEnumerationTestCases"
- },
- {
- "name": "PackageManagerServiceUnitTests",
- "options": [
- {
- "include-filter": "com.android.server.pm.test.verify.domain"
- }
- ]
}
+ ],
+ "platinum-postsubmit": [
+ {
+ "name": "CtsIncrementalInstallHostTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsAppSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.appsecurity.cts.SplitTests"
+ },
+ {
+ "include-filter": "android.appsecurity.cts.EphemeralTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsContentTestCases",
+ "options":[
+ {
+ "include-filter": "android.content.cts.IntentFilterTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
]
}
diff --git a/core/java/android/content/pm/verify/domain/TEST_MAPPING b/core/java/android/content/pm/verify/domain/TEST_MAPPING
index ba4a62cdbbf1..8a1982a339ea 100644
--- a/core/java/android/content/pm/verify/domain/TEST_MAPPING
+++ b/core/java/android/content/pm/verify/domain/TEST_MAPPING
@@ -12,9 +12,6 @@
"name": "CtsDomainVerificationDeviceStandaloneTestCases"
},
{
- "name": "CtsDomainVerificationDeviceMultiUserTestCases"
- },
- {
"name": "CtsDomainVerificationHostTestCases"
}
]
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 39cb7f3ebddb..bb0caa787e27 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -224,9 +224,10 @@ public final class OutputConfiguration implements Parcelable {
* display subsystem for smoother display of camera frames. An output target of SurfaceView
* uses this time base by default.</p>
*
- * <p>The choreographer synchronized timestamps are also reasonable to use when drawing to a
- * TextureView. So this timestamp base can be used for a SurfaceTexture as part of a
- * TextureView, in addition to SurfaceView.</p>
+ * <p>This timestamp base isn't applicable to SurfaceTexture targets. SurfaceTexture's
+ * {@link android.graphics.SurfaceTexture#updateTexImage updateTexImage} function always
+ * uses the latest image from the camera stream. In the case of a TextureView, the image is
+ * displayed right away.</p>
*
* <p>Timestamps with this time base cannot directly match the timestamps in
* {@link CameraCaptureSession.CaptureCallback#onCaptureStarted} or the sensor timestamps in
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index a6ed42348af6..efd4f0681838 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -482,8 +482,9 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* Timeout after which hidden IME surface will be removed from memory
+ * TODO(b/230762351): reset timeout to 5000ms and invalidate cache when IME insets change.
*/
- private static final long TIMEOUT_SURFACE_REMOVAL_MILLIS = 5000;
+ private static final long TIMEOUT_SURFACE_REMOVAL_MILLIS = 500;
InputMethodManager mImm;
private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 34647b144be1..20a2bdf3b109 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7679,20 +7679,6 @@ public final class Settings {
"zen_settings_suggestion_viewed";
/**
- * State of whether review notification permissions notification needs to
- * be shown the user, and whether the user has interacted.
- *
- * Valid values:
- * -1 = UNKNOWN
- * 0 = SHOULD_SHOW
- * 1 = USER_INTERACTED
- * 2 = DISMISSED
- * @hide
- */
- public static final String REVIEW_PERMISSIONS_NOTIFICATION_STATE =
- "review_permissions_notification_state";
-
- /**
* Whether the in call notification is enabled to play sound during calls. The value is
* boolean (1 or 0).
* @hide
@@ -9696,6 +9682,26 @@ public final class Settings {
public static final String BIOMETRIC_APP_ENABLED = "biometric_app_enabled";
/**
+ * Whether or not active unlock triggers on wake.
+ * @hide
+ */
+ public static final String ACTIVE_UNLOCK_ON_WAKE = "active_unlock_on_wake";
+
+ /**
+ * Whether or not active unlock triggers on unlock intent.
+ * @hide
+ */
+ public static final String ACTIVE_UNLOCK_ON_UNLOCK_INTENT =
+ "active_unlock_on_unlock_intent";
+
+ /**
+ * Whether or not active unlock triggers on biometric failure.
+ * @hide
+ */
+ public static final String ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL =
+ "active_unlock_on_biometric_fail";
+
+ /**
* Whether the assist gesture should be enabled.
*
* @hide
@@ -16966,6 +16972,21 @@ public final class Settings {
"managed_provisioning_defer_provisioning_to_role_holder";
/**
+ * State of whether review notification permissions notification needs to
+ * be shown the user, and whether the user has interacted.
+ *
+ * Valid values:
+ * -1 = UNKNOWN
+ * 0 = SHOULD_SHOW
+ * 1 = USER_INTERACTED
+ * 2 = DISMISSED
+ * 3 = RESHOWN
+ * @hide
+ */
+ public static final String REVIEW_PERMISSIONS_NOTIFICATION_STATE =
+ "review_permissions_notification_state";
+
+ /**
* Settings migrated from Wear OS settings provider.
* @hide
*/
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 6a9afdb84e18..47fc120c9d4f 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -999,6 +999,14 @@ public class DreamService extends Service implements Window.Callback {
return mDreamServiceWrapper;
}
+ @Override
+ public boolean onUnbind(Intent intent) {
+ // We must unbind from any overlay connection if we are unbound before finishing.
+ mOverlayConnection.unbind(this);
+
+ return super.onUnbind(intent);
+ }
+
/**
* Stops the dream and detaches from the window.
* <p>
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 77591a7efb5e..ebbe64c396e7 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -785,9 +785,7 @@ public final class Choreographer {
}
}
frameTimeNanos = startNanos - lastFrameOffset;
- DisplayEventReceiver.VsyncEventData latestVsyncEventData =
- mDisplayEventReceiver.getLatestVsyncEventData();
- frameData.updateFrameData(frameTimeNanos, latestVsyncEventData);
+ frameData.updateFrameData(frameTimeNanos);
}
if (frameTimeNanos < mLastFrameTimeNanos) {
@@ -885,9 +883,7 @@ public final class Choreographer {
}
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
- DisplayEventReceiver.VsyncEventData latestVsyncEventData =
- mDisplayEventReceiver.getLatestVsyncEventData();
- frameData.updateFrameData(frameTimeNanos, latestVsyncEventData);
+ frameData.updateFrameData(frameTimeNanos);
}
}
}
@@ -1022,6 +1018,11 @@ public final class Choreographer {
return mVsyncId;
}
+ /** Reset the vsync ID to invalid. */
+ void resetVsyncId() {
+ mVsyncId = FrameInfo.INVALID_VSYNC_ID;
+ }
+
/**
* The time in {@link System#nanoTime()} timebase which this frame is expected to be
* presented.
@@ -1069,12 +1070,14 @@ public final class Choreographer {
private FrameTimeline[] mFrameTimelines;
private FrameTimeline mPreferredFrameTimeline;
- void updateFrameData(long frameTimeNanos,
- DisplayEventReceiver.VsyncEventData latestVsyncEventData) {
+ void updateFrameData(long frameTimeNanos) {
mFrameTimeNanos = frameTimeNanos;
- mFrameTimelines = convertFrameTimelines(latestVsyncEventData);
- mPreferredFrameTimeline =
- mFrameTimelines[latestVsyncEventData.preferredFrameTimelineIndex];
+ for (FrameTimeline ft : mFrameTimelines) {
+ // The ID is no longer valid because the frame time that was registered with the ID
+ // no longer matches.
+ // TODO(b/205721584): Ask SF for valid vsync information.
+ ft.resetVsyncId();
+ }
}
/** The time in nanoseconds when the frame started being rendered. */
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 4fdea3b006dc..332e97c8bcf5 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -65,6 +65,9 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
public void onWindowFocusGained(boolean hasViewFocus) {
super.onWindowFocusGained(hasViewFocus);
getImm().registerImeConsumer(this);
+ if (isRequestedVisible() && getControl() == null) {
+ mIsRequestedVisibleAwaitingControl = true;
+ }
}
@Override
@@ -149,14 +152,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
}
@Override
- public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
+ public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes,
int[] hideTypes) {
- super.setControl(control, showTypes, hideTypes);
- // TODO(b/204524304): clean-up how to deal with the timing issues of hiding IME:
- // 1) Already requested show IME, in the meantime of WM callback the control but got null
- // control when relayout comes first
- // 2) Make sure no regression on some implicit request IME visibility calls (e.g.
- // toggleSoftInput)
+ if (!super.setControl(control, showTypes, hideTypes)) {
+ return false;
+ }
if (control == null && !mIsRequestedVisibleAwaitingControl) {
hide();
removeSurface();
@@ -164,6 +164,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
if (control != null) {
mIsRequestedVisibleAwaitingControl = false;
}
+ return true;
}
@Override
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 4d9033df89e1..d6b75b94b19a 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -122,8 +122,9 @@ public class InsetsSourceConsumer {
* animation should be run after setting the control.
* @param hideTypes An integer array with a single entry that determines which types a hide
* animation should be run after setting the control.
+ * @return Whether the control has changed from the server
*/
- public void setControl(@Nullable InsetsSourceControl control,
+ public boolean setControl(@Nullable InsetsSourceControl control,
@InsetsType int[] showTypes, @InsetsType int[] hideTypes) {
if (mType == ITYPE_IME) {
ImeTracing.getInstance().triggerClientDump("InsetsSourceConsumer#setControl",
@@ -134,7 +135,7 @@ public class InsetsSourceConsumer {
mSourceControl.release(SurfaceControl::release);
mSourceControl = control;
}
- return;
+ return false;
}
SurfaceControl oldLeash = mSourceControl != null ? mSourceControl.getLeash() : null;
@@ -201,6 +202,7 @@ public class InsetsSourceConsumer {
if (lastControl != null) {
lastControl.release(SurfaceControl::release);
}
+ return true;
}
@VisibleForTesting
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b5bbc7537391..a1ce39e974e3 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -785,12 +785,14 @@ public final class SurfaceControl implements Parcelable {
private final HardwareBuffer mHardwareBuffer;
private final ColorSpace mColorSpace;
private final boolean mContainsSecureLayers;
+ private final boolean mContainsHdrLayers;
public ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace,
- boolean containsSecureLayers) {
+ boolean containsSecureLayers, boolean containsHdrLayers) {
mHardwareBuffer = hardwareBuffer;
mColorSpace = colorSpace;
mContainsSecureLayers = containsSecureLayers;
+ mContainsHdrLayers = containsHdrLayers;
}
/**
@@ -798,13 +800,15 @@ public final class SurfaceControl implements Parcelable {
* @param hardwareBuffer The existing HardwareBuffer object
* @param namedColorSpace Integer value of a named color space {@link ColorSpace.Named}
* @param containsSecureLayers Indicates whether this graphic buffer contains captured
- * contents
- * of secure layers, in which case the screenshot should not be persisted.
+ * contents of secure layers, in which case the screenshot
+ * should not be persisted.
+ * @param containsHdrLayers Indicates whether this graphic buffer contains HDR content.
*/
private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer,
- int namedColorSpace, boolean containsSecureLayers) {
+ int namedColorSpace, boolean containsSecureLayers, boolean containsHdrLayers) {
ColorSpace colorSpace = ColorSpace.get(ColorSpace.Named.values()[namedColorSpace]);
- return new ScreenshotHardwareBuffer(hardwareBuffer, colorSpace, containsSecureLayers);
+ return new ScreenshotHardwareBuffer(
+ hardwareBuffer, colorSpace, containsSecureLayers, containsHdrLayers);
}
public ColorSpace getColorSpace() {
@@ -818,6 +822,14 @@ public final class SurfaceControl implements Parcelable {
public boolean containsSecureLayers() {
return mContainsSecureLayers;
}
+ /**
+ * Returns whether the screenshot contains at least one HDR layer.
+ * This information may be useful for informing the display whether this screenshot
+ * is allowed to be dimmed to SDR white.
+ */
+ public boolean containsHdrLayers() {
+ return mContainsHdrLayers;
+ }
/**
* Copy content of ScreenshotHardwareBuffer into a hardware bitmap and return it.
@@ -3871,8 +3883,8 @@ public final class SurfaceControl implements Parcelable {
@Deprecated
public Transaction setColorSpace(SurfaceControl sc, ColorSpace colorSpace) {
checkPreconditions(sc);
- if (colorSpace.getId() == ColorSpace.Named.DCI_P3.ordinal()) {
- setDataSpace(sc, DataSpace.DATASPACE_DCI_P3);
+ if (colorSpace.getId() == ColorSpace.Named.DISPLAY_P3.ordinal()) {
+ setDataSpace(sc, DataSpace.DATASPACE_DISPLAY_P3);
} else {
setDataSpace(sc, DataSpace.DATASPACE_SRGB);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fbb86ff3a55a..b7a2aa0b0174 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5035,9 +5035,6 @@ public final class ViewRootImpl implements ViewParent,
}
void requestPointerCapture(boolean enabled) {
- if (mPointerCapture == enabled) {
- return;
- }
final IBinder inputToken = getInputToken();
if (inputToken == null) {
Log.e(mTag, "No input channel to request Pointer Capture.");
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index cfe44bbbf3c6..5bc340b76f56 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -488,6 +488,13 @@ public interface WindowManager extends ViewManager {
int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION = 0x8;
/**
+ * Transition flag: Keyguard is going away to the launcher, and it needs us to clear the task
+ * snapshot of the launcher because it has changed something in the Launcher window.
+ * @hide
+ */
+ int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT = 0x16;
+
+ /**
* Transition flag: App is crashed.
* @hide
*/
@@ -527,6 +534,7 @@ public interface WindowManager extends ViewManager {
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION,
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER,
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION,
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT,
TRANSIT_FLAG_APP_CRASHED,
TRANSIT_FLAG_OPEN_BEHIND,
TRANSIT_FLAG_KEYGUARD_LOCKED,
@@ -717,8 +725,8 @@ public interface WindowManager extends ViewManager {
/**
* Returns a set of {@link WindowMetrics} for the given display. Each WindowMetrics instance
- * is the maximum WindowMetrics for a device state, including rotations. This is not guaranteed
- * to include all possible device states.
+ * is the maximum WindowMetrics for a device state. This is not guaranteed to include all
+ * possible device states.
*
* This API can only be used by Launcher.
*
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 94f633314b4e..4d07171d3086 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -48,6 +48,7 @@ public interface WindowManagerPolicyConstants {
int KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS = 1 << 1;
int KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER = 1 << 2;
int KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS = 1 << 3;
+ int KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT = 1 << 4;
// Flags used for indicating whether the internal and/or external input devices
// of some type are available.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 850256871b15..d9bde5825fde 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -33,6 +33,7 @@ import static android.view.inputmethod.InputMethodManagerProto.CUR_ID;
import static android.view.inputmethod.InputMethodManagerProto.FULLSCREEN_MODE;
import static android.view.inputmethod.InputMethodManagerProto.SERVED_CONNECTING;
+import static com.android.internal.inputmethod.StartInputReason.BOUND_TO_IMMS;
import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION;
import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION;
@@ -450,6 +451,11 @@ public final class InputMethodManager {
int mInitialSelEnd;
/**
+ * Handler for {@link RemoteInputConnectionImpl#getInputConnection()}.
+ */
+ private Handler mServedInputConnectionHandler;
+
+ /**
* The instance that has previously been sent to the input method.
*/
private CursorAnchorInfo mCursorAnchorInfo = null;
@@ -1658,6 +1664,7 @@ public final class InputMethodManager {
if (mServedInputConnection != null) {
mServedInputConnection.deactivate();
mServedInputConnection = null;
+ mServedInputConnectionHandler = null;
}
}
@@ -2289,6 +2296,13 @@ public final class InputMethodManager {
"Starting input: finished by someone else. view=" + dumpViewInfo(view)
+ " servedView=" + dumpViewInfo(servedView)
+ " mServedConnecting=" + mServedConnecting);
+ if (mServedInputConnection != null && startInputReason == BOUND_TO_IMMS) {
+ // This is not an error. Once IME binds (MSG_BIND), InputConnection is fully
+ // established. So we report this to interested recipients.
+ reportInputConnectionOpened(
+ mServedInputConnection.getInputConnection(), mCurrentTextBoxAttribute,
+ mServedInputConnectionHandler, view);
+ }
return false;
}
@@ -2305,6 +2319,7 @@ public final class InputMethodManager {
if (mServedInputConnection != null) {
mServedInputConnection.deactivate();
mServedInputConnection = null;
+ mServedInputConnectionHandler = null;
}
RemoteInputConnectionImpl servedInputConnection;
if (ic != null) {
@@ -2323,11 +2338,13 @@ public final class InputMethodManager {
// TODO(b/199934664): See if we can remove this by providing a default impl.
}
icHandler = handler;
+ mServedInputConnectionHandler = icHandler;
servedInputConnection = new RemoteInputConnectionImpl(
icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this, view);
} else {
servedInputConnection = null;
icHandler = null;
+ mServedInputConnectionHandler = null;
}
mServedInputConnection = servedInputConnection;
@@ -2397,16 +2414,21 @@ public final class InputMethodManager {
Log.v(TAG, "Calling View.onInputConnectionOpened: view= " + view
+ ", ic=" + ic + ", tba=" + tba + ", handler=" + icHandler);
}
- view.onInputConnectionOpenedInternal(ic, tba, icHandler);
- final ViewRootImpl viewRoot = view.getViewRootImpl();
- if (viewRoot != null) {
- viewRoot.getHandwritingInitiator().onInputConnectionCreated(view);
- }
+ reportInputConnectionOpened(ic, tba, icHandler, view);
}
return true;
}
+ private void reportInputConnectionOpened(
+ InputConnection ic, EditorInfo tba, Handler icHandler, View view) {
+ view.onInputConnectionOpenedInternal(ic, tba, icHandler);
+ final ViewRootImpl viewRoot = view.getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.getHandwritingInitiator().onInputConnectionCreated(view);
+ }
+ }
+
/**
* An empty method only to avoid crashes of apps that call this method via reflection and do not
* handle {@link NoSuchMethodException} in a graceful manner.
@@ -3550,6 +3572,7 @@ public final class InputMethodManager {
p.println(" mCurrentTextBoxAttribute: null");
}
p.println(" mServedInputConnection=" + mServedInputConnection);
+ p.println(" mServedInputConnectionHandler=" + mServedInputConnectionHandler);
p.println(" mCompletions=" + Arrays.toString(mCompletions));
p.println(" mCursorRect=" + mCursorRect);
p.println(" mCursorSelStart=" + mCursorSelStart
diff --git a/core/java/com/android/internal/app/AppLocaleStore.java b/core/java/com/android/internal/app/AppLocaleStore.java
index f95838516927..599e6d24600c 100644
--- a/core/java/com/android/internal/app/AppLocaleStore.java
+++ b/core/java/com/android/internal/app/AppLocaleStore.java
@@ -51,13 +51,17 @@ class AppLocaleStore {
appSupportedLocales.add(packageLocaleList.get(i));
}
} else {
- localeStatus = LocaleStatus.NO_SUPPORTED_LANGUAGE;
+ localeStatus = LocaleStatus.NO_SUPPORTED_LANGUAGE_IN_APP;
}
} else if (localeConfig.getStatus() == LocaleConfig.STATUS_NOT_SPECIFIED) {
- localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
String[] languages = getAssetLocales(context, packageName);
- for (String language : languages) {
- appSupportedLocales.add(Locale.forLanguageTag(language));
+ if (languages.length > 0) {
+ localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
+ for (String language : languages) {
+ appSupportedLocales.add(Locale.forLanguageTag(language));
+ }
+ } else {
+ localeStatus = LocaleStatus.ASSET_LOCALE_IS_EMPTY;
}
}
}
@@ -89,7 +93,8 @@ class AppLocaleStore {
static class AppLocaleResult {
enum LocaleStatus {
UNKNOWN_FAILURE,
- NO_SUPPORTED_LANGUAGE,
+ NO_SUPPORTED_LANGUAGE_IN_APP,
+ ASSET_LOCALE_IS_EMPTY,
GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
GET_SUPPORTED_LANGUAGE_FROM_ASSET,
}
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 12b522bb98f2..f21cee997163 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -191,7 +191,7 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
* @return {@link InputConnection} to which incoming IPCs will be dispatched.
*/
@Nullable
- private InputConnection getInputConnection() {
+ public InputConnection getInputConnection() {
synchronized (mLock) {
return mInputConnection;
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 51a708b76801..c769da57eecc 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -322,7 +322,8 @@ public:
env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
gScreenshotHardwareBufferClassInfo.builder,
jhardwareBuffer, namedColorSpace,
- captureResults.capturedSecureLayers);
+ captureResults.capturedSecureLayers,
+ captureResults.capturedHdrLayers);
env->CallVoidMethod(screenCaptureListenerObject,
gScreenCaptureListenerClassInfo.onScreenCaptureComplete,
screenshotHardwareBuffer);
@@ -2399,7 +2400,7 @@ int register_android_view_SurfaceControl(JNIEnv* env)
MakeGlobalRefOrDie(env, screenshotGraphicsBufferClazz);
gScreenshotHardwareBufferClassInfo.builder =
GetStaticMethodIDOrDie(env, screenshotGraphicsBufferClazz, "createFromNative",
- "(Landroid/hardware/HardwareBuffer;IZ)Landroid/view/"
+ "(Landroid/hardware/HardwareBuffer;IZZ)Landroid/view/"
"SurfaceControl$ScreenshotHardwareBuffer;");
jclass displayedContentSampleClazz = FindClassOrDie(env,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 19b72bfbe6c0..edaf8cf279e3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2415,8 +2415,12 @@
<!-- Is the system user the only user allowed to dream. -->
<bool name="config_dreamsOnlyEnabledForSystemUser">false</bool>
+ <!-- Whether to dismiss the active dream when an activity is started. Doesn't apply to
+ assistant activities (ACTIVITY_TYPE_ASSISTANT) -->
+ <bool name="config_dismissDreamOnActivityStart">true</bool>
+
<!-- The prefix of dream component names that are loggable. If empty, logs "other" for all. -->
- <string name ="config_loggable_dream_prefix" translatable="false"></string>
+ <string name="config_loggable_dream_prefix" translatable="false"></string>
<!-- ComponentName of a dream to show whenever the system would otherwise have
gone to sleep. When the PowerManager is asked to go to sleep, it will instead
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 012030e9b393..8226ec435533 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2221,6 +2221,7 @@
<java-symbol type="array" name="config_supportedDreamComplications" />
<java-symbol type="array" name="config_dreamComplicationsEnabledByDefault" />
<java-symbol type="array" name="config_disabledDreamComponents" />
+ <java-symbol type="bool" name="config_dismissDreamOnActivityStart" />
<java-symbol type="string" name="config_loggable_dream_prefix" />
<java-symbol type="string" name="config_dozeComponent" />
<java-symbol type="string" name="enable_explore_by_touch_warning_title" />
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 81caf7786cf5..e50b9a1cd469 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -16,6 +16,7 @@
package androidx.window.extensions.embedding;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.app.Activity;
@@ -48,8 +49,7 @@ import java.util.concurrent.Executor;
class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
/** Mapping from the client assigned unique token to the {@link TaskFragmentInfo}. */
- @VisibleForTesting
- final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
+ private final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
/**
* Mapping from the client assigned unique token to the TaskFragment parent
@@ -120,29 +120,25 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
* @param secondaryFragmentBounds the initial bounds for the secondary TaskFragment
* @param activityIntent Intent to start the secondary Activity with.
* @param activityOptions ActivityOptions to start the secondary Activity with.
- * @param windowingMode the windowing mode to set for the TaskFragments.
*/
void startActivityToSide(@NonNull WindowContainerTransaction wct,
@NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds,
@NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken,
@NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent,
- @Nullable Bundle activityOptions, @NonNull SplitRule rule,
- @WindowingMode int windowingMode) {
+ @Nullable Bundle activityOptions, @NonNull SplitRule rule) {
final IBinder ownerToken = launchingActivity.getActivityToken();
// Create or resize the launching TaskFragment.
if (mFragmentInfos.containsKey(launchingFragmentToken)) {
resizeTaskFragment(wct, launchingFragmentToken, launchingFragmentBounds);
- wct.setWindowingMode(mFragmentInfos.get(launchingFragmentToken).getToken(),
- windowingMode);
} else {
createTaskFragmentAndReparentActivity(wct, launchingFragmentToken, ownerToken,
- launchingFragmentBounds, windowingMode, launchingActivity);
+ launchingFragmentBounds, WINDOWING_MODE_MULTI_WINDOW, launchingActivity);
}
// Create a TaskFragment for the secondary activity.
createTaskFragmentAndStartActivity(wct, secondaryFragmentToken, ownerToken,
- secondaryFragmentBounds, windowingMode, activityIntent,
+ secondaryFragmentBounds, WINDOWING_MODE_MULTI_WINDOW, activityIntent,
activityOptions);
// Set adjacent to each other so that the containers below will be invisible.
@@ -157,7 +153,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
void expandTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) {
resizeTaskFragment(wct, fragmentToken, new Rect());
setAdjacentTaskFragments(wct, fragmentToken, null /* secondary */, null /* splitRule */);
- setWindowingMode(wct, fragmentToken, WINDOWING_MODE_UNDEFINED);
}
/**
@@ -260,15 +255,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
wct.setBounds(mFragmentInfos.get(fragmentToken).getToken(), bounds);
}
- private void setWindowingMode(WindowContainerTransaction wct, IBinder fragmentToken,
- @WindowingMode int windowingMode) {
- if (!mFragmentInfos.containsKey(fragmentToken)) {
- throw new IllegalArgumentException(
- "Can't find an existing TaskFragment with fragmentToken=" + fragmentToken);
- }
- wct.setWindowingMode(mFragmentInfos.get(fragmentToken).getToken(), windowingMode);
- }
-
void deleteTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) {
if (!mFragmentInfos.containsKey(fragmentToken)) {
throw new IllegalArgumentException(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index b370e59ac7c8..2328f76a7130 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -257,9 +257,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (taskContainer == null) {
return;
}
- final boolean wasInPip = taskContainer.isInPictureInPicture();
+ final boolean wasInPip = isInPictureInPicture(taskContainer.getConfiguration());
final boolean isInPIp = isInPictureInPicture(config);
- taskContainer.setWindowingMode(config.windowConfiguration.getWindowingMode());
+ taskContainer.setConfiguration(config);
// We need to check the animation override when enter/exit PIP or has bounds changed.
boolean shouldUpdateAnimationOverride = wasInPip != isInPIp;
@@ -278,9 +278,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* bounds is large enough for at least one split rule.
*/
private void updateAnimationOverride(@NonNull TaskContainer taskContainer) {
- if (!taskContainer.isTaskBoundsInitialized()
- || !taskContainer.isWindowingModeInitialized()) {
- // We don't know about the Task bounds/windowingMode yet.
+ if (!taskContainer.isTaskBoundsInitialized()) {
+ // We don't know about the Task bounds yet.
return;
}
@@ -294,7 +293,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private boolean supportSplit(@NonNull TaskContainer taskContainer) {
// No split inside PIP.
- if (taskContainer.isInPictureInPicture()) {
+ if (isInPictureInPicture(taskContainer.getConfiguration())) {
return false;
}
// Check if the parent container bounds can support any split rule.
@@ -462,12 +461,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (!taskContainer.setTaskBounds(taskBounds)) {
Log.w(TAG, "Can't find bounds from activity=" + activityInTask);
}
+ updateAnimationOverride(taskContainer);
}
- if (!taskContainer.isWindowingModeInitialized()) {
- taskContainer.setWindowingMode(activityInTask.getResources().getConfiguration()
- .windowConfiguration.getWindowingMode());
- }
- updateAnimationOverride(taskContainer);
return container;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index e64e5d1c66d5..ee5a322eed4f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -16,11 +16,10 @@
package androidx.window.extensions.embedding;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.app.Activity;
import android.app.WindowConfiguration;
-import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
@@ -112,16 +111,13 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
primaryActivity, primaryRectBounds, null);
// Create new empty task fragment
- final int taskId = primaryContainer.getTaskId();
final TaskFragmentContainer secondaryContainer = mController.newContainer(
- null /* activity */, primaryActivity, taskId);
+ null /* activity */, primaryActivity, primaryContainer.getTaskId());
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
rule, isLtr(primaryActivity, rule));
- final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(secondaryRectBounds);
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
primaryActivity.getActivityToken(), secondaryRectBounds,
- windowingMode);
+ WINDOWING_MODE_MULTI_WINDOW);
secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
// Set adjacent to each other so that the containers below will be invisible.
@@ -177,7 +173,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final WindowContainerTransaction wct = new WindowContainerTransaction();
createTaskFragment(wct, newContainer.getTaskFragmentToken(),
- launchingActivity.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED);
+ launchingActivity.getActivityToken(), new Rect(), WINDOWING_MODE_MULTI_WINDOW);
applyTransaction(wct);
return newContainer;
@@ -193,17 +189,15 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull Rect bounds, @Nullable TaskFragmentContainer containerToAvoid) {
TaskFragmentContainer container = mController.getContainerWithActivity(
activity.getActivityToken());
- final int taskId = container != null ? container.getTaskId() : activity.getTaskId();
- final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(bounds);
if (container == null || container == containerToAvoid) {
- container = mController.newContainer(activity, taskId);
+ container = mController.newContainer(activity, activity.getTaskId());
+
final TaskFragmentCreationParams fragmentOptions =
createFragmentOptions(
container.getTaskFragmentToken(),
activity.getActivityToken(),
bounds,
- windowingMode);
+ WINDOWING_MODE_MULTI_WINDOW);
wct.createTaskFragment(fragmentOptions);
wct.reparentActivityToTaskFragment(container.getTaskFragmentToken(),
@@ -212,7 +206,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
container.setLastRequestedBounds(bounds);
} else {
resizeTaskFragmentIfRegistered(wct, container, bounds);
- updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
}
return container;
@@ -244,17 +237,14 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
launchingActivity.getTaskId());
}
- final int taskId = primaryContainer.getTaskId();
TaskFragmentContainer secondaryContainer = mController.newContainer(null /* activity */,
- launchingActivity, taskId);
- final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(primaryRectBounds);
+ launchingActivity, primaryContainer.getTaskId());
final WindowContainerTransaction wct = new WindowContainerTransaction();
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
rule);
startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
- activityIntent, activityOptions, rule, windowingMode);
+ activityIntent, activityOptions, rule);
if (isPlaceholder) {
// When placeholder is launched in split, we should keep the focus on the primary.
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
@@ -302,12 +292,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// When placeholder is shown in split, we should keep the focus on the primary.
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
}
- final TaskContainer taskContainer = mController.getTaskContainer(
- updatedContainer.getTaskId());
- final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
- primaryRectBounds);
- updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode);
- updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode);
}
private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
@@ -339,15 +323,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
resizeTaskFragment(wct, container.getTaskFragmentToken(), bounds);
}
- private void updateTaskFragmentWindowingModeIfRegistered(
- @NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentContainer container,
- @WindowingMode int windowingMode) {
- if (container.getInfo() != null) {
- wct.setWindowingMode(container.getInfo().getToken(), windowingMode);
- }
- }
-
@Override
void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
@Nullable Rect bounds) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 3c0762d81494..be793018d969 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -16,14 +16,9 @@
package androidx.window.extensions.embedding;
-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 android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.WindowConfiguration;
-import android.app.WindowConfiguration.WindowingMode;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArraySet;
@@ -42,9 +37,9 @@ class TaskContainer {
/** Available window bounds of this Task. */
private final Rect mTaskBounds = new Rect();
- /** Windowing mode of this Task. */
- @WindowingMode
- private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+ /** Configuration of the Task. */
+ @Nullable
+ private Configuration mConfiguration;
/** Active TaskFragments in this Task. */
final List<TaskFragmentContainer> mContainers = new ArrayList<>();
@@ -86,42 +81,13 @@ class TaskContainer {
return !mTaskBounds.isEmpty();
}
- void setWindowingMode(int windowingMode) {
- mWindowingMode = windowingMode;
- }
-
- /** Whether the Task windowing mode has been initialized. */
- boolean isWindowingModeInitialized() {
- return mWindowingMode != WINDOWING_MODE_UNDEFINED;
- }
-
- /**
- * Returns the windowing mode for the TaskFragments below this Task, which should be split with
- * other TaskFragments.
- *
- * @param taskFragmentBounds Requested bounds for the TaskFragment. It will be empty when
- * the pair of TaskFragments are stacked due to the limited space.
- */
- @WindowingMode
- int getWindowingModeForSplitTaskFragment(@Nullable Rect taskFragmentBounds) {
- // Only set to multi-windowing mode if the pair are showing side-by-side. Otherwise, it
- // will be set to UNDEFINED which will then inherit the Task windowing mode.
- if (taskFragmentBounds == null || taskFragmentBounds.isEmpty()) {
- return WINDOWING_MODE_UNDEFINED;
- }
- // We use WINDOWING_MODE_MULTI_WINDOW when the Task is fullscreen.
- // However, when the Task is in other multi windowing mode, such as Freeform, we need to
- // have the activity windowing mode to match the Task, otherwise things like
- // DecorCaptionView won't work correctly. As a result, have the TaskFragment to be in the
- // Task windowing mode if the Task is in multi window.
- // TODO we won't need this anymore after we migrate Freeform caption to WM Shell.
- return WindowConfiguration.inMultiWindowMode(mWindowingMode)
- ? mWindowingMode
- : WINDOWING_MODE_MULTI_WINDOW;
+ @Nullable
+ Configuration getConfiguration() {
+ return mConfiguration;
}
- boolean isInPictureInPicture() {
- return mWindowingMode == WINDOWING_MODE_PINNED;
+ void setConfiguration(@Nullable Configuration configuration) {
+ mConfiguration = configuration;
}
/** Whether there is any {@link TaskFragmentContainer} below this Task. */
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 1f12c4484159..b06ce4c19d5c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -16,23 +16,15 @@
package androidx.window.extensions.embedding;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import android.content.res.Configuration;
-import android.graphics.Point;
import android.platform.test.annotations.Presubmit;
-import android.window.TaskFragmentInfo;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -43,8 +35,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-
/**
* Test class for {@link JetpackTaskFragmentOrganizer}.
*
@@ -58,8 +48,6 @@ public class JetpackTaskFragmentOrganizerTest {
private static final int TASK_ID = 10;
@Mock
- private WindowContainerTransaction mTransaction;
- @Mock
private JetpackTaskFragmentOrganizer.TaskFragmentCallback mCallback;
private JetpackTaskFragmentOrganizer mOrganizer;
@@ -103,24 +91,4 @@ public class JetpackTaskFragmentOrganizerTest {
verify(mOrganizer).unregisterRemoteAnimations(TASK_ID);
}
-
- @Test
- public void testExpandTaskFragment() {
- final TaskFragmentContainer container = new TaskFragmentContainer(null, TASK_ID);
- final TaskFragmentInfo info = createMockInfo(container);
- mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info);
- container.setInfo(info);
-
- mOrganizer.expandTaskFragment(mTransaction, container.getTaskFragmentToken());
-
- verify(mTransaction).setWindowingMode(container.getInfo().getToken(),
- WINDOWING_MODE_UNDEFINED);
- }
-
- private TaskFragmentInfo createMockInfo(TaskFragmentContainer container) {
- return new TaskFragmentInfo(container.getTaskFragmentToken(),
- mock(WindowContainerToken.class), new Configuration(), 0 /* runningActivityCount */,
- false /* isVisible */, new ArrayList<>(), new Point(),
- false /* isTaskClearedForReuse */, false /* isTaskFragmentClearedForPip */);
- }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index c7feb7e59de3..9fb08dffbab8 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -16,13 +16,6 @@
package androidx.window.extensions.embedding;
-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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -71,56 +64,6 @@ public class TaskContainerTest {
}
@Test
- public void testIsWindowingModeInitialized() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
-
- assertFalse(taskContainer.isWindowingModeInitialized());
-
- taskContainer.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- assertTrue(taskContainer.isWindowingModeInitialized());
- }
-
- @Test
- public void testGetWindowingModeForSplitTaskFragment() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
- final Rect splitBounds = new Rect(0, 0, 500, 1000);
-
- assertEquals(WINDOWING_MODE_MULTI_WINDOW,
- taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
-
- taskContainer.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- assertEquals(WINDOWING_MODE_MULTI_WINDOW,
- taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
-
- taskContainer.setWindowingMode(WINDOWING_MODE_FREEFORM);
-
- assertEquals(WINDOWING_MODE_FREEFORM,
- taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
-
- // Empty bounds means the split pair are stacked, so it should be UNDEFINED which will then
- // inherit the Task windowing mode
- assertEquals(WINDOWING_MODE_UNDEFINED,
- taskContainer.getWindowingModeForSplitTaskFragment(new Rect()));
- }
-
- @Test
- public void testIsInPictureInPicture() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
-
- assertFalse(taskContainer.isInPictureInPicture());
-
- taskContainer.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- assertFalse(taskContainer.isInPictureInPicture());
-
- taskContainer.setWindowingMode(WINDOWING_MODE_PINNED);
-
- assertTrue(taskContainer.isInPictureInPicture());
- }
-
- @Test
public void testIsEmpty() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index a2b35fc9211a..a089585a5a00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -454,8 +454,11 @@ public class BubbleExpandedView extends LinearLayout {
p.beginRecording(mOverflowView.getWidth(), mOverflowView.getHeight()));
p.endRecording();
Bitmap snapshot = Bitmap.createBitmap(p);
- return new SurfaceControl.ScreenshotHardwareBuffer(snapshot.getHardwareBuffer(),
- snapshot.getColorSpace(), false /* containsSecureLayers */);
+ return new SurfaceControl.ScreenshotHardwareBuffer(
+ snapshot.getHardwareBuffer(),
+ snapshot.getColorSpace(),
+ false /* containsSecureLayers */,
+ false /* containsHdrLayers */);
}
if (mTaskView == null || mTaskView.getSurfaceControl() == null) {
return null;
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 9d6e34ddd9d7..91f9d2522397 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
@@ -1786,15 +1786,18 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onNoLongerSupportMultiWindow() {
if (mMainStage.isActive()) {
+ final boolean isMainStage = mMainStageListener == this;
if (!ENABLE_SHELL_TRANSITIONS) {
- StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
+ StageCoordinator.this.exitSplitScreen(isMainStage ? mMainStage : mSideStage,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+ return;
}
+ final int stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
final WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ prepareExitSplitScreen(stageType, wct);
mSplitTransitions.startDismissTransition(null /* transition */, wct,
- StageCoordinator.this, STAGE_TYPE_UNDEFINED,
+ StageCoordinator.this, stageType,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index b7c80df03ce2..c9cab39b7d8b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -16,8 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -31,6 +29,7 @@ import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSuppo
import org.junit.After
import org.junit.Before
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -76,23 +75,23 @@ class AppPairsTestCannotPairNonResizeableApps(
resetMultiWindowConfig(instrumentation)
}
- @FlakyTest
+ @Ignore
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest(bugId = 206753786)
+ @Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
- @Presubmit
+ @Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
- @Presubmit
+ @Ignore
@Test
fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
- @Presubmit
+ @Ignore
@Test
fun onlyResizeableAppWindowVisible() {
val nonResizeableApp = nonResizeableApp
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 1ac664eb1f62..60c32c99d1ff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -16,8 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -29,6 +27,7 @@ import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -57,23 +56,23 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
}
}
- @Presubmit
+ @Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
- @FlakyTest
+ @Ignore
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest(bugId = 206753786)
+ @Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
- @Presubmit
+ @Ignore
@Test
fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
- @Presubmit
+ @Ignore
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
@@ -82,7 +81,7 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
}
}
- @FlakyTest
+ @Ignore
@Test
fun appsEndingBounds() {
testSpec.assertLayersEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index 57bcbc093a62..24869a802167 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -16,9 +16,7 @@
package com.android.wm.shell.flicker.apppairs
-import android.platform.test.annotations.Presubmit
import android.view.Display
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -33,6 +31,7 @@ import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSuppo
import org.junit.After
import org.junit.Before
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -89,23 +88,23 @@ class AppPairsTestSupportPairNonResizeableApps(
resetMultiWindowConfig(instrumentation)
}
- @Presubmit
+ @Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
- @FlakyTest
+ @Ignore
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest(bugId = 206753786)
+ @Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
- @Presubmit
+ @Ignore
@Test
fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
- @Presubmit
+ @Ignore
@Test
fun bothAppWindowVisible() {
val nonResizeableApp = nonResizeableApp
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 12910dd74271..007415d19860 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -17,8 +17,6 @@
package com.android.wm.shell.flicker.apppairs
import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -30,6 +28,7 @@ import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -65,19 +64,19 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
}
}
- @FlakyTest
+ @Ignore
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest(bugId = 206753786)
+ @Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
- @Presubmit
+ @Ignore
@Test
fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
- @Presubmit
+ @Ignore
@Test
fun bothAppWindowsInvisible() {
testSpec.assertWmEnd {
@@ -86,7 +85,7 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
}
}
- @FlakyTest
+ @Ignore
@Test
fun appsStartingBounds() {
testSpec.assertLayersStart {
@@ -98,7 +97,7 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
}
}
- @FlakyTest
+ @Ignore
@Test
fun appsEndingBounds() {
testSpec.assertLayersEnd {
@@ -107,7 +106,7 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
}
}
- @Presubmit
+ @Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 863c3aff63a2..3e17948b4a84 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -18,9 +18,7 @@ package com.android.wm.shell.flicker.apppairs
import android.app.Instrumentation
import android.content.Context
-import android.platform.test.annotations.Presubmit
import android.system.helpers.ActivityHelper
-import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
@@ -42,6 +40,7 @@ import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.testapp.Components
import org.junit.After
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
@@ -145,35 +144,35 @@ abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter)
append("$primaryApp $secondaryApp")
}
- @FlakyTest(bugId = 186510496)
+ @Ignore
@Test
open fun navBarLayerIsVisible() {
testSpec.navBarLayerIsVisible()
}
- @Presubmit
+ @Ignore
@Test
open fun statusBarLayerIsVisible() {
testSpec.statusBarLayerIsVisible()
}
- @Presubmit
+ @Ignore
@Test
open fun navBarWindowIsVisible() {
testSpec.navBarWindowIsVisible()
}
- @Presubmit
+ @Ignore
@Test
open fun statusBarWindowIsVisible() {
testSpec.statusBarWindowIsVisible()
}
- @Presubmit
+ @Ignore
@Test
open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @Ignore
@Test
open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index f2f4877a44c4..b0c3ba20d948 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -16,9 +16,7 @@
package com.android.wm.shell.flicker.apppairs
-import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -32,6 +30,7 @@ import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -60,15 +59,15 @@ class RotateTwoLaunchedAppsInAppPairsMode(
}
}
- @Presubmit
+ @Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
- @Presubmit
+ @Ignore
@Test
override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
- @Presubmit
+ @Ignore
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
@@ -77,23 +76,23 @@ class RotateTwoLaunchedAppsInAppPairsMode(
}
}
- @Presubmit
+ @Ignore
@Test
fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
- @Presubmit
+ @Ignore
@Test
fun appPairsPrimaryBoundsIsVisibleAtEnd() =
testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
primaryApp.component)
- @Presubmit
+ @Ignore
@Test
fun appPairsSecondaryBoundsIsVisibleAtEnd() =
testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
secondaryApp.component)
- @FlakyTest(bugId = 206753786)
+ @Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index 2a173d16004f..ae56c7732a4d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -16,9 +16,7 @@
package com.android.wm.shell.flicker.apppairs
-import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -32,6 +30,7 @@ import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -60,31 +59,31 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
}
}
- @Presubmit
+ @Ignore
@Test
fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
- @Presubmit
+ @Ignore
@Test
override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
- @Presubmit
+ @Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
- @Presubmit
+ @Ignore
@Test
override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
- @Presubmit
+ @Ignore
@Test
override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
- @FlakyTest(bugId = 206753786)
+ @Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
- @Presubmit
+ @Ignore
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
@@ -93,13 +92,13 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
}
}
- @Presubmit
+ @Ignore
@Test
fun appPairsPrimaryBoundsIsVisibleAtEnd() =
testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
primaryApp.component)
- @Presubmit
+ @Ignore
@Test
fun appPairsSecondaryBoundsIsVisibleAtEnd() =
testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
index 670fbd810907..b1f1c9e539df 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.apppairs
import android.view.Surface
-import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
@@ -26,6 +25,7 @@ import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTrans
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.Assume.assumeFalse
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
abstract class RotateTwoLaunchedAppsTransition(
@@ -62,13 +62,13 @@ abstract class RotateTwoLaunchedAppsTransition(
super.setup()
}
- @FlakyTest
+ @Ignore
@Test
override fun navBarLayerIsVisible() {
super.navBarLayerIsVisible()
}
- @FlakyTest
+ @Ignore
@Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
index 7c3f5a566bdb..a596a9a59ee8 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
@@ -28,7 +28,8 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"/>
+ android:layout_weight="1"
+ android:hyphenationFrequency="normalFast"/>
<View
android:id="@+id/divider1"
@@ -43,7 +44,8 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"/>
+ android:layout_weight="1"
+ android:hyphenationFrequency="normalFast"/>
<View
android:id="@+id/divider2"
@@ -58,7 +60,8 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"/>
+ android:layout_weight="1"
+ android:hyphenationFrequency="normalFast"/>
<View
android:id="@+id/divider3"
@@ -73,5 +76,6 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"/>
+ android:layout_weight="1"
+ android:hyphenationFrequency="normalFast"/>
</LinearLayout>
diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java
index b038d59e9fe4..5693c2f22d1e 100644
--- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java
+++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/WorkPolicyUtils.java
@@ -42,15 +42,13 @@ public class WorkPolicyUtils {
private static final int USER_NULL = -10000;
public WorkPolicyUtils(
- Context applicationContext,
- PackageManager mPm,
- UserManager mUm,
- DevicePolicyManager mDpm
+ Context context
) {
- mContext = applicationContext;
- mPackageManager = mPm;
- mUserManager = mUm;
- mDevicePolicyManager = mDpm;
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mDevicePolicyManager =
+ (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 440a54435fc3..affcf585904a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -332,6 +332,9 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
*/
@Override
public int compareTo(MediaDevice another) {
+ if (another == null) {
+ return -1;
+ }
// Check Bluetooth device is have same connection state
if (isConnected() ^ another.isConnected()) {
if (isConnected()) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
index 552fa11a42b7..3514932d4e8d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
@@ -48,6 +48,12 @@ public class MediaOutputConstants {
"com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG";
/**
+ * An intent action to launch media output broadcast dialog.
+ */
+ public static final String ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG =
+ "com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG";
+
+ /**
* Settings package name.
*/
public static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index f949f99673d9..3029781f3e99 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -117,6 +117,9 @@ public class SecureSettings {
Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD,
Settings.Secure.FACE_UNLOCK_APP_ENABLED,
Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
+ Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
+ Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
+ Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
Settings.Secure.VR_DISPLAY_MODE,
Settings.Secure.NOTIFICATION_BADGING,
Settings.Secure.NOTIFICATION_DISMISS_RTL,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 2bdf81912709..a4da49713f87 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -173,6 +173,9 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.SHOW_MEDIA_WHEN_BYPASSING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_APP_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_WAKE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index f1b23d5733af..4b7d0d2a3c74 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -597,6 +597,7 @@ public class SettingsBackupTest {
Settings.Global.CLOCKWORK_HOME_READY,
Settings.Global.WATCHDOG_TIMEOUT_MILLIS,
Settings.Global.MANAGED_PROVISIONING_DEFER_PROVISIONING_TO_ROLE_HOLDER,
+ Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
Settings.Global.Wearable.BATTERY_SAVER_MODE,
Settings.Global.Wearable.COMBINED_LOCATION_ENABLED,
Settings.Global.Wearable.HAS_PAY_TOKENS,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6887d037c6f4..290ce345694e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -931,6 +931,7 @@
android:exported="true">
<intent-filter>
<action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG" />
+ <action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG" />
<action android:name="com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG" />
</intent-filter>
</receiver>
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 46dad02ddb45..dd45b6f39bdd 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -23,6 +23,7 @@ import com.android.internal.graphics.ColorUtils
import com.android.internal.graphics.cam.Cam
import com.android.internal.graphics.cam.CamUtils.lstarFromInt
import kotlin.math.absoluteValue
+import kotlin.math.max
import kotlin.math.roundToInt
const val TAG = "ColorScheme"
@@ -78,36 +79,32 @@ internal class HueSubtract(val amountDegrees: Double) : Hue {
}
internal class HueVibrantSecondary() : Hue {
- val hueToRotations = listOf(Pair(24, 15), Pair(53, 15), Pair(91, 15), Pair(123, 15),
- Pair(141, 15), Pair(172, 15), Pair(198, 15), Pair(234, 18), Pair(272, 18),
- Pair(302, 18), Pair(329, 30), Pair(354, 15))
+ val hueToRotations = listOf(Pair(0, 18), Pair(41, 15), Pair(61, 10), Pair(101, 12),
+ Pair(131, 15), Pair(181, 18), Pair(251, 15), Pair(301, 12))
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
}
internal class HueVibrantTertiary() : Hue {
- val hueToRotations = listOf(Pair(24, 30), Pair(53, 30), Pair(91, 15), Pair(123, 30),
- Pair(141, 27), Pair(172, 27), Pair(198, 30), Pair(234, 35), Pair(272, 30),
- Pair(302, 30), Pair(329, 60), Pair(354, 30))
+ val hueToRotations = listOf(Pair(0, 35), Pair(41, 30), Pair(61, 20), Pair(101, 25),
+ Pair(131, 30), Pair(181, 35), Pair(251, 30), Pair(301, 25))
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
}
internal class HueExpressiveSecondary() : Hue {
- val hueToRotations = listOf(Pair(24, 95), Pair(53, 45), Pair(91, 45), Pair(123, 20),
- Pair(141, 45), Pair(172, 45), Pair(198, 15), Pair(234, 15),
- Pair(272, 45), Pair(302, 45), Pair(329, 45), Pair(354, 45))
+ val hueToRotations = listOf(Pair(0, 45), Pair(21, 95), Pair(51, 45), Pair(121, 20),
+ Pair(141, 45), Pair(191, 90), Pair(271, 45), Pair(321, 45))
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
}
internal class HueExpressiveTertiary() : Hue {
- val hueToRotations = listOf(Pair(24, 20), Pair(53, 20), Pair(91, 20), Pair(123, 45),
- Pair(141, 20), Pair(172, 20), Pair(198, 90), Pair(234, 90), Pair(272, 20),
- Pair(302, 20), Pair(329, 120), Pair(354, 120))
+ val hueToRotations = listOf(Pair(0, 120), Pair(21, 120), Pair(51, 20), Pair(121, 45),
+ Pair(141, 20), Pair(191, 15), Pair(271, 20), Pair(321, 120))
override fun get(sourceColor: Cam): Double {
return getHueRotation(sourceColor.hue, hueToRotations)
}
@@ -140,18 +137,15 @@ internal interface Chroma {
}
}
-internal class ChromaConstant(val chroma: Double) : Chroma {
+internal class ChromaMinimum(val chroma: Double) : Chroma {
override fun get(sourceColor: Cam): Double {
- return chroma
+ return max(sourceColor.chroma.toDouble(), chroma)
}
}
-internal class ChromaExpressiveNeutral() : Chroma {
- val hueToChromas = listOf(Pair(24, 8), Pair(53, 8), Pair(91, 8), Pair(123, 8),
- Pair(141, 6), Pair(172, 6), Pair(198, 8), Pair(234, 8), Pair(272, 8),
- Pair(302, 8), Pair(329, 8), Pair(354, 8))
+internal class ChromaConstant(val chroma: Double) : Chroma {
override fun get(sourceColor: Cam): Double {
- return getSpecifiedChroma(sourceColor.hue, hueToChromas)
+ return chroma
}
}
@@ -187,17 +181,17 @@ enum class Style(internal val coreSpec: CoreSpec) {
n2 = TonalSpec(HueSource(), ChromaConstant(8.0))
)),
VIBRANT(CoreSpec(
- a1 = TonalSpec(HueSource(), ChromaConstant(48.0)),
+ a1 = TonalSpec(HueSource(), ChromaMinimum(48.0)),
a2 = TonalSpec(HueVibrantSecondary(), ChromaConstant(24.0)),
a3 = TonalSpec(HueVibrantTertiary(), ChromaConstant(32.0)),
- n1 = TonalSpec(HueSource(), ChromaConstant(6.0)),
+ n1 = TonalSpec(HueSource(), ChromaConstant(10.0)),
n2 = TonalSpec(HueSource(), ChromaConstant(12.0))
)),
EXPRESSIVE(CoreSpec(
a1 = TonalSpec(HueAdd(240.0), ChromaConstant(40.0)),
a2 = TonalSpec(HueExpressiveSecondary(), ChromaConstant(24.0)),
- a3 = TonalSpec(HueExpressiveTertiary(), ChromaConstant(40.0)),
- n1 = TonalSpec(HueAdd(15.0), ChromaExpressiveNeutral()),
+ a3 = TonalSpec(HueExpressiveTertiary(), ChromaConstant(32.0)),
+ n1 = TonalSpec(HueAdd(15.0), ChromaConstant(8.0)),
n2 = TonalSpec(HueAdd(15.0), ChromaConstant(12.0))
)),
RAINBOW(CoreSpec(
@@ -231,8 +225,13 @@ class ColorScheme(
constructor(@ColorInt seed: Int, darkTheme: Boolean):
this(seed, darkTheme, Style.TONAL_SPOT)
- constructor(wallpaperColors: WallpaperColors, darkTheme: Boolean):
- this(getSeedColor(wallpaperColors), darkTheme)
+ @JvmOverloads
+ constructor(
+ wallpaperColors: WallpaperColors,
+ darkTheme: Boolean,
+ style: Style = Style.TONAL_SPOT
+ ):
+ this(getSeedColor(wallpaperColors), darkTheme, style)
val allAccentColors: List<Int>
get() {
diff --git a/packages/SystemUI/res-keyguard/layout/fgs_footer.xml b/packages/SystemUI/res-keyguard/layout/fgs_footer.xml
index 9ffafbc8cc09..6757acf7014a 100644
--- a/packages/SystemUI/res-keyguard/layout/fgs_footer.xml
+++ b/packages/SystemUI/res-keyguard/layout/fgs_footer.xml
@@ -55,6 +55,15 @@
android:textColor="?android:attr/textColorSecondary"/>
<ImageView
+ android:id="@+id/fgs_new"
+ android:layout_width="12dp"
+ android:layout_height="12dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/fgs_dot"
+ android:contentDescription="@string/fgs_dot_content_description"
+ />
+
+ <ImageView
android:id="@+id/footer_icon"
android:layout_width="@dimen/qs_footer_icon_size"
android:layout_height="@dimen/qs_footer_icon_size"
@@ -82,7 +91,7 @@
android:textColor="?android:attr/textColorPrimary"
android:textSize="18sp"/>
<ImageView
- android:id="@+id/fgs_new"
+ android:id="@+id/fgs_collapsed_new"
android:layout_width="12dp"
android:layout_height="12dp"
android:scaleType="fitCenter"
diff --git a/packages/SystemUI/res/layout/media_output_broadcast_update_dialog.xml b/packages/SystemUI/res/layout/media_output_broadcast_update_dialog.xml
index 8b7a019b791b..89dcbcc5929f 100644
--- a/packages/SystemUI/res/layout/media_output_broadcast_update_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_broadcast_update_dialog.xml
@@ -19,11 +19,19 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="?android:attr/dialogPreferredPadding"
- android:paddingRight="?android:attr/dialogPreferredPadding">
+ android:paddingRight="?android:attr/dialogPreferredPadding"
+ android:orientation="vertical">
<EditText
android:id="@+id/broadcast_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:textAlignment="viewStart"/>
+ <TextView
+ android:id="@+id/broadcast_error_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="6dp"
+ style="@style/TextAppearance.ErrorText"
+ android:visibility="invisible"/>
</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index b85ea598dbb4..0e9700fe6b1e 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -104,7 +104,7 @@
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:indeterminate="true"
- android:layout_gravity="right|center"
+ android:layout_gravity="end|center"
android:indeterminateOnly="true"
android:visibility="gone"/>
@@ -114,7 +114,7 @@
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:indeterminate="true"
- android:layout_gravity="right|center"
+ android:layout_gravity="end|center"
android:indeterminateOnly="true"
android:importantForAccessibility="no"
android:visibility="gone"/>
@@ -125,14 +125,14 @@
android:orientation="vertical"
android:layout_width="48dp"
android:layout_height="64dp"
- android:layout_gravity="right|center"
+ android:layout_gravity="end|center"
android:gravity="center_vertical">
<CheckBox
android:id="@+id/check_box"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
- android:layout_gravity="right"
+ android:layout_gravity="end"
android:button="@drawable/ic_circle_check_box"
android:visibility="gone"
/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4a8fd1b00dde..4370432d85a9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -388,6 +388,8 @@
<dimen name="split_shade_notifications_scrim_margin_bottom">0dp</dimen>
+ <dimen name="shelf_and_lock_icon_overlap">5dp</dimen>
+
<dimen name="notification_panel_margin_horizontal">0dp</dimen>
<dimen name="brightness_mirror_height">48dp</dimen>
@@ -1179,6 +1181,10 @@
<!-- Maximum over scroll amount for the shade when transition to the full shade. -->
<dimen name="lockscreen_shade_max_over_scroll_amount">24dp</dimen>
+ <!-- Maximum over scroll amount for the shade when transition to the full shade.
+ Only used for split-shade. -->
+ <dimen name="shade_max_over_scroll_amount">@dimen/lockscreen_shade_max_over_scroll_amount</dimen>
+
<!-- Maximum overshoot for the pulse expansion -->
<dimen name="pulse_expansion_max_top_overshoot">32dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e1fa5752e2d3..6cc61b4706f6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2293,6 +2293,11 @@
<string name="media_output_broadcast_starting">Starting&#8230;</string>
<!-- The button text when Broadcast start failed [CHAR LIMIT=60] -->
<string name="media_output_broadcast_start_failed">Can\u2019t broadcast</string>
+ <!-- The error message when Broadcast name/code update failed [CHAR LIMIT=60] -->
+ <string name="media_output_broadcast_update_error">Can\u2019t save. Try again.</string>
+ <!-- The error message when Broadcast name/code update failed and can't change again[CHAR LIMIT=60] -->
+ <string name="media_output_broadcast_last_update_error">Can\u2019t save.</string>
+
<!-- Label for clip data when copying the build number off QS [CHAR LIMIT=NONE]-->
<string name="build_number_clip_data_label">Build number</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ILauncherUnlockAnimationController.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ILauncherUnlockAnimationController.aidl
index b2295b94127b..5a30901f1a8b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ILauncherUnlockAnimationController.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ILauncherUnlockAnimationController.aidl
@@ -16,17 +16,20 @@
package com.android.systemui.shared.system.smartspace;
+import android.graphics.Rect;
import com.android.systemui.shared.system.smartspace.SmartspaceState;
// Methods for System UI to interface with Launcher to perform the unlock animation.
interface ILauncherUnlockAnimationController {
// Prepares Launcher for the unlock animation by setting scale/alpha/etc. to their starting
// values.
- void prepareForUnlock(boolean willAnimateSmartspace, int selectedPage);
+ void prepareForUnlock(boolean animateSmartspace, in Rect lockscreenSmartspaceBounds,
+ int selectedPage);
// Set the unlock percentage. This is used when System UI is controlling each frame of the
- // unlock animation, such as during a swipe to unlock touch gesture.
- oneway void setUnlockAmount(float amount);
+ // unlock animation, such as during a swipe to unlock touch gesture. Will not apply this change
+ // if the unlock amount is animating unless forceIfAnimating is true.
+ oneway void setUnlockAmount(float amount, boolean forceIfAnimating);
// Play a full unlock animation from 0f to 1f. This is used when System UI is unlocking from a
// single action, such as biometric auth, and doesn't need to control individual frames.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 04c9a45af065..ea14b64b8b18 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -58,9 +58,7 @@ import com.android.systemui.util.ViewController;
import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
-import java.util.HashSet;
import java.util.Locale;
-import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.Executor;
@@ -134,14 +132,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mKeyguardUnlockAnimationListener =
new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
@Override
- public void onSmartspaceSharedElementTransitionStarted() {
- // The smartspace needs to be able to translate out of bounds in order to
- // end up where the launcher's smartspace is, while its container is being
- // swiped off the top of the screen.
- setClipChildrenForUnlock(false);
- }
-
- @Override
public void onUnlockAnimationFinished() {
// For performance reasons, reset this once the unlock animation ends.
setClipChildrenForUnlock(true);
@@ -390,41 +380,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
if (mStatusArea != null) {
PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X,
x, props, animate);
-
- // If we're unlocking with the SmartSpace shared element transition, let the controller
- // know that it should re-position our SmartSpace.
- if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
- mKeyguardUnlockAnimationController.updateLockscreenSmartSpacePosition();
- }
- }
- }
-
- /** Sets an alpha value on every child view except for the smartspace. */
- public void setChildrenAlphaExcludingSmartspace(float alpha) {
- final Set<View> excludedViews = new HashSet<>();
-
- if (mSmartspaceView != null) {
- excludedViews.add(mStatusArea);
- }
-
- // Don't change the alpha of the invisible clock.
- if (mCurrentClockSize == LARGE) {
- excludedViews.add(mClockFrame);
- } else {
- excludedViews.add(mLargeClockFrame);
- }
-
- setChildrenAlphaExcluding(alpha, excludedViews);
- }
-
- /** Sets an alpha value on every child view except for the views in the provided set. */
- public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) {
- for (int i = 0; i < mView.getChildCount(); i++) {
- final View child = mView.getChildAt(i);
-
- if (!excludedViews.contains(child)) {
- child.setAlpha(alpha);
- }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 1ede76fb1fa4..02776a295359 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -16,7 +16,6 @@
package com.android.keyguard;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.DEFAULT_DISPLAY_GROUP;
import android.app.Presentation;
import android.content.Context;
@@ -119,10 +118,9 @@ public class KeyguardDisplayManager {
if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display");
return false;
}
- if (mTmpDisplayInfo.displayGroupId != DEFAULT_DISPLAY_GROUP) {
+ if ((mTmpDisplayInfo.flags & Display.FLAG_ALWAYS_UNLOCKED) != 0) {
if (DEBUG) {
- Log.i(TAG,
- "Do not show KeyguardPresentation on a non-default group display");
+ Log.i(TAG, "Do not show KeyguardPresentation on an unlocked display");
}
return false;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 239b478949d2..804d14681812 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -68,12 +68,6 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
private final KeyguardUpdateMonitorCallback mUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
- public void onUserSwitchComplete(int userId) {
- mKeyguardSecurityContainerController.showPrimarySecurityScreen(
- false /* turning off */);
- }
-
- @Override
public void onTrustGrantedWithFlags(int flags, int userId) {
if (userId != KeyguardUpdateMonitor.getCurrentUser()) return;
boolean bouncerVisible = mView.isVisibleToUser();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 75425e1e6ca3..6a68c70c6acb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -28,6 +28,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -168,6 +169,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
private final FalsingCollector mFalsingCollector;
private final DevicePostureController mDevicePostureController;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Inject
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -178,7 +180,8 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
@Main Resources resources, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
EmergencyButtonController.Factory emergencyButtonControllerFactory,
- DevicePostureController devicePostureController) {
+ DevicePostureController devicePostureController,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -191,6 +194,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
mEmergencyButtonControllerFactory = emergencyButtonControllerFactory;
mFalsingCollector = falsingCollector;
mDevicePostureController = devicePostureController;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
}
/** Create a new {@link KeyguardInputViewController}. */
@@ -211,7 +215,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
- mFalsingCollector);
+ mFalsingCollector, mStatusBarKeyguardViewManager);
} else if (keyguardInputView instanceof KeyguardPINView) {
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 0529cdbcbb13..19035264db7f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -43,6 +43,7 @@ import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.List;
@@ -55,6 +56,7 @@ public class KeyguardPasswordViewController
private final KeyguardSecurityCallback mKeyguardSecurityCallback;
private final InputMethodManager mInputMethodManager;
private final DelayableExecutor mMainExecutor;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final boolean mShowImeAtScreenOn;
private EditText mPasswordEntry;
private ImageView mSwitchImeButton;
@@ -116,13 +118,15 @@ public class KeyguardPasswordViewController
EmergencyButtonController emergencyButtonController,
@Main DelayableExecutor mainExecutor,
@Main Resources resources,
- FalsingCollector falsingCollector) {
+ FalsingCollector falsingCollector,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
emergencyButtonController);
mKeyguardSecurityCallback = keyguardSecurityCallback;
mInputMethodManager = inputMethodManager;
mMainExecutor = mainExecutor;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on);
mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
mSwitchImeButton = mView.findViewById(R.id.switch_ime_button);
@@ -205,6 +209,10 @@ public class KeyguardPasswordViewController
}
private void showInput() {
+ if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ return;
+ }
+
mView.post(() -> {
if (mView.isShown()) {
mPasswordEntry.requestFocus();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index ce4aad882df9..28a3dbbf6c83 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -97,6 +97,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
+ private UserSwitcherController.UserSwitchCallback mUserSwitchCallback =
+ () -> showPrimarySecurityScreen(false);
@VisibleForTesting
final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
@@ -295,6 +297,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mView.setSwipeListener(mSwipeListener);
mView.addMotionEventListener(mGlobalTouchListener);
mConfigurationController.addCallback(mConfigurationListener);
+ mUserSwitcherController.addUserSwitchCallback(mUserSwitchCallback);
}
@Override
@@ -302,6 +305,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mConfigurationController.removeCallback(mConfigurationListener);
mView.removeMotionEventListener(mGlobalTouchListener);
+ mUserSwitcherController.removeUserSwitchCallback(mUserSwitchCallback);
}
/** */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 853d7402a1f8..cb3172dabdb1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -47,7 +47,6 @@ public class KeyguardStatusView extends GridLayout {
private float mDarkAmount = 0;
private int mTextColor;
- private float mChildrenAlphaExcludingSmartSpace = 1f;
public KeyguardStatusView(Context context) {
this(context, null, 0);
@@ -95,23 +94,6 @@ public class KeyguardStatusView extends GridLayout {
mClockView.setTextColor(blendedTextColor);
}
- public void setChildrenAlphaExcludingClockView(float alpha) {
- setChildrenAlphaExcluding(alpha, Set.of(mClockView));
- }
-
- /** Sets an alpha value on every view except for the views in the provided set. */
- public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) {
- mChildrenAlphaExcludingSmartSpace = alpha;
-
- for (int i = 0; i < mStatusViewContainer.getChildCount(); i++) {
- final View child = mStatusViewContainer.getChildAt(i);
-
- if (!excludedViews.contains(child)) {
- child.setAlpha(alpha);
- }
- }
- }
-
/** Sets a translationY value on every child view except for the media view. */
public void setChildrenTranslationYExcludingMediaView(float translationY) {
setChildrenTranslationYExcluding(translationY, Set.of(mMediaHostContainer));
@@ -128,10 +110,6 @@ public class KeyguardStatusView extends GridLayout {
}
}
- public float getChildrenAlphaExcludingSmartSpace() {
- return mChildrenAlphaExcludingSmartSpace;
- }
-
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardStatusView:");
pw.println(" mDarkAmount: " + mDarkAmount);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 14c9cb2022bc..083f2fe53d17 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -20,7 +20,6 @@ import android.graphics.Rect;
import android.util.Slog;
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -49,10 +48,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
private final KeyguardClockSwitchController mKeyguardClockSwitchController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
- private final DozeParameters mDozeParameters;
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
- private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
- private final KeyguardStateController mKeyguardStateController;
private final Rect mClipBounds = new Rect();
@Inject
@@ -64,18 +60,14 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController,
DozeParameters dozeParameters,
- KeyguardUnlockAnimationController keyguardUnlockAnimationController,
ScreenOffAnimationController screenOffAnimationController) {
super(keyguardStatusView);
mKeyguardSliceViewController = keyguardSliceViewController;
mKeyguardClockSwitchController = keyguardClockSwitchController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mConfigurationController = configurationController;
- mDozeParameters = dozeParameters;
- mKeyguardStateController = keyguardStateController;
mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
dozeParameters, screenOffAnimationController, /* animateYPos= */ true);
- mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
}
@Override
@@ -87,14 +79,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
protected void onViewAttached() {
mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
mConfigurationController.addCallback(mConfigurationListener);
- mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
}
@Override
protected void onViewDetached() {
mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
mConfigurationController.removeCallback(mConfigurationListener);
- mKeyguardStateController.removeCallback(mKeyguardStateControllerCallback);
}
/**
@@ -148,24 +138,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
*/
public void setAlpha(float alpha) {
if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
- // If we're capable of performing the SmartSpace shared element transition, and we are
- // going to (we're swiping to dismiss vs. bringing up the PIN screen), then fade out
- // everything except for the SmartSpace.
- if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
- mView.setChildrenAlphaExcludingClockView(alpha);
- mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(alpha);
- } else if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
- // Otherwise, we can just set the alpha for the entire container.
- mView.setAlpha(alpha);
-
- // If we previously unlocked with the shared element transition, some child views
- // might still have alpha = 0f. Set them back to 1f since we're just using the
- // parent container's alpha.
- if (mView.getChildrenAlphaExcludingSmartSpace() < 1f) {
- mView.setChildrenAlphaExcludingClockView(1f);
- mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(1f);
- }
- }
+ mView.setAlpha(alpha);
}
}
@@ -289,19 +262,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
};
- private KeyguardStateController.Callback mKeyguardStateControllerCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- // If we explicitly re-show the keyguard, make sure that all the child views are
- // visible. They might have been animating out as part of the SmartSpace shared
- // element transition.
- if (mKeyguardStateController.isShowing()) {
- mView.setChildrenAlphaExcludingClockView(1f);
- }
- }
- };
-
/**
* Rect that specifies how KSV should be clipped, on its parent's coordinates.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 239730d18934..95cda8b9dfca 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -668,7 +668,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mVelocityTracker.recycle();
mVelocityTracker = null;
}
- mVibrator.cancel();
}
private boolean inLockIconArea(MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index f925eaa0e40b..eb6705a2e979 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -15,6 +15,8 @@
*/
package com.android.keyguard;
+import static com.android.systemui.util.ColorUtilKt.getPrivateAttrColorIfUnset;
+
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
@@ -152,7 +154,7 @@ class NumPadAnimator {
ContextThemeWrapper ctw = new ContextThemeWrapper(context, mStyle);
TypedArray a = ctw.obtainStyledAttributes(customAttrs);
- mNormalColor = Utils.getPrivateAttrColorIfUnset(ctw, a, 0, 0,
+ mNormalColor = getPrivateAttrColorIfUnset(ctw, a, 0, 0,
com.android.internal.R.attr.colorSurface);
mHighlightColor = a.getColor(1, 0);
a.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
index ccb5b1146a1c..e51a63fbcd10 100644
--- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
+++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
@@ -228,14 +228,20 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView {
if (pendingRotationChange) {
return
}
+ val m = Matrix()
+ // Apply display ratio.
+ val physicalPixelDisplaySizeRatio = getPhysicalPixelDisplaySizeRatio()
+ m.postScale(physicalPixelDisplaySizeRatio, physicalPixelDisplaySizeRatio)
+
+ // Apply rotation.
val lw: Int = displayInfo.logicalWidth
val lh: Int = displayInfo.logicalHeight
val flipped = (displayInfo.rotation == Surface.ROTATION_90 ||
displayInfo.rotation == Surface.ROTATION_270)
val dw = if (flipped) lh else lw
val dh = if (flipped) lw else lh
- val m = Matrix()
transformPhysicalToLogicalCoordinates(displayInfo.rotation, dw, dh, m)
+
if (!protectionPathOrig.isEmpty) {
// Reset the protection path so we don't aggregate rotations
protectionPath.set(protectionPathOrig)
@@ -244,6 +250,14 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView {
}
}
+ @VisibleForTesting
+ open fun getPhysicalPixelDisplaySizeRatio(): Float {
+ displayInfo.displayCutout?.let {
+ return it.cutoutPathParserInfo.physicalPixelDisplaySizeRatio
+ }
+ return 1f
+ }
+
private fun displayModeChanged(oldMode: Display.Mode?, newMode: Display.Mode?): Boolean {
if (oldMode == null) {
return true
@@ -265,17 +279,17 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView {
out: Matrix
) {
when (rotation) {
- Surface.ROTATION_0 -> out.reset()
+ Surface.ROTATION_0 -> return
Surface.ROTATION_90 -> {
- out.setRotate(270f)
+ out.postRotate(270f)
out.postTranslate(0f, physicalWidth.toFloat())
}
Surface.ROTATION_180 -> {
- out.setRotate(180f)
+ out.postRotate(180f)
out.postTranslate(physicalWidth.toFloat(), physicalHeight.toFloat())
}
Surface.ROTATION_270 -> {
- out.setRotate(90f)
+ out.postRotate(90f)
out.postTranslate(physicalHeight.toFloat(), 0f)
}
else -> throw IllegalArgumentException("Unknown rotation: $rotation")
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index 59c658fa43d2..49e378e4a76f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -22,6 +22,10 @@ import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
@@ -46,6 +50,17 @@ public class UdfpsEnrollProgressBarDrawable extends Drawable {
private static final float STROKE_WIDTH_DP = 12f;
private static final Interpolator DEACCEL = new DecelerateInterpolator();
+ private static final VibrationEffect VIBRATE_EFFECT_ERROR =
+ VibrationEffect.createWaveform(new long[] {0, 5, 55, 60}, -1);
+ private static final VibrationAttributes FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ACCESSIBILITY);
+
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+
+ private static final VibrationEffect SUCCESS_VIBRATION_EFFECT =
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+
private final float mStrokeWidthPx;
@ColorInt private final int mProgressColor;
@ColorInt private final int mHelpColor;
@@ -54,6 +69,9 @@ public class UdfpsEnrollProgressBarDrawable extends Drawable {
@NonNull private final Interpolator mCheckmarkInterpolator;
@NonNull private final Paint mBackgroundPaint;
@NonNull private final Paint mFillPaint;
+ @NonNull private final Vibrator mVibrator;
+ @NonNull private final boolean mIsAccessibilityEnabled;
+ @NonNull private final Context mContext;
private boolean mAfterFirstTouch;
@@ -76,11 +94,12 @@ public class UdfpsEnrollProgressBarDrawable extends Drawable {
@NonNull private final ValueAnimator.AnimatorUpdateListener mCheckmarkUpdateListener;
public UdfpsEnrollProgressBarDrawable(@NonNull Context context) {
+ mContext = context;
mStrokeWidthPx = Utils.dpToPixels(context, STROKE_WIDTH_DP);
mProgressColor = context.getColor(R.color.udfps_enroll_progress);
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
- final boolean isAccessbilityEnabled = am.isTouchExplorationEnabled();
- if (!isAccessbilityEnabled) {
+ mIsAccessibilityEnabled = am.isTouchExplorationEnabled();
+ if (!mIsAccessibilityEnabled) {
mHelpColor = context.getColor(R.color.udfps_enroll_progress_help);
mOnFirstBucketFailedColor = context.getColor(R.color.udfps_moving_target_fill_error);
} else {
@@ -106,6 +125,8 @@ public class UdfpsEnrollProgressBarDrawable extends Drawable {
mFillPaint.setStyle(Paint.Style.STROKE);
mFillPaint.setStrokeCap(Paint.Cap.ROUND);
+ mVibrator = mContext.getSystemService(Vibrator.class);
+
mProgressUpdateListener = animation -> {
mProgress = (float) animation.getAnimatedValue();
invalidateSelf();
@@ -141,14 +162,41 @@ public class UdfpsEnrollProgressBarDrawable extends Drawable {
}
private void updateState(int remainingSteps, int totalSteps, boolean showingHelp) {
- updateProgress(remainingSteps, totalSteps);
+ updateProgress(remainingSteps, totalSteps, showingHelp);
updateFillColor(showingHelp);
}
- private void updateProgress(int remainingSteps, int totalSteps) {
+ private void updateProgress(int remainingSteps, int totalSteps, boolean showingHelp) {
if (mRemainingSteps == remainingSteps && mTotalSteps == totalSteps) {
return;
}
+
+ if (mShowingHelp) {
+ if (mVibrator != null && mIsAccessibilityEnabled) {
+ mVibrator.vibrate(Process.myUid(), mContext.getOpPackageName(),
+ VIBRATE_EFFECT_ERROR, getClass().getSimpleName() + "::onEnrollmentHelp",
+ FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES);
+ }
+ } else {
+ // If the first touch is an error, remainingSteps will be -1 and the callback
+ // doesn't come from onEnrollmentHelp. If we are in the accessibility flow,
+ // we still would like to vibrate.
+ if (mVibrator != null) {
+ if (remainingSteps == -1 && mIsAccessibilityEnabled) {
+ mVibrator.vibrate(Process.myUid(), mContext.getOpPackageName(),
+ VIBRATE_EFFECT_ERROR,
+ getClass().getSimpleName() + "::onFirstTouchError",
+ FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES);
+ } else if (remainingSteps != -1 && !mIsAccessibilityEnabled) {
+ mVibrator.vibrate(Process.myUid(),
+ mContext.getOpPackageName(),
+ SUCCESS_VIBRATION_EFFECT,
+ getClass().getSimpleName() + "::OnEnrollmentProgress",
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
+ }
+ }
+ }
+
mRemainingSteps = remainingSteps;
mTotalSteps = totalSteps;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index a8c286241141..b21a886b037d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -23,7 +23,7 @@ import android.content.Context
import android.graphics.Matrix
import android.graphics.Rect
import android.os.Handler
-import android.provider.Settings
+import android.os.RemoteException
import android.util.Log
import android.view.RemoteAnimationTarget
import android.view.SyncRtSurfaceTransactionApplier
@@ -47,7 +47,6 @@ import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
-import kotlin.math.min
const val TAG = "KeyguardUnlock"
@@ -77,7 +76,7 @@ const val SURFACE_BEHIND_SCALE_PIVOT_Y = 0.66f
* The dismiss amount is the inverse of the notification panel expansion, which decreases as the
* lock screen is swiped away.
*/
-const val DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD = 0.25f
+const val DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD = 0.15f
/**
* Dismiss amount at which to complete the keyguard exit animation and hide the keyguard.
@@ -85,7 +84,7 @@ const val DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD = 0.25f
* The dismiss amount is the inverse of the notification panel expansion, which decreases as the
* lock screen is swiped away.
*/
-const val DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD = 0.4f
+const val DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD = 0.3f
/**
* How long the canned unlock animation takes. This is used if we are unlocking from biometric auth,
@@ -112,7 +111,7 @@ const val CANNED_UNLOCK_START_DELAY = 100L
* Duration for the alpha animation on the surface behind. This plays to fade in the surface during
* a swipe to unlock (and to fade it back out if the swipe is cancelled).
*/
-const val SURFACE_BEHIND_SWIPE_FADE_DURATION_MS = 150L
+const val SURFACE_BEHIND_SWIPE_FADE_DURATION_MS = 175L
/**
* Start delay for the surface behind animation, used so that the lockscreen can get out of the way
@@ -151,12 +150,21 @@ class KeyguardUnlockAnimationController @Inject constructor(
* [playingCannedAnimation] indicates whether we are playing a canned animation to show the
* app/launcher behind the keyguard, vs. this being a swipe to unlock where the dismiss
* amount drives the animation.
+ *
* [fromWakeAndUnlock] tells us whether we are unlocking directly from AOD - in this case,
* the lockscreen is dismissed instantly, so we shouldn't run any animations that rely on it
* being visible.
+ *
+ * [unlockAnimationStartDelay] and [unlockAnimationDuration] provide the timing parameters
+ * for the canned animation (if applicable) so interested parties can sync with it. If no
+ * canned animation is playing, these are both 0.
*/
@JvmDefault
- fun onUnlockAnimationStarted(playingCannedAnimation: Boolean, fromWakeAndUnlock: Boolean) {}
+ fun onUnlockAnimationStarted(
+ playingCannedAnimation: Boolean,
+ fromWakeAndUnlock: Boolean,
+ unlockAnimationStartDelay: Long,
+ unlockAnimationDuration: Long) {}
/**
* Called when the remote unlock animation ends, in all cases, canned or swipe-to-unlock.
@@ -165,19 +173,6 @@ class KeyguardUnlockAnimationController @Inject constructor(
*/
@JvmDefault
fun onUnlockAnimationFinished() {}
-
- /**
- * Called when we begin the smartspace shared element transition, either due to an unlock
- * action (biometric, etc.) or a swipe to unlock.
- *
- * This transition can begin BEFORE [onUnlockAnimationStarted] is called, if we are swiping
- * to unlock and the surface behind the keyguard has not yet been made visible. This is
- * because the lockscreen smartspace immediately begins moving towards the launcher
- * smartspace location when a swipe begins, even before we start the keyguard exit remote
- * animation and show the launcher itself.
- */
- @JvmDefault
- fun onSmartspaceSharedElementTransitionStarted() {}
}
/** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */
@@ -259,8 +254,9 @@ class KeyguardUnlockAnimationController @Inject constructor(
* animation plays.
*/
private var surfaceBehindAlpha = 1f
- private var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f)
- private var smartspaceAnimator = ValueAnimator.ofFloat(0f, 1f)
+
+ @VisibleForTesting
+ var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f)
/**
* Matrix applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the
@@ -281,59 +277,38 @@ class KeyguardUnlockAnimationController @Inject constructor(
private var roundedCornerRadius = 0f
/**
- * Whether we tried to start the SmartSpace shared element transition for this unlock swipe.
- * It's possible we were unable to do so (if the Launcher SmartSpace is not available), and we
- * need to keep track of that so that we don't start doing it halfway through the swipe if
- * Launcher becomes available suddenly.
- */
- private var attemptedSmartSpaceTransitionForThisSwipe = false
-
- /**
- * The original location of the lockscreen smartspace on the screen.
- */
- private val smartspaceOriginBounds = Rect()
-
- /**
- * The bounds to which the lockscreen smartspace is moving. This is set to the bounds of the
- * launcher's smartspace prior to the transition starting.
- */
- private val smartspaceDestBounds = Rect()
-
- /**
- * From 0f to 1f, the progress of the smartspace shared element animation. 0f means the
- * smartspace is at its normal position within the lock screen hierarchy, and 1f means it has
- * fully animated to the location of the Launcher's smartspace.
+ * Whether we decided in [prepareForInWindowLauncherAnimations] that we are able to and want to
+ * play the in-window launcher unlock animations rather than simply animating the Launcher
+ * window like any other app. This can be true while [willUnlockWithSmartspaceTransition] is
+ * false, if the smartspace is not available or was not ready in time.
*/
- private var smartspaceUnlockProgress = 0f
+ private var willUnlockWithInWindowLauncherAnimations: Boolean = false
/**
- * Whether we're currently unlocking, and we're talking to Launcher to perform in-window
- * animations rather than simply animating the Launcher window like any other app. This can be
- * true while [unlockingWithSmartspaceTransition] is false, if the smartspace is not available
- * or was not ready in time.
+ * Whether we decided in [prepareForInWindowLauncherAnimations] that we are able to and want to
+ * play the smartspace shared element animation. If true,
+ * [willUnlockWithInWindowLauncherAnimations] will also always be true since in-window
+ * animations are a prerequisite for the smartspace transition.
*/
- private var unlockingToLauncherWithInWindowAnimations: Boolean = false
-
- /**
- * Whether we are currently unlocking, and the smartspace shared element transition is in
- * progress. If true, we're also [unlockingToLauncherWithInWindowAnimations].
- */
- private var unlockingWithSmartspaceTransition: Boolean = false
+ private var willUnlockWithSmartspaceTransition: Boolean = false
private val handler = Handler()
init {
with(surfaceBehindAlphaAnimator) {
duration = SURFACE_BEHIND_SWIPE_FADE_DURATION_MS
- interpolator = Interpolators.TOUCH_RESPONSE
+ interpolator = Interpolators.LINEAR
addUpdateListener { valueAnimator: ValueAnimator ->
surfaceBehindAlpha = valueAnimator.animatedValue as Float
updateSurfaceBehindAppearAmount()
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
- // If the surface alpha is 0f, it's no longer visible so we can safely be done
- // with the animation even if other properties are still animating.
+ // If we animated the surface alpha to 0f, it means we cancelled a swipe to
+ // dismiss. In this case, we should ask the KeyguardViewMediator to end the
+ // remote animation to hide the surface behind the keyguard, but should *not*
+ // call onKeyguardExitRemoteAnimationFinished since that will hide the keyguard
+ // and unlock the device as well as hiding the surface.
if (surfaceBehindAlpha == 0f) {
keyguardViewMediator.get().finishSurfaceBehindRemoteAnimation(
false /* cancelled */)
@@ -360,21 +335,6 @@ class KeyguardUnlockAnimationController @Inject constructor(
})
}
- with(smartspaceAnimator) {
- duration = UNLOCK_ANIMATION_DURATION_MS
- interpolator = Interpolators.TOUCH_RESPONSE
- addUpdateListener {
- smartspaceUnlockProgress = it.animatedValue as Float
- }
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- launcherUnlockController?.setSmartspaceVisibility(View.VISIBLE)
- keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
- false /* cancelled */)
- }
- })
- }
-
// Listen for changes in the dismiss amount.
keyguardStateController.addCallback(this)
@@ -394,6 +354,74 @@ class KeyguardUnlockAnimationController @Inject constructor(
}
/**
+ * Whether we should be able to do the in-window launcher animations given the current state of
+ * the device.
+ */
+ fun canPerformInWindowLauncherAnimations(): Boolean {
+ return isNexusLauncherUnderneath() &&
+ launcherUnlockController != null &&
+ !keyguardStateController.isDismissingFromSwipe &&
+ // Temporarily disable for foldables since foldable launcher has two first pages,
+ // which breaks the in-window animation.
+ !isFoldable(context)
+ }
+
+ /**
+ * Called from [KeyguardStateController] to let us know that the keyguard going away state has
+ * changed.
+ */
+ override fun onKeyguardGoingAwayChanged() {
+ if (keyguardStateController.isKeyguardGoingAway) {
+ prepareForInWindowLauncherAnimations()
+ }
+ }
+
+ /**
+ * Prepare for in-window Launcher unlock animations, if we're able to do so.
+ *
+ * The in-window animations consist of the staggered ring icon unlock animation, and optionally
+ * the shared element smartspace transition.
+ */
+ fun prepareForInWindowLauncherAnimations() {
+ willUnlockWithInWindowLauncherAnimations = canPerformInWindowLauncherAnimations()
+
+ if (!willUnlockWithInWindowLauncherAnimations) {
+ return
+ }
+
+ // There are additional conditions under which we should not perform the smartspace
+ // transition specifically, so check those.
+ willUnlockWithSmartspaceTransition = shouldPerformSmartspaceTransition()
+
+ var lockscreenSmartspaceBounds = Rect()
+
+ // Grab the bounds of our lockscreen smartspace and send them to launcher so they can
+ // position their smartspace there initially, then animate it to its resting position.
+ if (willUnlockWithSmartspaceTransition) {
+ lockscreenSmartspaceBounds = Rect().apply {
+ lockscreenSmartspace!!.getBoundsOnScreen(this)
+ offset(lockscreenSmartspace!!.paddingLeft, lockscreenSmartspace!!.paddingTop)
+ }
+ }
+
+ // Currently selected lockscreen smartspace page, or -1 if it's not available.
+ val selectedPage =
+ (lockscreenSmartspace as BcSmartspaceDataPlugin.SmartspaceView?)?.selectedPage ?: -1
+
+ try {
+
+ // Let the launcher know to prepare for this animation.
+ launcherUnlockController?.prepareForUnlock(
+ willUnlockWithSmartspaceTransition, /* willAnimateSmartspace */
+ lockscreenSmartspaceBounds, /* lockscreenSmartspaceBounds */
+ selectedPage, /* selectedPage */
+ )
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Remote exception in prepareForInWindowUnlockAnimations.", e)
+ }
+ }
+
+ /**
* Called from [KeyguardViewMediator] to tell us that the RemoteAnimation on the surface behind
* the keyguard has started successfully. We can use these parameters to directly manipulate the
* surface for the unlock gesture/animation.
@@ -404,7 +432,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
*
* [requestedShowSurfaceBehindKeyguard] indicates whether the animation started because of a
* call to [KeyguardViewMediator.showSurfaceBehindKeyguard], as happens during a swipe gesture,
- * as opposed to being called because the device was unlocked and the keyguard is going away.
+ * as opposed to being called because the device was unlocked instantly by some other means
+ * (fingerprint, tap, etc.) and the keyguard is going away.
*/
fun notifyStartSurfaceBehindRemoteAnimation(
target: RemoteAnimationTarget,
@@ -422,19 +451,31 @@ class KeyguardUnlockAnimationController @Inject constructor(
surfaceBehindRemoteAnimationTarget = target
surfaceBehindRemoteAnimationStartTime = startTime
- // If we specifically requested that the surface behind be made visible, it means we are
- // swiping to unlock. In that case, the surface visibility is tied to the dismiss amount,
- // and we'll handle that in onKeyguardDismissAmountChanged(). If we didn't request that, the
- // keyguard is being dismissed for a different reason (biometric auth, etc.) and we should
- // play a canned animation to make the surface fully visible.
- if (!requestedShowSurfaceBehindKeyguard) {
+ // If we specifically requested that the surface behind be made visible (vs. it being made
+ // visible because we're unlocking), then we're in the middle of a swipe-to-unlock touch
+ // gesture and the surface behind the keyguard should be made visible.
+ if (requestedShowSurfaceBehindKeyguard) {
+ // Fade in the surface, as long as we're not now flinging. The touch gesture ending in
+ // a fling during the time it takes the keyguard exit animation to start is an edge
+ // case race condition, and we'll handle it by playing a canned animation on the
+ // now-visible surface to finish unlocking.
+ if (!keyguardStateController.isFlingingToDismissKeyguard) {
+ fadeInSurfaceBehind()
+ } else {
+ playCannedUnlockAnimation()
+ }
+ } else {
+ // The surface was made visible since we're unlocking not from a swipe (fingerprint,
+ // lock icon long-press, etc). Play the full unlock animation.
playCannedUnlockAnimation()
}
listeners.forEach {
it.onUnlockAnimationStarted(
playingCannedUnlockAnimation /* playingCannedAnimation */,
- biometricUnlockControllerLazy.get().isWakeAndUnlock /* isWakeAndUnlock */) }
+ biometricUnlockControllerLazy.get().isWakeAndUnlock /* isWakeAndUnlock */,
+ CANNED_UNLOCK_START_DELAY /* unlockStartDelay */,
+ LAUNCHER_ICONS_ANIMATION_DURATION_MS /* unlockAnimationDuration */) }
// Finish the keyguard remote animation if the dismiss amount has crossed the threshold.
// Check it here in case there is no more change to the dismiss amount after the last change
@@ -443,57 +484,32 @@ class KeyguardUnlockAnimationController @Inject constructor(
}
/**
- * Called by [KeyguardViewMediator] to let us know that the remote animation has finished, and
- * we should clean up all of our state.
- */
- fun notifyFinishedKeyguardExitAnimation(cancelled: Boolean) {
- // Cancel any pending actions.
- handler.removeCallbacksAndMessages(null)
-
- // Make sure we made the surface behind fully visible, just in case. It should already be
- // fully visible.
- setSurfaceBehindAppearAmount(1f)
- launcherUnlockController?.setUnlockAmount(1f)
- smartspaceDestBounds.setEmpty()
-
- // That target is no longer valid since the animation finished, null it out.
- surfaceBehindRemoteAnimationTarget = null
- surfaceBehindParams = null
-
- playingCannedUnlockAnimation = false
- unlockingToLauncherWithInWindowAnimations = false
- unlockingWithSmartspaceTransition = false
- resetSmartspaceTransition()
-
- listeners.forEach { it.onUnlockAnimationFinished() }
- }
-
- /**
* Play a canned unlock animation to unlock the device. This is used when we were *not* swiping
* to unlock using a touch gesture. If we were swiping to unlock, the animation will be driven
* by the dismiss amount via [onKeyguardDismissAmountChanged].
*/
- fun playCannedUnlockAnimation() {
+ private fun playCannedUnlockAnimation() {
playingCannedUnlockAnimation = true
- if (canPerformInWindowLauncherAnimations()) {
- // If possible, use the neat in-window animations to unlock to the launcher.
- unlockToLauncherWithInWindowAnimations()
- } else if (!biometricUnlockControllerLazy.get().isWakeAndUnlock) {
- // If the launcher isn't behind the keyguard, or the launcher unlock controller is not
- // available, animate in the entire window.
- surfaceBehindEntryAnimator.start()
- } else {
- setSurfaceBehindAppearAmount(1f)
- keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(false)
- }
- // If this is a wake and unlock, hide the lockscreen immediately. In the future, we should
- // animate it out nicely instead, but to the current state of wake and unlock, not hiding it
- // causes a lot of issues.
- // TODO(b/210016643): Not this, it looks not-ideal!
- if (biometricUnlockControllerLazy.get().isWakeAndUnlock) {
- keyguardViewController.hide(surfaceBehindRemoteAnimationStartTime, 350)
+ when {
+ // If we're set up for in-window launcher animations, ask Launcher to play its in-window
+ // canned animation.
+ willUnlockWithInWindowLauncherAnimations -> unlockToLauncherWithInWindowAnimations()
+
+ // If we're waking and unlocking to a non-Launcher app surface (or Launcher in-window
+ // animations are not available), show it immediately and end the remote animation. The
+ // circular light reveal will show the app surface, and it looks weird if it's moving
+ // around behind that.
+ biometricUnlockControllerLazy.get().isWakeAndUnlock -> {
+ setSurfaceBehindAppearAmount(1f)
+ keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ }
+
+ // Otherwise, we're doing a normal full-window unlock. Start this animator, which will
+ // scale/translate the window underneath the lockscreen.
+ else -> surfaceBehindEntryAnimator.start()
}
}
@@ -502,205 +518,31 @@ class KeyguardUnlockAnimationController @Inject constructor(
* transition if possible.
*/
private fun unlockToLauncherWithInWindowAnimations() {
- // See if we can do the smartspace transition, and if so, do it!
- if (prepareForSmartspaceTransition()) {
- animateSmartspaceToDestination()
- listeners.forEach { it.onSmartspaceSharedElementTransitionStarted() }
- }
-
- val startDelay = Settings.Secure.getLong(
- context.contentResolver, "unlock_start_delay", CANNED_UNLOCK_START_DELAY)
- val duration = Settings.Secure.getLong(
- context.contentResolver, "unlock_duration", LAUNCHER_ICONS_ANIMATION_DURATION_MS)
-
- unlockingToLauncherWithInWindowAnimations = true
- prepareLauncherWorkspaceForUnlockAnimation()
+ setSurfaceBehindAppearAmount(1f)
// Begin the animation, waiting for the shade to animate out.
launcherUnlockController?.playUnlockAnimation(
true /* unlocked */,
- duration /* duration */,
- startDelay /* startDelay */)
-
- handler.postDelayed({
- applyParamsToSurface(
- SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
- surfaceBehindRemoteAnimationTarget!!.leash)
- .withAlpha(1f)
- .build())
- }, startDelay)
-
- if (!unlockingWithSmartspaceTransition) {
- // If we are not unlocking with the smartspace transition, wait for the unlock animation
- // to end and then finish the remote animation. If we are using the smartspace
- // transition, it will finish the remote animation once it ends.
- handler.postDelayed({
- keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
- false /* cancelled */)
- }, UNLOCK_ANIMATION_DURATION_MS)
- }
- }
-
- /**
- * Asks Launcher to prepare the workspace to be unlocked. This sets up the animation and makes
- * the page invisible.
- */
- private fun prepareLauncherWorkspaceForUnlockAnimation() {
- // Tell the launcher to prepare for the animation by setting its views invisible and
- // syncing the selected smartspace pages.
- launcherUnlockController?.prepareForUnlock(
- unlockingWithSmartspaceTransition /* willAnimateSmartspace */,
- (lockscreenSmartspace as BcSmartspaceDataPlugin.SmartspaceView?)?.selectedPage ?: -1)
- }
-
- /**
- * Animates the lockscreen smartspace all the way to the launcher's smartspace location, then
- * makes the launcher smartspace visible and ends the remote animation.
- */
- private fun animateSmartspaceToDestination() {
- smartspaceAnimator.start()
- }
-
- /**
- * Reset the lockscreen smartspace's position, and reset all state involving the smartspace
- * transition.
- */
- public fun resetSmartspaceTransition() {
- unlockingWithSmartspaceTransition = false
- smartspaceUnlockProgress = 0f
-
- lockscreenSmartspace?.post {
- lockscreenSmartspace!!.translationX = 0f
- lockscreenSmartspace!!.translationY = 0f
- }
- }
-
- /**
- * Moves the lockscreen smartspace towards the launcher smartspace's position.
- */
- private fun setSmartspaceProgressToDestinationBounds(progress: Float) {
- if (smartspaceDestBounds.isEmpty) {
- return
- }
-
- val progressClamped = min(1f, progress)
-
- // Calculate the distance (relative to the origin) that we need to be for the current
- // progress value.
- val progressX =
- (smartspaceDestBounds.left - smartspaceOriginBounds.left) * progressClamped
- val progressY =
- (smartspaceDestBounds.top - smartspaceOriginBounds.top) * progressClamped
-
- val lockscreenSmartspaceCurrentBounds = Rect().also {
- lockscreenSmartspace!!.getBoundsOnScreen(it)
- }
-
- // Figure out how far that is from our present location on the screen. This approach
- // compensates for the fact that our parent container is also translating to animate out.
- val dx = smartspaceOriginBounds.left + progressX -
- lockscreenSmartspaceCurrentBounds.left
- val dy = smartspaceOriginBounds.top + progressY -
- lockscreenSmartspaceCurrentBounds.top
-
- with(lockscreenSmartspace!!) {
- translationX += dx
- translationY += dy
- }
- }
-
- /**
- * Update the lockscreen SmartSpace to be positioned according to the current dismiss amount. As
- * the dismiss amount increases, we will increase our SmartSpace's progress to the destination
- * bounds (the location of the Launcher SmartSpace).
- *
- * This is used by [KeyguardClockSwitchController] to keep the smartspace position updated as
- * the clock is swiped away.
- */
- fun updateLockscreenSmartSpacePosition() {
- setSmartspaceProgressToDestinationBounds(smartspaceUnlockProgress)
- }
-
- /**
- * Asks the keyguard view to hide, using the start time from the beginning of the remote
- * animation.
- */
- fun hideKeyguardViewAfterRemoteAnimation() {
- if (keyguardViewController.isShowing) {
- // Hide the keyguard, with no fade out since we animated it away during the unlock.
- keyguardViewController.hide(
- surfaceBehindRemoteAnimationStartTime,
- 0 /* fadeOutDuration */
- )
- } else {
- Log.e(TAG, "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " +
- "showing. Ignoring...")
- }
- }
+ LAUNCHER_ICONS_ANIMATION_DURATION_MS /* duration */,
+ CANNED_UNLOCK_START_DELAY /* startDelay */)
- private fun applyParamsToSurface(params: SyncRtSurfaceTransactionApplier.SurfaceParams) {
- surfaceTransactionApplier!!.scheduleApply(params)
- surfaceBehindParams = params
- }
+ // Now that the Launcher surface (with its smartspace positioned identically to ours) is
+ // visible, hide our smartspace.
+ lockscreenSmartspace!!.visibility = View.INVISIBLE
- /**
- * Scales in and translates up the surface behind the keyguard. This is used during unlock
- * animations and swipe gestures to animate the surface's entry (and exit, if the swipe is
- * cancelled).
- */
- fun setSurfaceBehindAppearAmount(amount: Float) {
- if (surfaceBehindRemoteAnimationTarget == null) {
- return
- }
-
- if (unlockingToLauncherWithInWindowAnimations) {
- // If we aren't using the canned unlock animation (which would be setting the unlock
- // amount in its update listener), do it here.
- if (!isPlayingCannedUnlockAnimation()) {
- launcherUnlockController?.setUnlockAmount(amount)
-
- if (surfaceBehindParams?.alpha?.let { it < 1f } != false) {
- applyParamsToSurface(
- SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
- surfaceBehindRemoteAnimationTarget!!.leash)
- .withAlpha(1f)
- .build())
- }
+ // As soon as the shade has animated out of the way, finish the keyguard exit animation. The
+ // in-window animations in the Launcher window will end on their own.
+ handler.postDelayed({
+ if (keyguardViewMediator.get().isShowingAndNotOccluded &&
+ !keyguardStateController.isKeyguardGoingAway) {
+ Log.e(TAG, "Finish keyguard exit animation delayed Runnable ran, but we are " +
+ "showing and not going away.")
+ return@postDelayed
}
- } else {
- // Otherwise, animate in the surface's scale/transltion.
- val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.height()
- val scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
- (1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
- MathUtils.clamp(amount, 0f, 1f))
-
- // Scale up from a point at the center-bottom of the surface.
- surfaceBehindMatrix.setScale(
- scaleFactor,
- scaleFactor,
- surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.width() / 2f,
- surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
- )
- // Translate up from the bottom.
- surfaceBehindMatrix.postTranslate(
- 0f,
- surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
- )
-
- // If we're snapping the keyguard back, immediately begin fading it out.
- val animationAlpha =
- if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount
- else surfaceBehindAlpha
-
- applyParamsToSurface(
- SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
- surfaceBehindRemoteAnimationTarget!!.leash)
- .withMatrix(surfaceBehindMatrix)
- .withCornerRadius(roundedCornerRadius)
- .withAlpha(animationAlpha)
- .build())
- }
+ keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ }, CANNED_UNLOCK_START_DELAY)
}
/**
@@ -738,7 +580,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
return
}
- if (keyguardViewController.isShowing) {
+ if (keyguardViewController.isShowing && !playingCannedUnlockAnimation) {
showOrHideSurfaceIfDismissAmountThresholdsReached()
// If the surface is visible or it's about to be, start updating its appearance to
@@ -750,11 +592,6 @@ class KeyguardUnlockAnimationController @Inject constructor(
updateSurfaceBehindAppearAmount()
}
}
-
- // The end of the SmartSpace transition can occur after the keyguard is hidden (when we tell
- // Launcher's SmartSpace to become visible again), so update it even if the keyguard view is
- // no longer showing.
- applyDismissAmountToSmartspaceTransition()
}
/**
@@ -775,18 +612,16 @@ class KeyguardUnlockAnimationController @Inject constructor(
return
}
+ if (!keyguardStateController.isShowing) {
+ return
+ }
+
val dismissAmount = keyguardStateController.dismissAmount
+
if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
- !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
- // We passed the threshold, and we're not yet showing the surface behind the
- // keyguard. Animate it in.
- if (!unlockingToLauncherWithInWindowAnimations &&
- canPerformInWindowLauncherAnimations()) {
- unlockingToLauncherWithInWindowAnimations = true
- prepareLauncherWorkspaceForUnlockAnimation()
- }
+ !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
+
keyguardViewMediator.get().showSurfaceBehindKeyguard()
- fadeInSurfaceBehind()
} else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
// We're no longer past the threshold but we are showing the surface. Animate it
@@ -828,60 +663,103 @@ class KeyguardUnlockAnimationController @Inject constructor(
}
/**
- * Updates flags related to the SmartSpace transition in response to a change in keyguard
- * dismiss amount, and also updates the SmartSpaceTransitionController, which will let Launcher
- * know if it needs to do something as a result.
+ * Scales in and translates up the surface behind the keyguard. This is used during unlock
+ * animations and swipe gestures to animate the surface's entry (and exit, if the swipe is
+ * cancelled).
*/
- private fun applyDismissAmountToSmartspaceTransition() {
- if (!featureFlags.isEnabled(Flags.SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED)) {
+ fun setSurfaceBehindAppearAmount(amount: Float) {
+ if (surfaceBehindRemoteAnimationTarget == null) {
return
}
- // If we are playing the canned animation, the smartspace is being animated directly between
- // its original location and the location of the launcher smartspace by smartspaceAnimator.
- // We can ignore the dismiss amount, which is caused by panel height changes as the panel is
- // flung away.
- if (playingCannedUnlockAnimation) {
- return
- }
+ // Otherwise, animate in the surface's scale/transltion.
+ val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.height()
+ val scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
+ (1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
+ MathUtils.clamp(amount, 0f, 1f))
+
+ // Scale up from a point at the center-bottom of the surface.
+ surfaceBehindMatrix.setScale(
+ scaleFactor,
+ scaleFactor,
+ surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.width() / 2f,
+ surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
+ )
+
+ // Translate up from the bottom.
+ surfaceBehindMatrix.postTranslate(
+ 0f,
+ surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
+ )
+
+ // If we're snapping the keyguard back, immediately begin fading it out.
+ val animationAlpha =
+ if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount
+ else surfaceBehindAlpha
+
+ applyParamsToSurface(
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
+ surfaceBehindRemoteAnimationTarget!!.leash)
+ .withMatrix(surfaceBehindMatrix)
+ .withCornerRadius(roundedCornerRadius)
+ .withAlpha(animationAlpha)
+ .build())
+ }
- val dismissAmount = keyguardStateController.dismissAmount
+ /**
+ * Called by [KeyguardViewMediator] to let us know that the remote animation has finished, and
+ * we should clean up all of our state.
+ *
+ * This is generally triggered by us, calling
+ * [KeyguardViewMediator.finishSurfaceBehindRemoteAnimation].
+ */
+ fun notifyFinishedKeyguardExitAnimation(cancelled: Boolean) {
+ // Cancel any pending actions.
+ handler.removeCallbacksAndMessages(null)
- // If we've begun a swipe, and haven't yet tried doing the SmartSpace transition, do that
- // now.
- if (!attemptedSmartSpaceTransitionForThisSwipe &&
- keyguardViewController.isShowing &&
- dismissAmount > 0f &&
- dismissAmount < 1f) {
- attemptedSmartSpaceTransitionForThisSwipe = true
+ // Make sure we made the surface behind fully visible, just in case. It should already be
+ // fully visible. If the launcher is doing its own animation, let it continue without
+ // forcing it to 1f.
+ setSurfaceBehindAppearAmount(1f)
+ launcherUnlockController?.setUnlockAmount(1f, false /* forceIfAnimating */)
- if (prepareForSmartspaceTransition()) {
- unlockingWithSmartspaceTransition = true
+ // That target is no longer valid since the animation finished, null it out.
+ surfaceBehindRemoteAnimationTarget = null
+ surfaceBehindParams = null
- // Ensure that the smartspace is invisible if we're doing the transition, and
- // visible if we aren't.
- launcherUnlockController?.setSmartspaceVisibility(
- if (unlockingWithSmartspaceTransition) View.INVISIBLE else View.VISIBLE)
+ playingCannedUnlockAnimation = false
+ willUnlockWithInWindowLauncherAnimations = false
+ willUnlockWithSmartspaceTransition = false
- if (unlockingWithSmartspaceTransition) {
- listeners.forEach { it.onSmartspaceSharedElementTransitionStarted() }
- }
- }
- } else if (attemptedSmartSpaceTransitionForThisSwipe &&
- (dismissAmount == 0f || dismissAmount == 1f)) {
- attemptedSmartSpaceTransitionForThisSwipe = false
- unlockingWithSmartspaceTransition = false
- launcherUnlockController?.setSmartspaceVisibility(View.VISIBLE)
- }
+ // The lockscreen surface is gone, so it is now safe to re-show the smartspace.
+ lockscreenSmartspace?.visibility = View.VISIBLE
+
+ listeners.forEach { it.onUnlockAnimationFinished() }
+ }
+
+ /**
+ * Asks the keyguard view to hide, using the start time from the beginning of the remote
+ * animation.
+ */
+ fun hideKeyguardViewAfterRemoteAnimation() {
+ if (keyguardViewController.isShowing) {
+ // Hide the keyguard, with no fade out since we animated it away during the unlock.
- if (unlockingWithSmartspaceTransition) {
- val swipedFraction: Float = keyguardStateController.dismissAmount
- val progress = swipedFraction / DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD
- smartspaceUnlockProgress = progress
- setSmartspaceProgressToDestinationBounds(smartspaceUnlockProgress)
+ keyguardViewController.hide(
+ surfaceBehindRemoteAnimationStartTime,
+ 0 /* fadeOutDuration */
+ )
+ } else {
+ Log.e(TAG, "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " +
+ "showing. Ignoring...")
}
}
+ private fun applyParamsToSurface(params: SyncRtSurfaceTransactionApplier.SurfaceParams) {
+ surfaceTransactionApplier!!.scheduleApply(params)
+ surfaceBehindParams = params
+ }
+
private fun fadeInSurfaceBehind() {
surfaceBehindAlphaAnimator.cancel()
surfaceBehindAlphaAnimator.start()
@@ -892,14 +770,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
surfaceBehindAlphaAnimator.reverse()
}
- /**
- * Prepare for the smartspace shared element transition, if possible, by figuring out where we
- * are animating from/to.
- *
- * Return true if we'll be able to do the smartspace transition, or false if conditions are not
- * right to do it right now.
- */
- private fun prepareForSmartspaceTransition(): Boolean {
+
+ private fun shouldPerformSmartspaceTransition(): Boolean {
// Feature is disabled, so we don't want to.
if (!featureFlags.isEnabled(Flags.SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED)) {
return false
@@ -940,45 +812,22 @@ class KeyguardUnlockAnimationController @Inject constructor(
return false
}
- unlockingWithSmartspaceTransition = true
- smartspaceDestBounds.setEmpty()
-
- // Assuming we were able to retrieve the launcher's state, start the lockscreen
- // smartspace at 0, 0, and save its starting bounds.
- with(lockscreenSmartspace!!) {
- translationX = 0f
- translationY = 0f
- getBoundsOnScreen(smartspaceOriginBounds)
- }
-
- // Set the destination bounds to the launcher smartspace's bounds, offset by any
- // padding on our smartspace.
- with(smartspaceDestBounds) {
- set(launcherSmartspaceState!!.boundsOnScreen)
- offset(-lockscreenSmartspace!!.paddingLeft, -lockscreenSmartspace!!.paddingTop)
+ // We started to swipe to dismiss, but now we're doing a fling animation to complete the
+ // dismiss. In this case, the smartspace swiped away with the rest of the keyguard, so don't
+ // do the shared element transition.
+ if (keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture) {
+ return false
}
return true
}
/**
- * Whether we should be able to do the in-window launcher animations given the current state of
- * the device.
- */
- fun canPerformInWindowLauncherAnimations(): Boolean {
- return isNexusLauncherUnderneath() &&
- launcherUnlockController != null &&
- // Temporarily disable for foldables since foldable launcher has two first pages,
- // which breaks the in-window animation.
- !isFoldable(context)
- }
-
- /**
* Whether we are currently in the process of unlocking the keyguard, and we are performing the
* shared element SmartSpace transition.
*/
fun isUnlockingWithSmartSpaceTransition(): Boolean {
- return unlockingWithSmartspaceTransition
+ return willUnlockWithSmartspaceTransition
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 29e940f24df6..10ea1e06c6d7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -17,6 +17,8 @@
package com.android.systemui.keyguard;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
+import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN;
@@ -119,7 +121,6 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -2307,8 +2308,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
int flags = 0;
if (mKeyguardViewControllerLazy.get().shouldDisableWindowAnimationsForUnlock()
|| mWakeAndUnlocking && !mWallpaperSupportsAmbientMode) {
- flags |= WindowManagerPolicyConstants
- .KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
+ flags |= KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
}
if (mKeyguardViewControllerLazy.get().isGoingToNotificationShade()
|| mWakeAndUnlocking && mWallpaperSupportsAmbientMode) {
@@ -2324,6 +2324,15 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
}
+ // If we are unlocking to the launcher, clear the snapshot so that any changes as part
+ // of the in-window animations are reflected. This is needed even if we're not actually
+ // playing in-window animations for this particular unlock since a previous unlock might
+ // have changed the Launcher state.
+ if (mWakeAndUnlocking
+ && KeyguardUnlockAnimationController.Companion.isNexusLauncherUnderneath()) {
+ flags |= KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
+ }
+
mUpdateMonitor.setKeyguardGoingAway(true);
mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(true);
@@ -2628,9 +2637,18 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mSurfaceBehindRemoteAnimationRequested = true;
try {
- ActivityTaskManager.getService().keyguardGoingAway(
- WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS
- | WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER);
+ int flags = KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS
+ | KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+
+ // If we are unlocking to the launcher, clear the snapshot so that any changes as part
+ // of the in-window animations are reflected. This is needed even if we're not actually
+ // playing in-window animations for this particular unlock since a previous unlock might
+ // have changed the Launcher state.
+ if (KeyguardUnlockAnimationController.Companion.isNexusLauncherUnderneath()) {
+ flags |= KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
+ }
+
+ ActivityTaskManager.getService().keyguardGoingAway(flags);
mKeyguardStateController.notifyKeyguardGoingAway(true);
} catch (RemoteException e) {
mSurfaceBehindRemoteAnimationRequested = false;
@@ -2660,7 +2678,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/** If it's running, finishes the RemoteAnimation on the surface behind the keyguard. */
- public void finishSurfaceBehindRemoteAnimation(boolean cancelled) {
+ void finishSurfaceBehindRemoteAnimation(boolean cancelled) {
if (!mSurfaceBehindRemoteAnimationRunning) {
return;
}
@@ -2796,12 +2814,6 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
mWakeAndUnlocking = true;
- // We're going to animate in the Launcher, so ask WM to clear the task snapshot so we don't
- // initially display an old snapshot with all of the icons visible. We're System UI, so
- // we're allowed to pass in null to ask WM to find the home activity for us to prevent
- // needing to IPC to Launcher.
- ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(null /* homeActivity */);
-
keyguardDone();
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 24671696f3bd..f72f1bb47468 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -218,6 +218,18 @@ public class LogModule {
return factory.create("MediaTimeout", 100);
}
+ /**
+ * Provides a buffer for our connections and disconnections to MediaBrowserService.
+ *
+ * See {@link com.android.systemui.media.ResumeMediaBrowser}.
+ */
+ @Provides
+ @SysUISingleton
+ @MediaBrowserLog
+ public static LogBuffer provideMediaBrowserBuffer(LogBufferFactory factory) {
+ return factory.create("MediaBrowser", 100);
+ }
+
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
@Provides
@SysUISingleton
diff --git a/services/tests/servicestests/aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
index d96450478f90..1d7ba94af4ed 100644
--- a/services/tests/servicestests/aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 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.
@@ -14,8 +14,22 @@
* limitations under the License.
*/
-package com.android.servicestests.aidl;
+package com.android.systemui.log.dagger;
-interface ICmdReceiverService {
- void finishActivity();
-} \ No newline at end of file
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/**
+ * A {@link LogBuffer} for {@link com.android.systemui.media.ResumeMediaBrowser}
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface MediaBrowserLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
index 5a214d1cd5e0..a6b4f1d2a9d0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
@@ -21,24 +21,41 @@ import android.animation.ValueAnimator.AnimatorUpdateListener
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.ColorStateList
+import android.graphics.drawable.GradientDrawable
import com.android.internal.R
import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
import com.android.systemui.monet.ColorScheme
+import com.android.systemui.util.getColorWithAlpha
/**
- * ColorTransition is responsible for managing the animation between two specific colors.
+ * A [ColorTransition] is an object that updates the colors of views each time [updateColorScheme]
+ * is triggered.
+ */
+interface ColorTransition {
+ fun updateColorScheme(scheme: ColorScheme?)
+}
+
+/** A generic implementation of [ColorTransition] so that we can define a factory method. */
+open class GenericColorTransition(
+ private val applyTheme: (ColorScheme?) -> Unit
+) : ColorTransition {
+ override fun updateColorScheme(scheme: ColorScheme?) = applyTheme(scheme)
+}
+
+/**
+ * A [ColorTransition] that animates between two specific colors.
* It uses a ValueAnimator to execute the animation and interpolate between the source color and
* the target color.
*
* Selection of the target color from the scheme, and application of the interpolated color
* are delegated to callbacks.
*/
-open class ColorTransition(
+open class AnimatingColorTransition(
private val defaultColor: Int,
private val extractColor: (ColorScheme) -> Int,
private val applyColor: (Int) -> Unit
-) : AnimatorUpdateListener {
+) : AnimatorUpdateListener, ColorTransition {
private val argbEvaluator = ArgbEvaluator()
private val valueAnimator = buildAnimator()
@@ -53,7 +70,7 @@ open class ColorTransition(
applyColor(currentColor)
}
- fun updateColorScheme(scheme: ColorScheme?) {
+ override fun updateColorScheme(scheme: ColorScheme?) {
val newTargetColor = if (scheme == null) defaultColor else extractColor(scheme)
if (newTargetColor != targetColor) {
sourceColor = currentColor
@@ -76,7 +93,9 @@ open class ColorTransition(
}
}
-typealias ColorTransitionFactory = (Int, (ColorScheme) -> Int, (Int) -> Unit) -> ColorTransition
+typealias AnimatingColorTransitionFactory =
+ (Int, (ColorScheme) -> Int, (Int) -> Unit) -> AnimatingColorTransition
+typealias GenericColorTransitionFactory = ((ColorScheme?) -> Unit) -> GenericColorTransition
/**
* ColorSchemeTransition constructs a ColorTransition for each color in the scheme
@@ -86,27 +105,26 @@ typealias ColorTransitionFactory = (Int, (ColorScheme) -> Int, (Int) -> Unit) ->
class ColorSchemeTransition internal constructor(
private val context: Context,
mediaViewHolder: MediaViewHolder,
- colorTransitionFactory: ColorTransitionFactory
+ animatingColorTransitionFactory: AnimatingColorTransitionFactory,
+ genericColorTransitionFactory: GenericColorTransitionFactory
) {
constructor(context: Context, mediaViewHolder: MediaViewHolder) :
- this(context, mediaViewHolder, ::ColorTransition)
+ this(context, mediaViewHolder, ::AnimatingColorTransition, ::GenericColorTransition)
val bgColor = context.getColor(com.android.systemui.R.color.material_dynamic_secondary95)
- val surfaceColor = colorTransitionFactory(
+ val surfaceColor = animatingColorTransitionFactory(
bgColor,
::surfaceFromScheme
) { surfaceColor ->
val colorList = ColorStateList.valueOf(surfaceColor)
mediaViewHolder.player.backgroundTintList = colorList
- mediaViewHolder.albumView.foregroundTintList = colorList
- mediaViewHolder.albumView.backgroundTintList = colorList
mediaViewHolder.seamlessIcon.imageTintList = colorList
mediaViewHolder.seamlessText.setTextColor(surfaceColor)
mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor)
}
- val accentPrimary = colorTransitionFactory(
+ val accentPrimary = animatingColorTransitionFactory(
loadDefaultColor(R.attr.textColorPrimary),
::accentPrimaryFromScheme
) { accentPrimary ->
@@ -116,7 +134,7 @@ class ColorSchemeTransition internal constructor(
mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary)
}
- val textPrimary = colorTransitionFactory(
+ val textPrimary = animatingColorTransitionFactory(
loadDefaultColor(R.attr.textColorPrimary),
::textPrimaryFromScheme
) { textPrimary ->
@@ -132,28 +150,65 @@ class ColorSchemeTransition internal constructor(
mediaViewHolder.gutsViewHolder.setTextPrimaryColor(textPrimary)
}
- val textPrimaryInverse = colorTransitionFactory(
+ val textPrimaryInverse = animatingColorTransitionFactory(
loadDefaultColor(R.attr.textColorPrimaryInverse),
::textPrimaryInverseFromScheme
) { textPrimaryInverse ->
mediaViewHolder.actionPlayPause.imageTintList = ColorStateList.valueOf(textPrimaryInverse)
}
- val textSecondary = colorTransitionFactory(
+ val textSecondary = animatingColorTransitionFactory(
loadDefaultColor(R.attr.textColorSecondary),
::textSecondaryFromScheme
) { textSecondary -> mediaViewHolder.artistText.setTextColor(textSecondary) }
- val textTertiary = colorTransitionFactory(
+ val textTertiary = animatingColorTransitionFactory(
loadDefaultColor(R.attr.textColorTertiary),
::textTertiaryFromScheme
) { textTertiary ->
mediaViewHolder.seekBar.progressBackgroundTintList = ColorStateList.valueOf(textTertiary)
}
+ // Note: This background gradient currently doesn't animate between colors.
+ val backgroundGradient = genericColorTransitionFactory { scheme ->
+ val defaultTintColor = ColorStateList.valueOf(bgColor)
+ if (scheme == null) {
+ mediaViewHolder.albumView.foregroundTintList = defaultTintColor
+ mediaViewHolder.albumView.backgroundTintList = defaultTintColor
+ return@genericColorTransitionFactory
+ }
+
+ // If there's no album art, just hide the gradient so we show the solid background.
+ val showGradient = mediaViewHolder.albumView.drawable != null
+ val startColor = getColorWithAlpha(
+ backgroundStartFromScheme(scheme),
+ alpha = if (showGradient) .25f else 0f
+ )
+ val endColor = getColorWithAlpha(
+ backgroundEndFromScheme(scheme),
+ alpha = if (showGradient) .90f else 0f
+ )
+ val gradientColors = intArrayOf(startColor, endColor)
+
+ val foregroundGradient = mediaViewHolder.albumView.foreground?.mutate()
+ if (foregroundGradient is GradientDrawable) {
+ foregroundGradient.colors = gradientColors
+ }
+ val backgroundGradient = mediaViewHolder.albumView.background?.mutate()
+ if (backgroundGradient is GradientDrawable) {
+ backgroundGradient.colors = gradientColors
+ }
+ }
+
val colorTransitions = arrayOf(
- surfaceColor, accentPrimary, textPrimary,
- textPrimaryInverse, textSecondary, textTertiary)
+ surfaceColor,
+ accentPrimary,
+ textPrimary,
+ textPrimaryInverse,
+ textSecondary,
+ textTertiary,
+ backgroundGradient
+ )
private fun loadDefaultColor(id: Int): Int {
return Utils.getColorAttr(context, id).defaultColor
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 7b72ab732543..38128bfb70e7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -127,7 +127,7 @@ class MediaCarouselController @Inject constructor(
private val visualStabilityCallback: OnReorderingAllowedListener
private var needsReordering: Boolean = false
private var keysNeedRemoval = mutableSetOf<String>()
- protected var shouldScrollToActivePlayer: Boolean = false
+ var shouldScrollToActivePlayer: Boolean = false
private var isRtl: Boolean = false
set(value) {
if (value != field) {
@@ -413,9 +413,7 @@ class MediaCarouselController @Inject constructor(
.indexOfFirst { key -> it == key }
mediaCarouselScrollHandler
.scrollToPlayer(previousVisibleIndex, activeMediaIndex)
- } ?: {
- mediaCarouselScrollHandler.scrollToPlayer(destIndex = activeMediaIndex)
- }
+ } ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = activeMediaIndex)
}
}
}
@@ -432,7 +430,7 @@ class MediaCarouselController @Inject constructor(
val curVisibleMediaKey = MediaPlayerData.playerKeys()
.elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
if (existingPlayer == null) {
- var newPlayer = mediaControlPanelFactory.get()
+ val newPlayer = mediaControlPanelFactory.get()
newPlayer.attachPlayer(MediaViewHolder.create(
LayoutInflater.from(context), mediaContent))
newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
@@ -480,7 +478,7 @@ class MediaCarouselController @Inject constructor(
MediaPlayerData.removeMediaPlayer(existingSmartspaceMediaKey)
}
- var newRecs = mediaControlPanelFactory.get()
+ val newRecs = mediaControlPanelFactory.get()
newRecs.attachRecommendation(
RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent))
newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
@@ -1048,7 +1046,5 @@ internal object MediaPlayerData {
return false
}
- fun isSsReactivated(key: String): Boolean = mediaData.get(key)?.let {
- it.isSsReactivated
- } ?: false
+ fun isSsReactivated(key: String): Boolean = mediaData.get(key)?.isSsReactivated ?: false
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt b/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt
index 97c6014c91bd..5e767b0458b9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt
@@ -35,3 +35,9 @@ internal fun textSecondaryFromScheme(scheme: ColorScheme) = scheme.neutral2[3] /
/** Returns the tertiary text color for media controls based on the scheme. */
internal fun textTertiaryFromScheme(scheme: ColorScheme) = scheme.neutral2[5] // N2-400
+
+/** Returns the color for the start of the background gradient based on the scheme. */
+internal fun backgroundStartFromScheme(scheme: ColorScheme) = scheme.accent2[8] // A2-700
+
+/** Returns the color for the end of the background gradient based on the scheme. */
+internal fun backgroundEndFromScheme(scheme: ColorScheme) = scheme.accent1[8] // A1-700
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index d9ee8f3f06b4..5a32e91b1d18 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -18,6 +18,8 @@ package com.android.systemui.media;
import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
+import static com.android.systemui.media.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
+
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorSet;
@@ -102,7 +104,6 @@ public class MediaControlPanel {
+ ".android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity";
private static final String EXTRAS_SMARTSPACE_INTENT =
"com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT";
- private static final int MEDIA_RECOMMENDATION_MAX_NUM = 3;
private static final String KEY_SMARTSPACE_ARTIST_NAME = "artist_name";
private static final String KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND";
private static final String KEY_SMARTSPACE_APP_NAME = "KEY_SMARTSPACE_APP_NAME";
@@ -949,16 +950,14 @@ public class MediaControlPanel {
return;
}
+ if (!data.isValid()) {
+ Log.e(TAG, "Received an invalid recommendation list; returning");
+ return;
+ }
+
mSmartspaceId = SmallHash.hash(data.getTargetId());
mPackageName = data.getPackageName();
mInstanceId = data.getInstanceId();
- TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
-
- List<SmartspaceAction> mediaRecommendationList = data.getRecommendations();
- if (mediaRecommendationList == null || mediaRecommendationList.isEmpty()) {
- Log.w(TAG, "Empty media recommendations");
- return;
- }
// Set up recommendation card's header.
ApplicationInfo applicationInfo;
@@ -994,6 +993,7 @@ public class MediaControlPanel {
}
// Set up media rec card's tap action if applicable.
+ TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
setSmartspaceRecItemOnClickListener(recommendationCard, data.getCardAction(),
/* interactedSubcardRank */ -1);
// Set up media rec card's accessibility label.
@@ -1002,29 +1002,20 @@ public class MediaControlPanel {
List<ImageView> mediaCoverItems = mRecommendationViewHolder.getMediaCoverItems();
List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers();
- int mediaRecommendationNum = Math.min(mediaRecommendationList.size(),
- MEDIA_RECOMMENDATION_MAX_NUM);
+ List<SmartspaceAction> recommendations = data.getValidRecommendations();
boolean hasTitle = false;
boolean hasSubtitle = false;
- int uiComponentIndex = 0;
- for (int itemIndex = 0;
- itemIndex < mediaRecommendationNum && uiComponentIndex < mediaRecommendationNum;
- itemIndex++) {
- SmartspaceAction recommendation = mediaRecommendationList.get(itemIndex);
- if (recommendation.getIcon() == null) {
- Log.w(TAG, "No media cover is provided. Skipping this item...");
- continue;
- }
+ for (int itemIndex = 0; itemIndex < NUM_REQUIRED_RECOMMENDATIONS; itemIndex++) {
+ SmartspaceAction recommendation = recommendations.get(itemIndex);
// Set up media item cover.
- ImageView mediaCoverImageView = mediaCoverItems.get(uiComponentIndex);
+ ImageView mediaCoverImageView = mediaCoverItems.get(itemIndex);
mediaCoverImageView.setImageIcon(recommendation.getIcon());
// Set up the media item's click listener if applicable.
- ViewGroup mediaCoverContainer = mediaCoverContainers.get(uiComponentIndex);
- setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation,
- uiComponentIndex);
+ ViewGroup mediaCoverContainer = mediaCoverContainers.get(itemIndex);
+ setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation, itemIndex);
// Bubble up the long-click event to the card.
mediaCoverContainer.setOnLongClickListener(v -> {
View parent = (View) v.getParent();
@@ -1053,8 +1044,7 @@ public class MediaControlPanel {
// Set up title
CharSequence title = recommendation.getTitle();
hasTitle |= !TextUtils.isEmpty(title);
- TextView titleView =
- mRecommendationViewHolder.getMediaTitles().get(uiComponentIndex);
+ TextView titleView = mRecommendationViewHolder.getMediaTitles().get(itemIndex);
titleView.setText(title);
// Set up subtitle
@@ -1062,13 +1052,10 @@ public class MediaControlPanel {
boolean shouldShowSubtitleText = !TextUtils.isEmpty(title);
CharSequence subtitle = shouldShowSubtitleText ? recommendation.getSubtitle() : "";
hasSubtitle |= !TextUtils.isEmpty(subtitle);
- TextView subtitleView =
- mRecommendationViewHolder.getMediaSubtitles().get(uiComponentIndex);
+ TextView subtitleView = mRecommendationViewHolder.getMediaSubtitles().get(itemIndex);
subtitleView.setText(subtitle);
-
- uiComponentIndex++;
}
- mSmartspaceMediaItemsCount = uiComponentIndex;
+ mSmartspaceMediaItemsCount = NUM_REQUIRED_RECOMMENDATIONS;
// If there's no subtitles and/or titles for any of the albums, hide those views.
ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
@@ -1301,7 +1288,7 @@ public class MediaControlPanel {
}
logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
interactedSubcardRank,
- getSmartspaceSubCardCardinality());
+ mSmartspaceMediaItemsCount);
if (shouldSmartspaceRecItemOpenInForeground(action)) {
// Request to unlock the device if the activity needs to be opened in foreground.
@@ -1386,13 +1373,4 @@ public class MediaControlPanel {
interactedSubcardRank,
interactedSubcardCardinality);
}
-
- private int getSmartspaceSubCardCardinality() {
- if (!mMediaCarouselController.getMediaCarouselScrollHandler().getQsExpanded()
- && mSmartspaceMediaItemsCount > 3) {
- return 3;
- }
-
- return mSmartspaceMediaItemsCount;
- }
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 647d3efa5916..81efdf591b41 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -166,7 +166,7 @@ class MediaDataFilter @Inject constructor(
shouldPrioritizeMutable = true
}
- if (!data.isValid) {
+ if (!data.isValid()) {
Log.d(TAG, "Invalid recommendation data. Skip showing the rec card")
return
}
@@ -203,7 +203,6 @@ class MediaDataFilter @Inject constructor(
if (smartspaceMediaData.isActive) {
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = smartspaceMediaData.targetId,
- isValid = smartspaceMediaData.isValid,
instanceId = smartspaceMediaData.instanceId)
}
listeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) }
@@ -260,7 +259,6 @@ class MediaDataFilter @Inject constructor(
}
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = smartspaceMediaData.targetId,
- isValid = smartspaceMediaData.isValid,
instanceId = smartspaceMediaData.instanceId)
mediaDataManager.dismissSmartspaceRecommendation(smartspaceMediaData.targetId,
delay = 0L)
@@ -272,13 +270,13 @@ class MediaDataFilter @Inject constructor(
*/
fun hasActiveMediaOrRecommendation() =
userEntries.any { it.value.active } ||
- (smartspaceMediaData.isActive && smartspaceMediaData.isValid)
+ (smartspaceMediaData.isActive && smartspaceMediaData.isValid())
/**
* Are there any media entries we should display?
*/
fun hasAnyMediaOrRecommendation() = userEntries.isNotEmpty() ||
- (smartspaceMediaData.isActive && smartspaceMediaData.isValid)
+ (smartspaceMediaData.isActive && smartspaceMediaData.isValid())
/**
* Are there any media notifications active (excluding the recommendation)?
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index b2751cec5d9e..0a4455658b6b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -105,7 +105,6 @@ private val LOADING = MediaData(
internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData(
targetId = "INVALID",
isActive = false,
- isValid = false,
packageName = "INVALID",
cardAction = null,
recommendations = emptyList(),
@@ -261,6 +260,8 @@ class MediaDataManager(
// Set up links back into the pipeline for listeners that need to send events upstream.
mediaTimeoutListener.timeoutCallback = { key: String, timedOut: Boolean ->
setTimedOut(key, timedOut) }
+ mediaTimeoutListener.stateCallback = { key: String, state: PlaybackState ->
+ updateState(key, state) }
mediaResumeListener.setManager(this)
mediaDataFilter.mediaDataManager = this
@@ -502,6 +503,21 @@ class MediaDataManager(
}
}
+ /**
+ * Called when the player's [PlaybackState] has been updated with new actions and/or state
+ */
+ private fun updateState(key: String, state: PlaybackState) {
+ mediaEntries.get(key)?.let {
+ val actions = createActionsFromState(it.packageName,
+ mediaControllerFactory.create(it.token), UserHandle(it.userId))
+ val data = it.copy(
+ semanticActions = actions,
+ isPlaying = isPlayingState(state.state))
+ if (DEBUG) Log.d(TAG, "State updated outside of notification")
+ onMediaDataLoaded(key, key, data)
+ }
+ }
+
private fun removeEntry(key: String) {
mediaEntries.remove(key)?.let {
logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
@@ -534,7 +550,7 @@ class MediaDataManager(
* connection session.
*/
fun dismissSmartspaceRecommendation(key: String, delay: Long) {
- if (smartspaceMediaData.targetId != key || !smartspaceMediaData.isValid) {
+ if (smartspaceMediaData.targetId != key || !smartspaceMediaData.isValid()) {
// If this doesn't match, or we've already invalidated the data, no action needed
return
}
@@ -673,11 +689,8 @@ class MediaDataManager(
// Otherwise, use the notification actions
var actionIcons: List<MediaAction> = emptyList()
var actionsToShowCollapsed: List<Int> = emptyList()
- var semanticActions: MediaButton? = null
- if (mediaFlags.areMediaSessionActionsEnabled(sbn.packageName, sbn.user) &&
- mediaController.playbackState != null) {
- semanticActions = createActionsFromState(sbn.packageName, mediaController)
- } else {
+ val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user)
+ if (semanticActions == null) {
val actions = createActionsFromNotification(sbn)
actionIcons = actions.first
actionsToShowCollapsed = actions.second
@@ -789,13 +802,17 @@ class MediaDataManager(
* @return a Pair consisting of a list of media actions, and a list of ints representing which
* of those actions should be shown in the compact player
*/
- private fun createActionsFromState(packageName: String, controller: MediaController):
- MediaButton? {
+ private fun createActionsFromState(
+ packageName: String,
+ controller: MediaController,
+ user: UserHandle
+ ): MediaButton? {
val state = controller.playbackState
- if (state == null) {
- return MediaButton()
+ if (state == null || !mediaFlags.areMediaSessionActionsEnabled(packageName, user)) {
+ return null
}
- // First, check for} standard actions
+
+ // First, check for standard actions
val playOrPause = if (isConnectingState(state.state)) {
// Spinner needs to be animating to render anything. Start it here.
val drawable = context.getDrawable(
@@ -1222,7 +1239,6 @@ class MediaDataManager(
return SmartspaceMediaData(
targetId = target.smartspaceTargetId,
isActive = isActive,
- isValid = true,
packageName = it,
cardAction = target.baseAction,
recommendations = target.iconGrid,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 7f25642be5ee..cc06b6c67879 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -64,6 +64,11 @@ class MediaResumeListener @Inject constructor(
private lateinit var mediaDataManager: MediaDataManager
private var mediaBrowser: ResumeMediaBrowser? = null
+ set(value) {
+ // Always disconnect the old browser -- see b/225403871.
+ field?.disconnect()
+ field = value
+ }
private var currentUserId: Int = context.userId
@VisibleForTesting
@@ -189,7 +194,6 @@ class MediaResumeListener @Inject constructor(
if (useMediaResumption) {
// If this had been started from a resume state, disconnect now that it's live
if (!key.equals(oldKey)) {
- mediaBrowser?.disconnect()
mediaBrowser = null
}
// If we don't have a resume action, check if we haven't already
@@ -223,7 +227,6 @@ class MediaResumeListener @Inject constructor(
Log.d(TAG, "Testing if we can connect to $componentName")
// Set null action to prevent additional attempts to connect
mediaDataManager.setResumeAction(key, null)
- mediaBrowser?.disconnect()
mediaBrowser = mediaBrowserFactory.create(
object : ResumeMediaBrowser.Callback() {
override fun onConnected() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 8c6710a6fd68..fc8d38d59d59 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -55,6 +55,13 @@ class MediaTimeoutListener @Inject constructor(
*/
lateinit var timeoutCallback: (String, Boolean) -> Unit
+ /**
+ * Callback representing that a media object [PlaybackState] has changed.
+ * @param key Media control unique identifier
+ * @param state The new [PlaybackState]
+ */
+ lateinit var stateCallback: (String, PlaybackState) -> Unit
+
override fun onMediaDataLoaded(
key: String,
oldKey: String?,
@@ -85,17 +92,17 @@ class MediaTimeoutListener @Inject constructor(
}
reusedListener?.let {
- val wasPlaying = it.playing ?: false
+ val wasPlaying = it.isPlaying()
logger.logUpdateListener(key, wasPlaying)
it.mediaData = data
it.key = key
mediaListeners[key] = it
- if (wasPlaying != it.playing) {
+ if (wasPlaying != it.isPlaying()) {
// If a player becomes active because of a migration, we'll need to broadcast
// its state. Doing it now would lead to reentrant callbacks, so let's wait
// until we're done.
mainExecutor.execute {
- if (mediaListeners[key]?.playing == true) {
+ if (mediaListeners[key]?.isPlaying() == true) {
logger.logDelayedUpdate(key)
timeoutCallback.invoke(key, false /* timedOut */)
}
@@ -121,7 +128,7 @@ class MediaTimeoutListener @Inject constructor(
) : MediaController.Callback() {
var timedOut = false
- var playing: Boolean? = null
+ var lastState: PlaybackState? = null
var resumption: Boolean? = null
var destroyed = false
@@ -145,6 +152,9 @@ class MediaTimeoutListener @Inject constructor(
private var mediaController: MediaController? = null
private var cancellation: Runnable? = null
+ fun Int.isPlaying() = isPlayingState(this)
+ fun isPlaying() = lastState?.state?.isPlaying() ?: false
+
init {
mediaData = data
}
@@ -175,16 +185,26 @@ class MediaTimeoutListener @Inject constructor(
private fun processState(state: PlaybackState?, dispatchEvents: Boolean) {
logger.logPlaybackState(key, state)
- val isPlaying = state != null && isPlayingState(state.state)
+ val playingStateSame = (state?.state?.isPlaying() == isPlaying())
+ val actionsSame = (lastState?.actions == state?.actions) &&
+ areCustomActionListsEqual(lastState?.customActions, state?.customActions)
val resumptionChanged = resumption != mediaData.resumption
- if (playing == isPlaying && playing != null && !resumptionChanged) {
+
+ lastState = state
+
+ if ((!actionsSame || !playingStateSame) && state != null && dispatchEvents) {
+ logger.logStateCallback(key)
+ stateCallback.invoke(key, state)
+ }
+
+ if (playingStateSame && !resumptionChanged) {
return
}
- playing = isPlaying
resumption = mediaData.resumption
- if (!isPlaying) {
- logger.logScheduleTimeout(key, isPlaying, resumption!!)
+ val playing = isPlaying()
+ if (!playing) {
+ logger.logScheduleTimeout(key, playing, resumption!!)
if (cancellation != null && !resumptionChanged) {
// if the media changed resume state, we'll need to adjust the timeout length
logger.logCancelIgnored(key)
@@ -220,4 +240,50 @@ class MediaTimeoutListener @Inject constructor(
cancellation = null
}
}
+
+ private fun areCustomActionListsEqual(
+ first: List<PlaybackState.CustomAction>?,
+ second: List<PlaybackState.CustomAction>?
+ ): Boolean {
+ // Same object, or both null
+ if (first === second) {
+ return true
+ }
+
+ // Only one null, or different number of actions
+ if ((first == null || second == null) || (first.size != second.size)) {
+ return false
+ }
+
+ // Compare individual actions
+ first.asSequence().zip(second.asSequence()).forEach { (firstAction, secondAction) ->
+ if (!areCustomActionsEqual(firstAction, secondAction)) {
+ return false
+ }
+ }
+ return true
+ }
+
+ private fun areCustomActionsEqual(
+ firstAction: PlaybackState.CustomAction,
+ secondAction: PlaybackState.CustomAction
+ ): Boolean {
+ if (firstAction.action != secondAction.action ||
+ firstAction.name != secondAction.name ||
+ firstAction.icon != secondAction.icon) {
+ return false
+ }
+
+ if ((firstAction.extras == null) != (secondAction.extras == null)) {
+ return false
+ }
+ if (firstAction.extras != null) {
+ firstAction.extras.keySet().forEach { key ->
+ if (firstAction.extras.get(key) != secondAction.extras.get(key)) {
+ return false
+ }
+ }
+ }
+ return true
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutLogger.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutLogger.kt
index a86515990fcb..d9c58c0d0d76 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutLogger.kt
@@ -102,6 +102,17 @@ class MediaTimeoutLogger @Inject constructor(
}
)
+ fun logStateCallback(key: String) = buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = key
+ },
+ {
+ "dispatching state update for $key"
+ }
+ )
+
fun logScheduleTimeout(key: String, playing: Boolean, resumption: Boolean) = buffer.log(
TAG,
LogLevel.DEBUG,
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
index fecc903326f5..4f598ff797d0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
@@ -49,9 +49,11 @@ public class ResumeMediaBrowser {
private static final String TAG = "ResumeMediaBrowser";
private final Context mContext;
@Nullable private final Callback mCallback;
- private MediaBrowserFactory mBrowserFactory;
+ private final MediaBrowserFactory mBrowserFactory;
+ private final ResumeMediaBrowserLogger mLogger;
+ private final ComponentName mComponentName;
+
private MediaBrowser mMediaBrowser;
- private ComponentName mComponentName;
/**
* Initialize a new media browser
@@ -59,12 +61,17 @@ public class ResumeMediaBrowser {
* @param callback used to report media items found
* @param componentName Component name of the MediaBrowserService this browser will connect to
*/
- public ResumeMediaBrowser(Context context, @Nullable Callback callback,
- ComponentName componentName, MediaBrowserFactory browserFactory) {
+ public ResumeMediaBrowser(
+ Context context,
+ @Nullable Callback callback,
+ ComponentName componentName,
+ MediaBrowserFactory browserFactory,
+ ResumeMediaBrowserLogger logger) {
mContext = context;
mCallback = callback;
mComponentName = componentName;
mBrowserFactory = browserFactory;
+ mLogger = logger;
}
/**
@@ -76,7 +83,6 @@ public class ResumeMediaBrowser {
* ResumeMediaBrowser#disconnect will be called automatically with this function.
*/
public void findRecentMedia() {
- Log.d(TAG, "Connecting to " + mComponentName);
disconnect();
Bundle rootHints = new Bundle();
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
@@ -84,6 +90,7 @@ public class ResumeMediaBrowser {
mComponentName,
mConnectionCallback,
rootHints);
+ mLogger.logConnection(mComponentName, "findRecentMedia");
mMediaBrowser.connect();
}
@@ -196,6 +203,7 @@ public class ResumeMediaBrowser {
*/
protected void disconnect() {
if (mMediaBrowser != null) {
+ mLogger.logDisconnect(mComponentName);
mMediaBrowser.disconnect();
}
mMediaBrowser = null;
@@ -251,6 +259,7 @@ public class ResumeMediaBrowser {
disconnect();
}
}, rootHints);
+ mLogger.logConnection(mComponentName, "restart");
mMediaBrowser.connect();
}
@@ -296,6 +305,7 @@ public class ResumeMediaBrowser {
mComponentName,
mConnectionCallback,
rootHints);
+ mLogger.logConnection(mComponentName, "testConnection");
mMediaBrowser.connect();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java
index 2261aa5ac265..3d1380b6bd24 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java
@@ -27,11 +27,14 @@ import javax.inject.Inject;
public class ResumeMediaBrowserFactory {
private final Context mContext;
private final MediaBrowserFactory mBrowserFactory;
+ private final ResumeMediaBrowserLogger mLogger;
@Inject
- public ResumeMediaBrowserFactory(Context context, MediaBrowserFactory browserFactory) {
+ public ResumeMediaBrowserFactory(
+ Context context, MediaBrowserFactory browserFactory, ResumeMediaBrowserLogger logger) {
mContext = context;
mBrowserFactory = browserFactory;
+ mLogger = logger;
}
/**
@@ -43,6 +46,6 @@ public class ResumeMediaBrowserFactory {
*/
public ResumeMediaBrowser create(ResumeMediaBrowser.Callback callback,
ComponentName componentName) {
- return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory);
+ return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory, mLogger);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserLogger.kt b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserLogger.kt
new file mode 100644
index 000000000000..ccc5edc1123a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserLogger.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.media
+
+import android.content.ComponentName
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.MediaBrowserLog
+import javax.inject.Inject
+
+/** A logger for events in [ResumeMediaBrowser]. */
+@SysUISingleton
+class ResumeMediaBrowserLogger @Inject constructor(
+ @MediaBrowserLog private val buffer: LogBuffer
+) {
+ /** Logs that we've initiated a connection to a [android.media.browse.MediaBrowser]. */
+ fun logConnection(componentName: ComponentName, reason: String) = buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = componentName.toShortString()
+ str2 = reason
+ },
+ { "Connecting browser for component $str1 due to $str2" }
+ )
+
+ /** Logs that we've disconnected from a [android.media.browse.MediaBrowser]. */
+ fun logDisconnect(componentName: ComponentName) = buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = componentName.toShortString()
+ },
+ { "Disconnecting browser for component $str1" }
+ )
+}
+
+private const val TAG = "MediaBrowser"
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
index 930c5a8de125..50a96f601443 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
@@ -31,10 +31,6 @@ data class SmartspaceMediaData(
*/
val isActive: Boolean,
/**
- * Indicates if all the required data field is valid.
- */
- val isValid: Boolean,
- /**
* Package name of the media recommendations' provider-app.
*/
val packageName: String,
@@ -58,4 +54,19 @@ data class SmartspaceMediaData(
* Instance ID for [MediaUiEventLogger]
*/
val instanceId: InstanceId
-)
+) {
+ /**
+ * Indicates if all the data is valid.
+ *
+ * TODO(b/230333302): Make MediaControlPanel more flexible so that we can display fewer than
+ * [NUM_REQUIRED_RECOMMENDATIONS].
+ */
+ fun isValid() = getValidRecommendations().size >= NUM_REQUIRED_RECOMMENDATIONS
+
+ /**
+ * Returns the list of [recommendations] that have valid data.
+ */
+ fun getValidRecommendations() = recommendations.filter { it.icon != null }
+}
+
+const val NUM_REQUIRED_RECOMMENDATIONS = 3
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index 9b3b3ce6109f..dd4f1d6c9015 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -59,11 +59,14 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
private ImageView mBroadcastCodeEye;
private Boolean mIsPasswordHide = true;
private ImageView mBroadcastCodeEdit;
- private Button mStopButton;
+ private AlertDialog mAlertDialog;
+ private TextView mBroadcastErrorMessage;
static final int METADATA_BROADCAST_NAME = 0;
static final int METADATA_BROADCAST_CODE = 1;
+ private static final int MAX_BROADCAST_INFO_UPDATE = 3;
+
MediaOutputBroadcastDialog(Context context, boolean aboveStatusbar,
BroadcastSender broadcastSender, MediaOutputController mediaOutputController) {
super(context, broadcastSender, mediaOutputController);
@@ -118,14 +121,18 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
return View.VISIBLE;
}
- // TODO(b/222674827): To get the information from BluetoothLeBroadcastMetadata(Broadcast code)
- // and BluetoothLeAudioContentMetadata(Program info) when start Broadcast is successful.
- private String getBroadcastMetaDataInfo(int metaData) {
- switch (metaData) {
+ @Override
+ public void onStopButtonClick() {
+ mMediaOutputController.stopBluetoothLeBroadcast();
+ dismiss();
+ }
+
+ private String getBroadcastMetadataInfo(int metadata) {
+ switch (metadata) {
case METADATA_BROADCAST_NAME:
- return "";
+ return mMediaOutputController.getBroadcastName();
case METADATA_BROADCAST_CODE:
- return "";
+ return mMediaOutputController.getBroadcastCode();
default:
return "";
}
@@ -164,13 +171,8 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
launchBroadcastUpdatedDialog(true, mBroadcastCode.getText().toString());
});
- mBroadcastName.setText(getBroadcastMetaDataInfo(METADATA_BROADCAST_NAME));
- mBroadcastCode.setText(getBroadcastMetaDataInfo(METADATA_BROADCAST_CODE));
-
- mStopButton = getDialogView().requireViewById(R.id.stop);
- mStopButton.setOnClickListener(v -> {
- stopBroadcast();
- });
+ mBroadcastName.setText(getBroadcastMetadataInfo(METADATA_BROADCAST_NAME));
+ mBroadcastCode.setText(getBroadcastMetadataInfo(METADATA_BROADCAST_CODE));
}
private void inflateBroadcastInfoArea() {
@@ -179,16 +181,16 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
}
private void setQrCodeView() {
- //get the MetaData, and convert to BT QR code format.
- String broadcastMetaData = getBroadcastMetaData();
- if (broadcastMetaData.isEmpty()) {
+ //get the Metadata, and convert to BT QR code format.
+ String broadcastMetadata = getBroadcastMetadata();
+ if (broadcastMetadata.isEmpty()) {
//TDOD(b/226708424) Error handling for unable to generate the QR code bitmap
return;
}
try {
final int qrcodeSize = getContext().getResources().getDimensionPixelSize(
R.dimen.media_output_qrcode_size);
- final Bitmap bmp = QrCodeGenerator.encodeQrCode(broadcastMetaData, qrcodeSize);
+ final Bitmap bmp = QrCodeGenerator.encodeQrCode(broadcastMetadata, qrcodeSize);
mBroadcastQrCodeView.setImageBitmap(bmp);
} catch (WriterException e) {
//TDOD(b/226708424) Error handling for unable to generate the QR code bitmap
@@ -203,50 +205,87 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
mIsPasswordHide = !mIsPasswordHide;
}
- private void launchBroadcastUpdatedDialog(boolean isPassword, String editString) {
+ private void launchBroadcastUpdatedDialog(boolean isBroadcastCode, String editString) {
final View layout = LayoutInflater.from(mContext).inflate(
R.layout.media_output_broadcast_update_dialog, null);
final EditText editText = layout.requireViewById(R.id.broadcast_edit_text);
editText.setText(editString);
- final AlertDialog alertDialog = new Builder(mContext)
- .setTitle(isPassword ? R.string.media_output_broadcast_code
+ mBroadcastErrorMessage = layout.requireViewById(R.id.broadcast_error_message);
+ mAlertDialog = new Builder(mContext)
+ .setTitle(isBroadcastCode ? R.string.media_output_broadcast_code
: R.string.media_output_broadcast_name)
.setView(layout)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.media_output_broadcast_dialog_save,
(d, w) -> {
- updateBroadcast(isPassword, editText.getText().toString());
+ updateBroadcastInfo(isBroadcastCode, editText.getText().toString());
})
.create();
- alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- SystemUIDialog.setShowForAllUsers(alertDialog, true);
- SystemUIDialog.registerDismissListener(alertDialog);
- alertDialog.show();
+ mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
+ SystemUIDialog.registerDismissListener(mAlertDialog);
+ mAlertDialog.show();
}
- /**
- * TODO(b/222674827): The method should be get the BluetoothLeBroadcastMetadata after
- * starting the Broadcast session successfully. Then we will follow the BT QR code format
- * that convert BluetoothLeBroadcastMetadata object to String format.
- */
- private String getBroadcastMetaData() {
- return "TEST";
+ private String getBroadcastMetadata() {
+ return mMediaOutputController.getBroadcastMetadata();
}
- /**
- * TODO(b/222676140): These method are about the LE Audio Broadcast API. The framework APIS
- * will be wrapped in SettingsLib. And the UI will be executed through it.
- */
- private void updateBroadcast(boolean isPassword, String updatedString) {
+ private void updateBroadcastInfo(boolean isBroadcastCode, String updatedString) {
+ Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ if (positiveBtn != null) {
+ positiveBtn.setEnabled(false);
+ }
+ if (isBroadcastCode) {
+ handleBroadcastCodeUpdated(updatedString);
+ } else {
+ handleBroadcastNameUpdated(updatedString);
+ }
}
- /**
- * TODO(b/222676140): These method are about the LE Audio Broadcast API. The framework APIS
- * will be wrapped in SettingsLib. And the UI will be executed through it.
- */
- private void stopBroadcast() {
- dismiss();
+ private void handleBroadcastNameUpdated(String name) {
+ // TODO(b/230473995) Add the retry mechanism and error handling when update fails
+ String currentName = mMediaOutputController.getBroadcastName();
+ int retryCount = MAX_BROADCAST_INFO_UPDATE;
+ mMediaOutputController.setBroadcastName(name);
+ if (!mMediaOutputController.updateBluetoothLeBroadcast()) {
+ mMediaOutputController.setBroadcastName(currentName);
+ handleLeUpdateBroadcastFailed(retryCount);
+ }
+ }
+
+ private void handleBroadcastCodeUpdated(String newPassword) {
+ // TODO(b/230473995) Add the retry mechanism and error handling when update fails
+ String currentPassword = mMediaOutputController.getBroadcastCode();
+ int retryCount = MAX_BROADCAST_INFO_UPDATE;
+ if (!mMediaOutputController.stopBluetoothLeBroadcast()) {
+ mMediaOutputController.setBroadcastCode(currentPassword);
+ handleLeUpdateBroadcastFailed(retryCount);
+ return;
+ }
+
+ mMediaOutputController.setBroadcastCode(newPassword);
+ if (!mMediaOutputController.startBluetoothLeBroadcast()) {
+ mMediaOutputController.setBroadcastCode(currentPassword);
+ handleLeUpdateBroadcastFailed(retryCount);
+ return;
+ }
+
+ mAlertDialog.dismiss();
+ }
+
+ private void handleLeUpdateBroadcastFailed(int retryCount) {
+ final Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ mBroadcastErrorMessage.setVisibility(View.VISIBLE);
+ if (retryCount < MAX_BROADCAST_INFO_UPDATE) {
+ if (positiveBtn != null) {
+ positiveBtn.setEnabled(true);
+ }
+ mBroadcastErrorMessage.setText(R.string.media_output_broadcast_update_error);
+ } else {
+ mBroadcastErrorMessage.setText(R.string.media_output_broadcast_last_update_error);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt
new file mode 100644
index 000000000000..31266b6dc8ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt
@@ -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.media.dialog
+
+import android.content.Context
+import android.media.session.MediaSessionManager
+import android.view.View
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.media.nearby.NearbyMediaDevicesManager
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import java.util.Optional
+import javax.inject.Inject
+
+/**
+ * Factory to create [MediaOutputBroadcastDialog] objects.
+ */
+class MediaOutputBroadcastDialogFactory @Inject constructor(
+ private val context: Context,
+ private val mediaSessionManager: MediaSessionManager,
+ private val lbm: LocalBluetoothManager?,
+ private val starter: ActivityStarter,
+ private val broadcastSender: BroadcastSender,
+ private val notifCollection: CommonNotifCollection,
+ private val uiEventLogger: UiEventLogger,
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val nearbyMediaDevicesManagerOptional: Optional<NearbyMediaDevicesManager>
+) {
+ var mediaOutputBroadcastDialog: MediaOutputBroadcastDialog? = null
+
+ /** Creates a [MediaOutputBroadcastDialog] for the given package. */
+ fun create(packageName: String, aboveStatusBar: Boolean, view: View? = null) {
+ // Dismiss the previous dialog, if any.
+ mediaOutputBroadcastDialog?.dismiss()
+
+ val controller = MediaOutputController(context, packageName,
+ mediaSessionManager, lbm, starter, notifCollection,
+ dialogLaunchAnimator, nearbyMediaDevicesManagerOptional)
+ val dialog =
+ MediaOutputBroadcastDialog(context, aboveStatusBar, broadcastSender, controller)
+ mediaOutputBroadcastDialog = dialog
+
+ // Show the dialog.
+ if (view != null) {
+ dialogLaunchAnimator.showFromView(dialog, view)
+ } else {
+ dialog.show()
+ }
+ }
+
+ /** dismiss [MediaOutputBroadcastDialog] if exist. */
+ fun dismiss() {
+ mediaOutputBroadcastDialog?.dismiss()
+ mediaOutputBroadcastDialog = null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 0fbec3baffa6..8723f4fd63bb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -63,7 +63,6 @@ import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.media.BluetoothMediaDevice;
import com.android.settingslib.media.InfoMediaManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
@@ -79,6 +78,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -678,6 +678,56 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
}
+ String getBroadcastName() {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ Log.d(TAG, "getBroadcastName: LE Audio Broadcast is null");
+ return "";
+ }
+ return broadcast.getProgramInfo();
+ }
+
+ void setBroadcastName(String broadcastName) {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ Log.d(TAG, "setBroadcastName: LE Audio Broadcast is null");
+ return;
+ }
+ broadcast.setProgramInfo(broadcastName);
+ }
+
+ String getBroadcastCode() {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ Log.d(TAG, "getBroadcastCode: LE Audio Broadcast is null");
+ return "";
+ }
+ return new String(broadcast.getBroadcastCode(), StandardCharsets.UTF_8);
+ }
+
+ void setBroadcastCode(String broadcastCode) {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ Log.d(TAG, "setBroadcastCode: LE Audio Broadcast is null");
+ return;
+ }
+ broadcast.setBroadcastCode(broadcastCode.getBytes(StandardCharsets.UTF_8));
+ }
+
+ String getBroadcastMetadata() {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ Log.d(TAG, "getBroadcastMetadata: LE Audio Broadcast is null");
+ return "";
+ }
+ return broadcast.getLocalBluetoothLeBroadcastMetaData().convertToQrCodeString();
+ }
+
boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
final List<String> features = device.getFeatures();
return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)
@@ -723,6 +773,17 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
return true;
}
+ boolean updateBluetoothLeBroadcast() {
+ LocalBluetoothLeBroadcast broadcast =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (broadcast == null) {
+ Log.d(TAG, "The broadcast profile is null");
+ return false;
+ }
+ broadcast.updateBroadcast(getAppSourceName(), /*language*/ null);
+ return true;
+ }
+
void registerLeBroadcastServiceCallBack(
@NonNull @CallbackExecutor Executor executor,
@NonNull BluetoothLeBroadcast.Callback callback) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
index 7fb7d8b0eaa5..dd9d35bf2021 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
@@ -31,7 +31,8 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
* BroadcastReceiver for handling media output intent
*/
class MediaOutputDialogReceiver @Inject constructor(
- private val mediaOutputDialogFactory: MediaOutputDialogFactory
+ private val mediaOutputDialogFactory: MediaOutputDialogFactory,
+ private val mediaOutputBroadcastDialogFactory: MediaOutputBroadcastDialogFactory
) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (TextUtils.equals(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG,
@@ -43,6 +44,16 @@ class MediaOutputDialogReceiver @Inject constructor(
} else if (DEBUG) {
Log.e(TAG, "Unable to launch media output dialog. Package name is empty.")
}
+ } else if (TextUtils.equals(
+ MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG,
+ intent.action)) {
+ val packageName: String? =
+ intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
+ if (!TextUtils.isEmpty(packageName)) {
+ mediaOutputBroadcastDialogFactory.create(packageName!!, false)
+ } else if (DEBUG) {
+ Log.e(TAG, "Unable to launch media output broadcast dialog. Package name is empty.")
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
index 0fe909552cb1..abebf3e80b21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
@@ -61,6 +61,7 @@ public class QSFgsManagerFooter implements View.OnClickListener,
private final View mNumberContainer;
private final TextView mNumberView;
private final ImageView mDotView;
+ private final ImageView mCollapsedDotView;
@Nullable
private VisibilityChangedDispatcher.OnVisibilityChangedListener mVisibilityChangedListener;
@@ -75,6 +76,7 @@ public class QSFgsManagerFooter implements View.OnClickListener,
mNumberContainer = mRootView.findViewById(R.id.fgs_number_container);
mNumberView = mRootView.findViewById(R.id.fgs_number);
mDotView = mRootView.findViewById(R.id.fgs_new);
+ mCollapsedDotView = mRootView.findViewById(R.id.fgs_collapsed_new);
mContext = rootView.getContext();
mMainExecutor = mainExecutor;
mExecutor = executor;
@@ -147,8 +149,10 @@ public class QSFgsManagerFooter implements View.OnClickListener,
if (mFgsManagerController.shouldUpdateFooterVisibility()) {
mRootView.setVisibility(mNumPackages > 0
&& mFgsManagerController.isAvailable() ? View.VISIBLE : View.GONE);
- mDotView.setVisibility(
- mFgsManagerController.getChangesSinceDialog() ? View.VISIBLE : View.GONE);
+ int dotVis =
+ mFgsManagerController.getChangesSinceDialog() ? View.VISIBLE : View.GONE;
+ mDotView.setVisibility(dotVis);
+ mCollapsedDotView.setVisibility(dotVis);
if (mVisibilityChangedListener != null) {
mVisibilityChangedListener.onVisibilityChanged(mRootView.getVisibility());
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 9ef90ecf50a7..311ee56477de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -17,6 +17,7 @@ import com.android.systemui.R;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
import com.android.systemui.qs.tileimpl.HeightOverrideable;
+import com.android.systemui.qs.tileimpl.QSTileViewImplKt;
import java.util.ArrayList;
@@ -242,7 +243,12 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
record.tileView.setLeftTopRightBottom(left, top, right, bottom);
}
record.tileView.setPosition(i);
- mLastTileBottom = bottom;
+ if (forLayout) {
+ mLastTileBottom = record.tileView.getBottom();
+ } else {
+ float scale = QSTileViewImplKt.constrainSquishiness(mSquishinessFraction);
+ mLastTileBottom = top + (int) (record.tileView.getMeasuredHeight() * scale);
+ }
}
}
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 72dad0608a3a..59164dea9c45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -262,7 +262,7 @@ open class QSTileViewImpl @JvmOverloads constructor(
}
// Limit how much we affect the height, so we don't have rounding artifacts when the tile
// is too short.
- val constrainedSquishiness = 0.1f + squishinessFraction * 0.9f
+ val constrainedSquishiness = constrainSquishiness(squishinessFraction)
bottom = top + (actualHeight * constrainedSquishiness).toInt()
scrollY = (actualHeight - height) / 2
}
@@ -678,6 +678,10 @@ internal object SubtitleArrayMapping {
}
}
+fun constrainSquishiness(squish: Float): Float {
+ return 0.1f + squish * 0.9f
+}
+
private fun colorValuesHolder(name: String, vararg values: Int): PropertyValuesHolder {
return PropertyValuesHolder.ofInt(name, *values).apply {
setEvaluator(ArgbEvaluator.getInstance())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 5585cde528fa..aa80b730d24f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -23,7 +23,7 @@ import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.ONLY_CORE_APPS;
+import static com.android.systemui.statusbar.phone.CentralSurfacesImpl.ONLY_CORE_APPS;
import android.annotation.Nullable;
import android.app.ITransientNotificationCallback;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 270bdc785178..0a616c095551 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -17,6 +17,7 @@ import android.util.MathUtils.lerp
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 java.util.function.Consumer
/**
@@ -367,7 +368,7 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
}
if (startColorAlpha > 0f) {
- canvas.drawColor(updateColorAlpha(revealGradientEndColor, startColorAlpha))
+ canvas.drawColor(getColorWithAlpha(revealGradientEndColor, startColorAlpha))
}
with(shaderGradientMatrix) {
@@ -383,15 +384,7 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
private fun setPaintColorFilter() {
gradientPaint.colorFilter = PorterDuffColorFilter(
- updateColorAlpha(revealGradientEndColor, revealGradientEndColorAlpha),
+ getColorWithAlpha(revealGradientEndColor, revealGradientEndColorAlpha),
PorterDuff.Mode.MULTIPLY)
}
-
- private fun updateColorAlpha(color: Int, alpha: Float): Int =
- Color.argb(
- (alpha * 255).toInt(),
- Color.red(color),
- Color.green(color),
- Color.blue(color)
- )
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index afce945a5bc5..734bc48093b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -39,7 +39,6 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.NotificationBackgroundView;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
@@ -411,7 +410,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
setBackgroundTop(backgroundTop);
setFirstElementRoundness(firstElementRoundness);
mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex());
- mShelfIcons.calculateIconTranslations();
+ mShelfIcons.calculateIconXTranslations();
mShelfIcons.applyIconStates();
for (int i = 0; i < mHostLayoutController.getChildCount(); i++) {
View child = mHostLayoutController.getChildAt(i);
@@ -636,7 +635,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
float viewEnd = viewStart + fullHeight;
float fullTransitionAmount = 0.0f;
float iconTransitionAmount = 0.0f;
- float shelfStart = getTranslationY();
+ float shelfStart = getTranslationY() - mPaddingBetweenElements;
if (mAmbientState.isExpansionChanging() && !mAmbientState.isOnKeyguard()) {
// TODO(b/172289889) handle icon placement for notification that is clipped by the shelf
if (mIndexOfFirstViewInShelf != -1 && i >= mIndexOfFirstViewInShelf) {
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 dea429f6c617..d49e1e6acc23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -33,7 +33,6 @@ import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkScoreManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
@@ -225,10 +224,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
TelephonyManager telephonyManager,
TelephonyListenerManager telephonyListenerManager,
@Nullable WifiManager wifiManager,
- NetworkScoreManager networkScoreManager,
AccessPointControllerImpl accessPointController,
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
+ WifiStatusTrackerFactory trackerFactory,
@Main Handler handler,
InternetDialogFactory internetDialogFactory,
FeatureFlags featureFlags,
@@ -237,7 +236,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
telephonyManager,
telephonyListenerManager,
wifiManager,
- networkScoreManager,
subscriptionManager,
Config.readConfig(context),
bgLooper,
@@ -250,6 +248,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
broadcastDispatcher,
demoModeController,
carrierConfigTracker,
+ trackerFactory,
handler,
featureFlags,
dumpManager);
@@ -262,8 +261,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
TelephonyManager telephonyManager,
TelephonyListenerManager telephonyListenerManager,
WifiManager wifiManager,
- NetworkScoreManager networkScoreManager,
- SubscriptionManager subManager, Config config, Looper bgLooper,
+ SubscriptionManager subManager,
+ Config config,
+ Looper bgLooper,
Executor bgExecutor,
CallbackHandler callbackHandler,
AccessPointControllerImpl accessPointController,
@@ -273,6 +273,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
BroadcastDispatcher broadcastDispatcher,
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
+ WifiStatusTrackerFactory trackerFactory,
@Main Handler handler,
FeatureFlags featureFlags,
DumpManager dumpManager
@@ -315,9 +316,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
notifyControllersMobileDataChanged();
}
});
+
mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
- mCallbackHandler, this, mWifiManager, mConnectivityManager, networkScoreManager,
- mMainHandler, mReceiverHandler);
+ mCallbackHandler, this, mWifiManager, trackerFactory,
+ mReceiverHandler);
mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
index e2806a39130f..7e8f04e4bc23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
@@ -50,7 +50,7 @@ public abstract class SignalController<T extends ConnectivityState, I extends Ic
protected final T mLastState;
protected final int mTransportType;
protected final Context mContext;
- // The owner of the SignalController (i.e. NetworkController will maintain the following
+ // The owner of the SignalController (i.e. NetworkController) will maintain the following
// lists and call notifyListeners whenever the list has changed to ensure everyone
// is aware of current state.
protected final NetworkControllerImpl mNetworkController;
@@ -103,7 +103,7 @@ public abstract class SignalController<T extends ConnectivityState, I extends Ic
* Determines if the state of this signal controller has changed and
* needs to trigger callbacks related to it.
*/
- public boolean isDirty() {
+ boolean isDirty() {
if (!mLastState.equals(mCurrentState)) {
if (DEBUG) {
Log.d(mTag, "Change in state from: " + mLastState + "\n"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index a4589c8dd6d3..87cdb17245f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -21,14 +21,13 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OU
import android.content.Context;
import android.content.Intent;
-import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
-import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.text.Html;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.android.settingslib.SignalIcon.IconGroup;
import com.android.settingslib.SignalIcon.MobileIconGroup;
import com.android.settingslib.graph.SignalDrawable;
@@ -36,8 +35,6 @@ import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.wifi.WifiStatusTracker;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.Assert;
import java.io.PrintWriter;
@@ -49,21 +46,21 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
private final MobileIconGroup mCarrierMergedWifiIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
private final WifiManager mWifiManager;
+ private final Handler mBgHandler;
+
public WifiSignalController(
Context context,
boolean hasMobileDataFeature,
CallbackHandler callbackHandler,
NetworkControllerImpl networkController,
WifiManager wifiManager,
- ConnectivityManager connectivityManager,
- NetworkScoreManager networkScoreManager,
- @Main Handler handler,
- @Background Handler backgroundHandler) {
+ WifiStatusTrackerFactory trackerFactory,
+ @Background Handler bgHandler) {
super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
callbackHandler, networkController);
+ mBgHandler = bgHandler;
mWifiManager = wifiManager;
- mWifiTracker = new WifiStatusTracker(mContext, wifiManager, networkScoreManager,
- connectivityManager, this::handleStatusUpdated, handler, backgroundHandler);
+ mWifiTracker = trackerFactory.createTracker(this::handleStatusUpdated, bgHandler);
mWifiTracker.setListening(true);
mHasMobileDataFeature = hasMobileDataFeature;
if (wifiManager != null) {
@@ -181,33 +178,51 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
* Fetches wifi initial state replacing the initial sticky broadcast.
*/
public void fetchInitialState() {
- mWifiTracker.fetchInitialState();
- copyWifiStates();
- notifyListenersIfNecessary();
+ doInBackground(() -> {
+ mWifiTracker.fetchInitialState();
+ copyWifiStates();
+ notifyListenersIfNecessary();
+ });
}
/**
* Extract wifi state directly from broadcasts about changes in wifi state.
*/
- public void handleBroadcast(Intent intent) {
- mWifiTracker.handleBroadcast(intent);
- copyWifiStates();
- notifyListenersIfNecessary();
+ void handleBroadcast(Intent intent) {
+ doInBackground(() -> {
+ mWifiTracker.handleBroadcast(intent);
+ copyWifiStates();
+ notifyListenersIfNecessary();
+ });
}
private void handleStatusUpdated() {
- Assert.isMainThread();
- copyWifiStates();
- notifyListenersIfNecessary();
+ // The WifiStatusTracker callback comes in on the main thread, but the rest of our data
+ // access happens on the bgHandler
+ doInBackground(() -> {
+ copyWifiStates();
+ notifyListenersIfNecessary();
+ });
+ }
+
+ private void doInBackground(Runnable action) {
+ if (Thread.currentThread() != mBgHandler.getLooper().getThread()) {
+ mBgHandler.post(action);
+ } else {
+ action.run();
+ }
}
private void copyWifiStates() {
+ // Data access should only happen on our bg thread
+ Preconditions.checkState(mBgHandler.getLooper().isCurrentThread());
+
mCurrentState.enabled = mWifiTracker.enabled;
mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
- notifyWifiLevelChangeIfNecessary(mWifiTracker.level);
+ boolean levelChanged = mCurrentState.level != mWifiTracker.level;
mCurrentState.level = mWifiTracker.level;
mCurrentState.statusLabel = mWifiTracker.statusLabel;
mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
@@ -215,11 +230,9 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
mCurrentState.iconGroup =
mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup
: mUnmergedWifiIconGroup;
- }
- void notifyWifiLevelChangeIfNecessary(int level) {
- if (level != mCurrentState.level) {
- mNetworkController.notifyWifiLevelChange(level);
+ if (levelChanged) {
+ mNetworkController.notifyWifiLevelChange(mCurrentState.level);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiStatusTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiStatusTrackerFactory.kt
new file mode 100644
index 000000000000..9dc17d0f50de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiStatusTrackerFactory.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 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.Context
+import android.net.ConnectivityManager
+import android.net.NetworkScoreManager
+import android.net.wifi.WifiManager
+import android.os.Handler
+
+import com.android.settingslib.wifi.WifiStatusTracker
+import com.android.systemui.dagger.qualifiers.Main
+
+import javax.inject.Inject
+
+/**
+ * Factory class for [WifiStatusTracker] which lives in SettingsLib (and thus doesn't use Dagger).
+ * This enables the constructors for NetworkControllerImpl and WifiSignalController to be slightly
+ * nicer.
+ */
+internal class WifiStatusTrackerFactory @Inject constructor(
+ private val mContext: Context,
+ private val mWifiManager: WifiManager?,
+ private val mNetworkScoreManager: NetworkScoreManager,
+ private val mConnectivityManager: ConnectivityManager,
+ @Main private val mMainHandler: Handler
+) {
+ fun createTracker(callback: Runnable?, bgHandler: Handler?): WifiStatusTracker {
+ return WifiStatusTracker(mContext,
+ mWifiManager,
+ mNetworkScoreManager,
+ mConnectivityManager,
+ callback,
+ mMainHandler,
+ bgHandler)
+ }
+}
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 83290af24f2a..29411e63b163 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -61,6 +61,7 @@ import com.android.systemui.statusbar.notification.collection.legacy.VisualStabi
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
@@ -87,7 +88,7 @@ import dagger.Module;
import dagger.Provides;
/**
- * This module provides instances needed to construct {@link CentralSurfaces}. These are moved to
+ * This module provides instances needed to construct {@link CentralSurfacesImpl}. These are moved to
* this separate from {@link CentralSurfacesModule} module so that components that wish to build
* their own version of CentralSurfaces can include just dependencies, without injecting
* CentralSurfaces itself.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
index fe55dea7333a..e84d31dd6a0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.dagger
import com.android.systemui.CoreStartable
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.CentralSurfacesImpl
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -29,5 +30,5 @@ interface StartCentralSurfacesModule {
@Binds
@IntoMap
@ClassKey(CentralSurfaces::class)
- abstract fun bindsCentralSurfaces(centralSurfaces: CentralSurfaces): CoreStartable
+ abstract fun bindsCentralSurfaces(centralSurfaces: CentralSurfacesImpl): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index a4e2d5ec0829..a35bced819c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -28,7 +28,8 @@ import android.database.ContentObserver
import android.net.Uri
import android.os.Handler
import android.os.UserHandle
-import android.provider.Settings
+import android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
+import android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS
import android.util.Log
import android.view.View
import android.view.ViewGroup
@@ -85,6 +86,7 @@ class LockscreenSmartspaceController @Inject constructor(
// Smartspace can be used on multiple displays, such as when the user casts their screen
private var smartspaceViews = mutableSetOf<SmartspaceView>()
+ private var showNotifications = false
private var showSensitiveContentForCurrentUser = false
private var showSensitiveContentForManagedUser = false
private var managedUserHandle: UserHandle? = null
@@ -233,7 +235,13 @@ class LockscreenSmartspaceController @Inject constructor(
deviceProvisionedController.removeCallback(deviceProvisionedListener)
userTracker.addCallback(userTrackerCallback, uiExecutor)
contentResolver.registerContentObserver(
- secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ secureSettings.getUriFor(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ true,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
+ contentResolver.registerContentObserver(
+ secureSettings.getUriFor(LOCK_SCREEN_SHOW_NOTIFICATIONS),
true,
settingsObserver,
UserHandle.USER_ALL
@@ -286,6 +294,9 @@ class LockscreenSmartspaceController @Inject constructor(
}
private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
+ if (!showNotifications) {
+ return t.getFeatureType() == SmartspaceTarget.FEATURE_WEATHER
+ }
return when (t.userHandle) {
userTracker.userHandle -> {
!t.isSensitive || showSensitiveContentForCurrentUser
@@ -310,16 +321,26 @@ class LockscreenSmartspaceController @Inject constructor(
}
private fun reloadSmartspace() {
- val setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
-
- showSensitiveContentForCurrentUser =
- secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1
+ showNotifications = secureSettings.getIntForUser(
+ LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ 0,
+ userTracker.userId
+ ) == 1
+
+ showSensitiveContentForCurrentUser = secureSettings.getIntForUser(
+ LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ 0,
+ userTracker.userId
+ ) == 1
managedUserHandle = getWorkProfileUser()
val managedId = managedUserHandle?.identifier
if (managedId != null) {
- showSensitiveContentForManagedUser =
- secureSettings.getIntForUser(setting, 0, managedId) == 1
+ showSensitiveContentForManagedUser = secureSettings.getIntForUser(
+ LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ 0,
+ managedId
+ ) == 1
}
session?.requestSmartspaceUpdate()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 51af9559eda2..1b5e52d7f8fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -709,8 +709,8 @@ public class ShadeListBuilder implements Dumpable {
new ArraySet<>(groupsWithChildrenLostToStability);
// Any group which lost a child to filtering or promotion is exempt from having its summary
// promoted when it has no attached children.
- getGroupsWithChildrenLostToFiltering(groupsExemptFromSummaryPromotion);
- getGroupsWithChildrenLostToPromotion(shadeList, groupsExemptFromSummaryPromotion);
+ addGroupsWithChildrenLostToFiltering(groupsExemptFromSummaryPromotion);
+ addGroupsWithChildrenLostToPromotion(shadeList, groupsExemptFromSummaryPromotion);
// Iterate backwards, so that we can remove elements without affecting indices of
// yet-to-be-accessed entries.
@@ -749,7 +749,7 @@ public class ShadeListBuilder implements Dumpable {
continue;
}
if (group.wasAttachedInPreviousPass()
- && !getStabilityManager().isGroupChangeAllowed(group.getSummary())) {
+ && !getStabilityManager().isGroupPruneAllowed(group)) {
checkState(!children.isEmpty(), "empty group should have been pruned");
// This group was previously attached and group changes aren't
// allowed; keep it around until group changes are allowed again.
@@ -865,7 +865,7 @@ public class ShadeListBuilder implements Dumpable {
*
* These groups will be exempt from appearing without any children.
*/
- private void getGroupsWithChildrenLostToPromotion(List<ListEntry> shadeList, Set<String> out) {
+ private void addGroupsWithChildrenLostToPromotion(List<ListEntry> shadeList, Set<String> out) {
for (int i = 0; i < shadeList.size(); i++) {
final ListEntry tle = shadeList.get(i);
if (tle.getAttachState().getPromoter() != null) {
@@ -882,13 +882,13 @@ public class ShadeListBuilder implements Dumpable {
*
* These groups will be exempt from appearing without any children.
*/
- private void getGroupsWithChildrenLostToFiltering(Set<String> out) {
+ private void addGroupsWithChildrenLostToFiltering(Set<String> out) {
for (ListEntry tle : mAllEntries) {
StatusBarNotification sbn = tle.getRepresentativeEntry().getSbn();
if (sbn.isGroup()
&& !sbn.getNotification().isGroupSummary()
&& tle.getAttachState().getExcludingFilter() != null) {
- out.add(sbn.getGroup());
+ out.add(sbn.getGroupKey());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 085ce859de87..d7bd95c0949e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -27,6 +27,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -139,6 +140,13 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
}
@Override
+ public boolean isGroupPruneAllowed(@NonNull GroupEntry entry) {
+ final boolean isGroupPruneAllowedForEntry = mReorderingAllowed;
+ mIsSuppressingGroupChange |= !isGroupPruneAllowedForEntry;
+ return isGroupPruneAllowedForEntry;
+ }
+
+ @Override
public boolean isSectionChangeAllowed(@NonNull NotificationEntry entry) {
final boolean isSectionChangeAllowedForEntry =
mReorderingAllowed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
index 446449864606..58bbca822164 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -52,6 +53,14 @@ abstract class NotifStabilityManager protected constructor(name: String) :
abstract fun isGroupChangeAllowed(entry: NotificationEntry): Boolean
/**
+ * Returns whether this notification group can be pruned for not having enough children.
+ * Per iteration of the notification pipeline, locally stores this information until the next
+ * run of the pipeline. When this method returns false, it's expected that a group prune for
+ * this entry is being suppressed.
+ */
+ abstract fun isGroupPruneAllowed(entry: GroupEntry): Boolean
+
+ /**
* Returns whether this notification entry can currently change sections.
* Per iteration of the notification pipeline, locally stores this information until the next
* run of the pipeline. When this method returns false, it's expected that a section change is
@@ -89,6 +98,7 @@ object DefaultNotifStabilityManager : NotifStabilityManager("DefaultNotifStabili
override fun isPipelineRunAllowed(): Boolean = true
override fun onBeginRun() {}
override fun isGroupChangeAllowed(entry: NotificationEntry): Boolean = true
+ override fun isGroupPruneAllowed(entry: GroupEntry): Boolean = true
override fun isSectionChangeAllowed(entry: NotificationEntry): Boolean = true
override fun isEntryReorderingAllowed(entry: ListEntry): Boolean = true
override fun isEveryChangeAllowed(): Boolean = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 032e6784ae08..386e2d31380c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.render
import android.annotation.MainThread
import android.view.View
+import com.android.systemui.util.kotlin.transform
import com.android.systemui.util.traceSection
/**
@@ -40,6 +41,7 @@ class ShadeViewDiffer(
) {
private val rootNode = ShadeNode(rootController)
private val nodes = mutableMapOf(rootController to rootNode)
+ private val views = mutableMapOf<View, ShadeNode>()
/**
* Adds and removes views from the root (and its children) until their structure matches the
@@ -64,25 +66,26 @@ class ShadeViewDiffer(
*
* For debugging purposes.
*/
- fun getViewLabel(view: View): String =
- nodes.values.firstOrNull { node -> node.view === view }?.label ?: view.toString()
-
- private fun detachChildren(parentNode: ShadeNode, specMap: Map<NodeController, NodeSpec>) {
- val views = nodes.values.asSequence().map { node -> node.view to node }.toMap()
- fun detachRecursively(parentNode: ShadeNode, specMap: Map<NodeController, NodeSpec>) {
- val parentSpec = specMap[parentNode.controller]
- for (i in parentNode.getChildCount() - 1 downTo 0) {
- val childView = parentNode.getChildAt(i)
- views[childView]?.let { childNode ->
- val childSpec = specMap[childNode.controller]
- maybeDetachChild(parentNode, parentSpec, childNode, childSpec)
- if (childNode.controller.getChildCount() > 0) {
- detachRecursively(childNode, specMap)
- }
+ fun getViewLabel(view: View): String = views[view]?.label ?: view.toString()
+
+ private fun detachChildren(
+ parentNode: ShadeNode,
+ specMap: Map<NodeController, NodeSpec>
+ ) {
+ val parentSpec = specMap[parentNode.controller]
+
+ for (i in parentNode.getChildCount() - 1 downTo 0) {
+ val childView = parentNode.getChildAt(i)
+ views[childView]?.let { childNode ->
+ val childSpec = specMap[childNode.controller]
+
+ maybeDetachChild(parentNode, parentSpec, childNode, childSpec)
+
+ if (childNode.controller.getChildCount() > 0) {
+ detachChildren(childNode, specMap)
}
}
}
- detachRecursively(parentNode, specMap)
}
private fun maybeDetachChild(
@@ -91,13 +94,14 @@ class ShadeViewDiffer(
childNode: ShadeNode,
childSpec: NodeSpec?
) {
- val newParentNode = childSpec?.parent?.let { getNode(it) }
+ val newParentNode = transform(childSpec?.parent) { getNode(it) }
if (newParentNode != parentNode) {
val childCompletelyRemoved = newParentNode == null
if (childCompletelyRemoved) {
nodes.remove(childNode.controller)
+ views.remove(childNode.controller.view)
}
logger.logDetachingChild(
@@ -111,7 +115,10 @@ class ShadeViewDiffer(
}
}
- private fun attachChildren(parentNode: ShadeNode, specMap: Map<NodeController, NodeSpec>) {
+ private fun attachChildren(
+ parentNode: ShadeNode,
+ specMap: Map<NodeController, NodeSpec>
+ ) {
val parentSpec = checkNotNull(specMap[parentNode.controller])
for ((index, childSpec) in parentSpec.children.withIndex()) {
@@ -153,6 +160,7 @@ class ShadeViewDiffer(
if (node == null) {
node = ShadeNode(spec.controller)
nodes[node.controller] = node
+ views[node.view] = node
}
return node
}
@@ -186,9 +194,10 @@ class ShadeViewDiffer(
private class DuplicateNodeException(message: String) : RuntimeException(message)
-private class ShadeNode(val controller: NodeController) {
- val view: View
- get() = controller.view
+private class ShadeNode(
+ val controller: NodeController
+) {
+ val view = controller.view
var parent: ShadeNode? = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index c2750c2d2a6f..2493ccbe5a48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -117,6 +117,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
+import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -178,6 +179,7 @@ public class NotificationStackScrollLayoutController {
private final CentralSurfaces mCentralSurfaces;
private final SectionHeaderController mSilentHeaderController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final ShadeTransitionController mShadeTransitionController;
private final InteractionJankMonitor mJankMonitor;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private final StackStateLogger mStackStateLogger;
@@ -647,6 +649,7 @@ public class NotificationStackScrollLayoutController {
NotifCollection notifCollection,
NotificationEntryManager notificationEntryManager,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
+ ShadeTransitionController shadeTransitionController,
IStatusBarService iStatusBarService,
UiEventLogger uiEventLogger,
LayoutInflater layoutInflater,
@@ -675,6 +678,7 @@ public class NotificationStackScrollLayoutController {
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
+ mShadeTransitionController = shadeTransitionController;
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mResources = resources;
@@ -769,6 +773,7 @@ public class NotificationStackScrollLayoutController {
mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming);
mLockscreenShadeTransitionController.setStackScroller(this);
+ mShadeTransitionController.setNotificationStackScrollLayoutController(this);
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 2b11f693da1d..9ea36d540f4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -207,9 +207,7 @@ constructor(
visibleIndex: Int
): Float {
var height = stack.calculateGapHeight(previous, current, visibleIndex)
- if (visibleIndex != 0) {
- height += dividerHeight
- }
+ height += dividerHeight
return height
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 22242b8fb4b4..2b79986662f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -314,7 +314,8 @@ public class StackScrollAlgorithm {
if (ambientState.getShelf() != null) {
final float shelfStart = ambientState.getStackEndHeight()
- - ambientState.getShelf().getIntrinsicHeight();
+ - ambientState.getShelf().getIntrinsicHeight()
+ - mPaddingBetweenElements;
if (currentY >= shelfStart
&& !(view instanceof FooterView)
&& state.firstViewInShelf == null) {
@@ -507,8 +508,9 @@ public class StackScrollAlgorithm {
|| bypassPulseNotExpanding
? ambientState.getInnerHeight()
: (int) ambientState.getStackHeight();
- final int shelfStart =
- stackBottom - ambientState.getShelf().getIntrinsicHeight();
+ final int shelfStart = stackBottom
+ - ambientState.getShelf().getIntrinsicHeight()
+ - mPaddingBetweenElements;
viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart);
if (viewState.yTranslation >= shelfStart) {
viewState.hidden = !view.isExpandAnimationRunning()
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 83017c41629c..9c6ba3af5154 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 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.
@@ -16,4528 +16,570 @@
package com.android.systemui.statusbar.phone;
-import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
-import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.app.StatusBarManager.WindowVisibleState;
-import static android.app.StatusBarManager.windowStateToString;
-import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.containsType;
-import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
-
-import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
-import static androidx.lifecycle.Lifecycle.State.RESUMED;
-
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
-import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.IWallpaperManager;
-import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.StatusBarManager;
-import android.app.TaskInfo;
-import android.app.TaskStackBuilder;
-import android.app.UiModeManager;
-import android.app.WallpaperInfo;
-import android.app.WallpaperManager;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks2;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.hardware.devicestate.DeviceStateManager;
-import android.metrics.LogMaker;
-import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.Trace;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.dreams.DreamService;
-import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.DisplayMetrics;
-import android.util.EventLog;
-import android.util.IndentingPrintWriter;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
-import android.view.Display;
-import android.view.IRemoteAnimationRunner;
-import android.view.IWindowManager;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
-import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowInsetsController.Appearance;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.DateTimeView;
import android.window.SplashScreen;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEvent;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.ActivityIntentHelper;
-import com.android.systemui.AutoReinflateContainer;
-import com.android.systemui.CoreStartable;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.EventLogTags;
-import com.android.systemui.InitController;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
+import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.animation.RemoteTransitionAdapter;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.camera.CameraIntents;
-import com.android.systemui.charging.WirelessChargingAnimation;
-import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.demomode.DemoMode;
-import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.emergency.EmergencyGesture;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.fragments.ExtensionFragmentListener;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.keyguard.KeyguardService;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.OverlayPlugin;
-import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSFragment;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.qs.QSPanelController;
-import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.scrim.ScrimView;
-import com.android.systemui.settings.brightness.BrightnessSliderController;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.AutoHideUiElement;
-import com.android.systemui.statusbar.BackDropView;
-import com.android.systemui.statusbar.CircleReveal;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.GestureRecorder;
-import com.android.systemui.statusbar.KeyboardShortcuts;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.LiftReveal;
import com.android.systemui.statusbar.LightRevealScrim;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.NotificationShelfController;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.PowerButtonReveal;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.charging.WiredChargingRippleController;
-import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.core.StatusBarInitializer;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
-import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
-import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent;
-import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BrightnessMirrorController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-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.util.DumpUtilsKt;
-import com.android.systemui.util.WallpaperController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.concurrency.MessageRouter;
-import com.android.systemui.volume.VolumeComponent;
-import com.android.systemui.wmshell.BubblesManager;
-import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
-import com.android.wm.shell.startingsurface.StartingSurface;
import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-
-import dagger.Lazy;
-
-/**
- * A class handling initialization and coordination between some of the key central surfaces in
- * System UI: The notification shade, the keyguard (lockscreen), and the status bar.
- *
- * This class is not our ideal architecture because it doesn't enforce much isolation between these
- * three mostly disparate surfaces. In an ideal world, this class would not exist. Instead, we would
- * break it up into three modules -- one for each of those three surfaces -- and we would define any
- * APIs that are needed for these surfaces to communicate with each other when necessary.
- *
- * <b>If at all possible, please avoid adding additional code to this monstrous class! Our goal is
- * to break up this class into many small classes, and any code added here will slow down that goal.
- * </b>
- */
-public class CentralSurfaces extends CoreStartable implements
- ActivityStarter,
- LifecycleOwner {
- public static final boolean MULTIUSER_DEBUG = false;
-
- protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
+public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwner {
+ boolean MULTIUSER_DEBUG = false;
// Should match the values in PhoneWindowManager
- public static final String SYSTEM_DIALOG_REASON_KEY = "reason";
- public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
- public static final String SYSTEM_DIALOG_REASON_DREAM = "dream";
- static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
-
- private static final String BANNER_ACTION_CANCEL =
- "com.android.systemui.statusbar.banner_action_cancel";
- private static final String BANNER_ACTION_SETUP =
- "com.android.systemui.statusbar.banner_action_setup";
- public static final String TAG = "CentralSurfaces";
- public static final boolean DEBUG = false;
- public static final boolean SPEW = false;
- public static final boolean DUMPTRUCK = true; // extra dumpsys info
- public static final boolean DEBUG_GESTURES = false;
- public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
- public static final boolean DEBUG_CAMERA_LIFT = false;
-
- public static final boolean DEBUG_WINDOW_STATE = false;
-
+ String SYSTEM_DIALOG_REASON_KEY = "reason";
+ String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
+ String SYSTEM_DIALOG_REASON_DREAM = "dream";
+ String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
+ String TAG = "CentralSurfaces";
+ boolean DEBUG = false;
+ boolean SPEW = false;
+ boolean DUMPTRUCK = true; // extra dumpsys info
+ boolean DEBUG_GESTURES = false;
+ boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
+ boolean DEBUG_CAMERA_LIFT = false;
+ boolean DEBUG_WINDOW_STATE = false;
// additional instrumentation for testing purposes; intended to be left on during development
- public static final boolean CHATTY = DEBUG;
-
- public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
-
- public static final String ACTION_FAKE_ARTWORK = "fake_artwork";
-
- private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
- private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003;
- // 1020-1040 reserved for BaseStatusBar
-
- // Time after we abort the launch transition.
- static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000;
-
- protected static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
-
- /**
- * The delay to reset the hint text when the hint animation is finished running.
- */
- private static final int HINT_RESET_DELAY_MS = 1200;
-
- public static final int FADE_KEYGUARD_START_DELAY = 100;
- public static final int FADE_KEYGUARD_DURATION = 300;
- public static final int FADE_KEYGUARD_DURATION_PULSING = 96;
-
- public static final long[] CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS =
+ boolean CHATTY = DEBUG;
+ boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
+ String ACTION_FAKE_ARTWORK = "fake_artwork";
+ int FADE_KEYGUARD_START_DELAY = 100;
+ int FADE_KEYGUARD_DURATION = 300;
+ int FADE_KEYGUARD_DURATION_PULSING = 96;
+ long[] CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS =
new long[]{20, 20, 20, 20, 100, 20};
- public static final int[] CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES =
+ int[] CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES =
new int[]{39, 82, 139, 213, 0, 127};
- /**
- * If true, the system is in the half-boot-to-decryption-screen state.
- * Prudently disable QS and notifications.
- */
- public static final boolean ONLY_CORE_APPS;
-
/** If true, the lockscreen will show a distinct wallpaper */
- public static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true;
-
- private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
-
- static {
- boolean onlyCoreApps;
- try {
- IPackageManager packageManager =
- IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
- onlyCoreApps = packageManager != null && packageManager.isOnlyCoreApps();
- } catch (RemoteException e) {
- onlyCoreApps = false;
- }
- ONLY_CORE_APPS = onlyCoreApps;
- }
+ boolean ENABLE_LOCKSCREEN_WALLPAPER = true;
+ // Time after we abort the launch transition.
+ long LAUNCH_TRANSITION_TIMEOUT_MS = 5000;
+ int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
- private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- private final DreamOverlayStateController mDreamOverlayStateController;
- private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
- private float mTransitionToFullShadeProgress = 0f;
- private NotificationListContainer mNotifListContainer;
-
- private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- boolean occluded = mKeyguardStateController.isOccluded();
- mStatusBarHideIconsForBouncerManager.setIsOccludedAndTriggerUpdate(occluded);
- mScrimController.setKeyguardOccluded(occluded);
- }
- };
-
- void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
- updateBubblesVisibility();
- mStatusBarWindowState = state;
- }
+ static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
- void acquireGestureWakeLock(long time) {
- mGestureWakeLock.acquire(time);
+ static String viewInfo(View v) {
+ return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
+ + ") " + v.getWidth() + "x" + v.getHeight() + "]";
}
- boolean setAppearance(int appearance) {
- if (mAppearance != appearance) {
- mAppearance = appearance;
- return updateBarMode(barMode(isTransientShown(), appearance));
+ static void dumpBarTransitions(
+ PrintWriter pw, String var, @Nullable BarTransitions transitions) {
+ pw.print(" ");
+ pw.print(var);
+ pw.print(".BarTransitions.mMode=");
+ if (transitions != null) {
+ pw.println(BarTransitions.modeToString(transitions.getMode()));
+ } else {
+ pw.println("Unknown");
}
-
- return false;
- }
-
- int getBarMode() {
- return mStatusBarMode;
- }
-
- void resendMessage(int msg) {
- mMessageRouter.cancelMessages(msg);
- mMessageRouter.sendMessage(msg);
- }
-
- void resendMessage(Object msg) {
- mMessageRouter.cancelMessages(msg.getClass());
- mMessageRouter.sendMessage(msg);
- }
-
- int getDisabled1() {
- return mDisabled1;
- }
-
- void setDisabled1(int disabled) {
- mDisabled1 = disabled;
- }
-
- int getDisabled2() {
- return mDisabled2;
- }
-
- void setDisabled2(int disabled) {
- mDisabled2 = disabled;
- }
-
- void setLastCameraLaunchSource(int source) {
- mLastCameraLaunchSource = source;
- }
-
- void setLaunchCameraOnFinishedGoingToSleep(boolean launch) {
- mLaunchCameraOnFinishedGoingToSleep = launch;
- }
-
- void setLaunchCameraOnFinishedWaking(boolean launch) {
- mLaunchCameraWhenFinishedWaking = launch;
- }
-
- void setLaunchEmergencyActionOnFinishedGoingToSleep(boolean launch) {
- mLaunchEmergencyActionOnFinishedGoingToSleep = launch;
- }
-
- void setLaunchEmergencyActionOnFinishedWaking(boolean launch) {
- mLaunchEmergencyActionWhenFinishedWaking = launch;
}
- void setTopHidesStatusBar(boolean hides) {
- mTopHidesStatusBar = hides;
- }
-
- QSPanelController getQSPanelController() {
- return mQSPanelController;
- }
-
- /** */
- public void animateExpandNotificationsPanel() {
- mCommandQueueCallbacks.animateExpandNotificationsPanel();
- }
-
- /** */
- public void animateExpandSettingsPanel(@Nullable String subpanel) {
- mCommandQueueCallbacks.animateExpandSettingsPanel(subpanel);
- }
-
- /** */
- public void animateCollapsePanels(int flags, boolean force) {
- mCommandQueueCallbacks.animateCollapsePanels(flags, force);
- }
-
- /** */
- public void togglePanel() {
- mCommandQueueCallbacks.togglePanel();
- }
/**
- * The {@link StatusBarState} of the status bar.
+ * Returns an ActivityOptions bundle created using the given parameters.
+ *
+ * @param displayId The ID of the display to launch the activity in. Typically this would
+ * be the display the status bar is on.
+ * @param animationAdapter The animation adapter used to start this activity, or {@code null}
+ * for the default animation.
*/
- protected int mState; // TODO: remove this. Just use StatusBarStateController
- protected boolean mBouncerShowing;
-
- private final PhoneStatusBarPolicy mIconPolicy;
-
- private final VolumeComponent mVolumeComponent;
- private BrightnessMirrorController mBrightnessMirrorController;
- private boolean mBrightnessMirrorVisible;
- private BiometricUnlockController mBiometricUnlockController;
- private final LightBarController mLightBarController;
- private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
- private final LockscreenGestureLogger mLockscreenGestureLogger;
- @Nullable
- protected LockscreenWallpaper mLockscreenWallpaper;
- private final AutoHideController mAutoHideController;
-
- private final Point mCurrentDisplaySize = new Point();
-
- protected NotificationShadeWindowView mNotificationShadeWindowView;
- protected PhoneStatusBarView mStatusBarView;
- private PhoneStatusBarViewController mPhoneStatusBarViewController;
- private PhoneStatusBarTransitions mStatusBarTransitions;
- private AuthRippleController mAuthRippleController;
- @WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
- protected final NotificationShadeWindowController mNotificationShadeWindowController;
- private final StatusBarWindowController mStatusBarWindowController;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @VisibleForTesting
- DozeServiceHost mDozeServiceHost;
- private boolean mWakeUpComingFromTouch;
- private PointF mWakeUpTouchLocation;
- private LightRevealScrim mLightRevealScrim;
- private PowerButtonReveal mPowerButtonReveal;
-
- private final Object mQueueLock = new Object();
-
- private final PulseExpansionHandler mPulseExpansionHandler;
- private final NotificationWakeUpCoordinator mWakeUpCoordinator;
- private final KeyguardBypassController mKeyguardBypassController;
- private final KeyguardStateController mKeyguardStateController;
- private final HeadsUpManagerPhone mHeadsUpManager;
- private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
- private final DynamicPrivacyController mDynamicPrivacyController;
- private final FalsingCollector mFalsingCollector;
- private final FalsingManager mFalsingManager;
- private final BroadcastDispatcher mBroadcastDispatcher;
- private final ConfigurationController mConfigurationController;
- protected NotificationShadeWindowViewController mNotificationShadeWindowViewController;
- private final DozeParameters mDozeParameters;
- private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
- private final CentralSurfacesComponent.Factory mCentralSurfacesComponentFactory;
- private final PluginManager mPluginManager;
- private final ShadeController mShadeController;
- private final InitController mInitController;
-
- private final PluginDependencyProvider mPluginDependencyProvider;
- private final KeyguardDismissUtil mKeyguardDismissUtil;
- private final ExtensionController mExtensionController;
- private final UserInfoControllerImpl mUserInfoControllerImpl;
- private final DemoModeController mDemoModeController;
- private final NotificationsController mNotificationsController;
- private final OngoingCallController mOngoingCallController;
- private final StatusBarSignalPolicy mStatusBarSignalPolicy;
- private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
-
- // expanded notifications
- // the sliding/resizing panel within the notification window
- protected NotificationPanelViewController mNotificationPanelViewController;
-
- // settings
- private QSPanelController mQSPanelController;
-
- KeyguardIndicationController mKeyguardIndicationController;
-
- private View mReportRejectedTouch;
-
- private boolean mExpandedVisible;
-
- private final int[] mAbsPos = new int[2];
-
- private final NotifShadeEventSource mNotifShadeEventSource;
- protected final NotificationEntryManager mEntryManager;
- private final NotificationGutsManager mGutsManager;
- private final NotificationLogger mNotificationLogger;
- private final NotificationViewHierarchyManager mViewHierarchyManager;
- private final PanelExpansionStateManager mPanelExpansionStateManager;
- private final KeyguardViewMediator mKeyguardViewMediator;
- protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
- private final BrightnessSliderController.Factory mBrightnessSliderFactory;
- private final FeatureFlags mFeatureFlags;
- private final FragmentService mFragmentService;
- private final ScreenOffAnimationController mScreenOffAnimationController;
- private final WallpaperController mWallpaperController;
- private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
- private final MessageRouter mMessageRouter;
- private final WallpaperManager mWallpaperManager;
-
- private CentralSurfacesComponent mCentralSurfacesComponent;
-
- // Flags for disabling the status bar
- // Two variables becaseu the first one evidently ran out of room for new flags.
- private int mDisabled1 = 0;
- private int mDisabled2 = 0;
-
- /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
- private @Appearance int mAppearance;
-
- private boolean mTransientShown;
-
- private final DisplayMetrics mDisplayMetrics;
-
- // XXX: gesture research
- private final GestureRecorder mGestureRec = DEBUG_GESTURES
- ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
- : null;
-
- private final ScreenPinningRequest mScreenPinningRequest;
-
- private final MetricsLogger mMetricsLogger;
-
- // ensure quick settings is disabled until the current user makes it through the setup wizard
- @VisibleForTesting
- protected boolean mUserSetup = false;
-
- @VisibleForTesting
- public enum StatusBarUiEvent implements UiEventLogger.UiEventEnum {
- @UiEvent(doc = "Secured lockscreen is opened.")
- LOCKSCREEN_OPEN_SECURE(405),
-
- @UiEvent(doc = "Lockscreen without security is opened.")
- LOCKSCREEN_OPEN_INSECURE(406),
-
- @UiEvent(doc = "Secured lockscreen is closed.")
- LOCKSCREEN_CLOSE_SECURE(407),
-
- @UiEvent(doc = "Lockscreen without security is closed.")
- LOCKSCREEN_CLOSE_INSECURE(408),
-
- @UiEvent(doc = "Secured bouncer is opened.")
- BOUNCER_OPEN_SECURE(409),
-
- @UiEvent(doc = "Bouncer without security is opened.")
- BOUNCER_OPEN_INSECURE(410),
-
- @UiEvent(doc = "Secured bouncer is closed.")
- BOUNCER_CLOSE_SECURE(411),
-
- @UiEvent(doc = "Bouncer without security is closed.")
- BOUNCER_CLOSE_INSECURE(412);
-
- private final int mId;
-
- StatusBarUiEvent(int id) {
- mId = id;
- }
-
- @Override
- public int getId() {
- return mId;
- }
+ static Bundle getActivityOptions(int displayId,
+ @Nullable RemoteAnimationAdapter animationAdapter) {
+ ActivityOptions options = getDefaultActivityOptions(animationAdapter);
+ options.setLaunchDisplayId(displayId);
+ options.setCallerDisplayId(displayId);
+ return options.toBundle();
}
- private Handler mMainHandler;
- private final DelayableExecutor mMainExecutor;
-
- private int mInteractingWindows;
- private @TransitionMode int mStatusBarMode;
-
- private final ViewMediatorCallback mKeyguardViewMediatorCallback;
- private final ScrimController mScrimController;
- protected DozeScrimController mDozeScrimController;
- private final Executor mUiBgExecutor;
-
- protected boolean mDozing;
- private boolean mIsFullscreen;
-
- boolean mCloseQsBeforeScreenOff;
-
- private final NotificationMediaManager mMediaManager;
- private final NotificationLockscreenUserManager mLockscreenUserManager;
- private final NotificationRemoteInputManager mRemoteInputManager;
- private boolean mWallpaperSupported;
-
- private Runnable mLaunchTransitionEndRunnable;
- private Runnable mLaunchTransitionCancelRunnable;
- private boolean mLaunchCameraWhenFinishedWaking;
- private boolean mLaunchCameraOnFinishedGoingToSleep;
- private boolean mLaunchEmergencyActionWhenFinishedWaking;
- private boolean mLaunchEmergencyActionOnFinishedGoingToSleep;
- private int mLastCameraLaunchSource;
- protected PowerManager.WakeLock mGestureWakeLock;
-
- private final int[] mTmpInt2 = new int[2];
-
- // Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
- private int mLastLoggedStateFingerprint;
- private boolean mTopHidesStatusBar;
- private boolean mStatusBarWindowHidden;
- private boolean mIsLaunchingActivityOverLockscreen;
-
- private final UserSwitcherController mUserSwitcherController;
- private final NetworkController mNetworkController;
- private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
- protected final BatteryController mBatteryController;
- protected boolean mPanelExpanded;
- private UiModeManager mUiModeManager;
- private LogMaker mStatusBarStateLog;
- protected final NotificationIconAreaController mNotificationIconAreaController;
- @Nullable private View mAmbientIndicationContainer;
- private final SysuiColorExtractor mColorExtractor;
- private final ScreenLifecycle mScreenLifecycle;
- private final WakefulnessLifecycle mWakefulnessLifecycle;
-
- private boolean mNoAnimationOnNextBarModeChange;
- private final SysuiStatusBarStateController mStatusBarStateController;
-
- private final ActivityLaunchAnimator mActivityLaunchAnimator;
- private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
- protected NotificationPresenter mPresenter;
- private NotificationActivityStarter mNotificationActivityStarter;
- private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
- private final Optional<BubblesManager> mBubblesManagerOptional;
- private final Optional<Bubbles> mBubblesOptional;
- private final Bubbles.BubbleExpandListener mBubbleExpandListener;
- private final Optional<StartingSurface> mStartingSurfaceOptional;
- private final NotifPipelineFlags mNotifPipelineFlags;
-
- private final ActivityIntentHelper mActivityIntentHelper;
- private NotificationStackScrollLayoutController mStackScrollerController;
-
- private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
- (extractor, which) -> updateTheme();
-
- private final InteractionJankMonitor mJankMonitor;
-
-
/**
- * Public constructor for CentralSurfaces.
+ * Returns an ActivityOptions bundle created using the given parameters.
*
- * CentralSurfaces is considered optional, and therefore can not be marked as @Inject directly.
- * Instead, an @Provide method is included. See {@link StatusBarPhoneModule}.
+ * @param displayId The ID of the display to launch the activity in. Typically this
+ * would be the
+ * display the status bar is on.
+ * @param animationAdapter The animation adapter used to start this activity, or {@code null}
+ * for the default animation.
+ * @param isKeyguardShowing Whether keyguard is currently showing.
+ * @param eventTime The event time in milliseconds since boot, not including sleep. See
+ * {@link ActivityOptions#setSourceInfo}.
*/
- @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
- public CentralSurfaces(
- Context context,
- NotificationsController notificationsController,
- FragmentService fragmentService,
- LightBarController lightBarController,
- AutoHideController autoHideController,
- StatusBarWindowController statusBarWindowController,
- StatusBarWindowStateController statusBarWindowStateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarSignalPolicy statusBarSignalPolicy,
- PulseExpansionHandler pulseExpansionHandler,
- NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- KeyguardBypassController keyguardBypassController,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManagerPhone,
- DynamicPrivacyController dynamicPrivacyController,
- FalsingManager falsingManager,
- FalsingCollector falsingCollector,
- BroadcastDispatcher broadcastDispatcher,
- NotifShadeEventSource notifShadeEventSource,
- NotificationEntryManager notificationEntryManager,
- NotificationGutsManager notificationGutsManager,
- NotificationLogger notificationLogger,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
- NotificationViewHierarchyManager notificationViewHierarchyManager,
- PanelExpansionStateManager panelExpansionStateManager,
- KeyguardViewMediator keyguardViewMediator,
- DisplayMetrics displayMetrics,
- MetricsLogger metricsLogger,
- @UiBackground Executor uiBgExecutor,
- NotificationMediaManager notificationMediaManager,
- NotificationLockscreenUserManager lockScreenUserManager,
- NotificationRemoteInputManager remoteInputManager,
- UserSwitcherController userSwitcherController,
- NetworkController networkController,
- BatteryController batteryController,
- SysuiColorExtractor colorExtractor,
- ScreenLifecycle screenLifecycle,
- WakefulnessLifecycle wakefulnessLifecycle,
- SysuiStatusBarStateController statusBarStateController,
- Optional<BubblesManager> bubblesManagerOptional,
- Optional<Bubbles> bubblesOptional,
- VisualStabilityManager visualStabilityManager,
- DeviceProvisionedController deviceProvisionedController,
- NavigationBarController navigationBarController,
- AccessibilityFloatingMenuController accessibilityFloatingMenuController,
- Lazy<AssistManager> assistManagerLazy,
- ConfigurationController configurationController,
- NotificationShadeWindowController notificationShadeWindowController,
- DozeParameters dozeParameters,
- ScrimController scrimController,
- Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
- LockscreenGestureLogger lockscreenGestureLogger,
- Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
- DozeServiceHost dozeServiceHost,
- PowerManager powerManager,
- ScreenPinningRequest screenPinningRequest,
- DozeScrimController dozeScrimController,
- VolumeComponent volumeComponent,
- CommandQueue commandQueue,
- CentralSurfacesComponent.Factory centralSurfacesComponentFactory,
- PluginManager pluginManager,
- ShadeController shadeController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- ViewMediatorCallback viewMediatorCallback,
- InitController initController,
- @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
- PluginDependencyProvider pluginDependencyProvider,
- KeyguardDismissUtil keyguardDismissUtil,
- ExtensionController extensionController,
- UserInfoControllerImpl userInfoControllerImpl,
- PhoneStatusBarPolicy phoneStatusBarPolicy,
- KeyguardIndicationController keyguardIndicationController,
- DemoModeController demoModeController,
- Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
- NotificationIconAreaController notificationIconAreaController,
- BrightnessSliderController.Factory brightnessSliderFactory,
- ScreenOffAnimationController screenOffAnimationController,
- WallpaperController wallpaperController,
- OngoingCallController ongoingCallController,
- StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
- LockscreenShadeTransitionController lockscreenShadeTransitionController,
- FeatureFlags featureFlags,
- KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- @Main Handler mainHandler,
- @Main DelayableExecutor delayableExecutor,
- @Main MessageRouter messageRouter,
- WallpaperManager wallpaperManager,
- Optional<StartingSurface> startingSurfaceOptional,
- ActivityLaunchAnimator activityLaunchAnimator,
- NotifPipelineFlags notifPipelineFlags,
- InteractionJankMonitor jankMonitor,
- DeviceStateManager deviceStateManager,
- DreamOverlayStateController dreamOverlayStateController,
- WiredChargingRippleController wiredChargingRippleController) {
- super(context);
- mNotificationsController = notificationsController;
- mFragmentService = fragmentService;
- mLightBarController = lightBarController;
- mAutoHideController = autoHideController;
- mStatusBarWindowController = statusBarWindowController;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mPulseExpansionHandler = pulseExpansionHandler;
- mWakeUpCoordinator = notificationWakeUpCoordinator;
- mKeyguardBypassController = keyguardBypassController;
- mKeyguardStateController = keyguardStateController;
- mHeadsUpManager = headsUpManagerPhone;
- mKeyguardIndicationController = keyguardIndicationController;
- mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
- mDynamicPrivacyController = dynamicPrivacyController;
- mFalsingCollector = falsingCollector;
- mFalsingManager = falsingManager;
- mBroadcastDispatcher = broadcastDispatcher;
- mNotifShadeEventSource = notifShadeEventSource;
- mEntryManager = notificationEntryManager;
- mGutsManager = notificationGutsManager;
- mNotificationLogger = notificationLogger;
- mNotificationInterruptStateProvider = notificationInterruptStateProvider;
- mViewHierarchyManager = notificationViewHierarchyManager;
- mPanelExpansionStateManager = panelExpansionStateManager;
- mKeyguardViewMediator = keyguardViewMediator;
- mDisplayMetrics = displayMetrics;
- mMetricsLogger = metricsLogger;
- mUiBgExecutor = uiBgExecutor;
- mMediaManager = notificationMediaManager;
- mLockscreenUserManager = lockScreenUserManager;
- mRemoteInputManager = remoteInputManager;
- mUserSwitcherController = userSwitcherController;
- mNetworkController = networkController;
- mBatteryController = batteryController;
- mColorExtractor = colorExtractor;
- mScreenLifecycle = screenLifecycle;
- mWakefulnessLifecycle = wakefulnessLifecycle;
- mStatusBarStateController = statusBarStateController;
- mBubblesManagerOptional = bubblesManagerOptional;
- mBubblesOptional = bubblesOptional;
- mVisualStabilityManager = visualStabilityManager;
- mDeviceProvisionedController = deviceProvisionedController;
- mNavigationBarController = navigationBarController;
- mAccessibilityFloatingMenuController = accessibilityFloatingMenuController;
- mAssistManagerLazy = assistManagerLazy;
- mConfigurationController = configurationController;
- mNotificationShadeWindowController = notificationShadeWindowController;
- mDozeServiceHost = dozeServiceHost;
- mPowerManager = powerManager;
- mDozeParameters = dozeParameters;
- mScrimController = scrimController;
- mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
- mLockscreenGestureLogger = lockscreenGestureLogger;
- mScreenPinningRequest = screenPinningRequest;
- mDozeScrimController = dozeScrimController;
- mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
- mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
- mVolumeComponent = volumeComponent;
- mCommandQueue = commandQueue;
- mCentralSurfacesComponentFactory = centralSurfacesComponentFactory;
- mPluginManager = pluginManager;
- mShadeController = shadeController;
- mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
- mKeyguardViewMediatorCallback = viewMediatorCallback;
- mInitController = initController;
- mPluginDependencyProvider = pluginDependencyProvider;
- mKeyguardDismissUtil = keyguardDismissUtil;
- mExtensionController = extensionController;
- mUserInfoControllerImpl = userInfoControllerImpl;
- mIconPolicy = phoneStatusBarPolicy;
- mDemoModeController = demoModeController;
- mNotificationIconAreaController = notificationIconAreaController;
- mBrightnessSliderFactory = brightnessSliderFactory;
- mWallpaperController = wallpaperController;
- mOngoingCallController = ongoingCallController;
- mStatusBarSignalPolicy = statusBarSignalPolicy;
- mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
- mFeatureFlags = featureFlags;
- mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
- mMainHandler = mainHandler;
- mMainExecutor = delayableExecutor;
- mMessageRouter = messageRouter;
- mWallpaperManager = wallpaperManager;
- mJankMonitor = jankMonitor;
- mDreamOverlayStateController = dreamOverlayStateController;
-
- mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
- mStartingSurfaceOptional = startingSurfaceOptional;
- mNotifPipelineFlags = notifPipelineFlags;
- lockscreenShadeTransitionController.setCentralSurfaces(this);
- statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
-
- mScreenOffAnimationController = screenOffAnimationController;
-
- mPanelExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged);
-
- mBubbleExpandListener =
- (isExpanding, key) -> mContext.getMainExecutor().execute(() -> {
- mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
- updateScrimController();
- });
-
- mActivityIntentHelper = new ActivityIntentHelper(mContext);
- mActivityLaunchAnimator = activityLaunchAnimator;
-
- // The status bar background may need updating when the ongoing call status changes.
- mOngoingCallController.addCallback((animate) -> maybeUpdateBarMode());
-
- // TODO(b/190746471): Find a better home for this.
- DateTimeView.setReceiverHandler(timeTickHandler);
-
- mMessageRouter.subscribeTo(KeyboardShortcutsMessage.class,
- data -> toggleKeyboardShortcuts(data.mDeviceId));
- mMessageRouter.subscribeTo(MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU,
- id -> dismissKeyboardShortcuts());
- mMessageRouter.subscribeTo(AnimateExpandSettingsPanelMessage.class,
- data -> mCommandQueueCallbacks.animateExpandSettingsPanel(data.mSubpanel));
- mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT,
- id -> onLaunchTransitionTimeout());
-
- deviceStateManager.registerCallback(mMainExecutor,
- new FoldStateListener(mContext, this::onFoldedStateChanged));
- wiredChargingRippleController.registerCallbacks();
- }
-
- @Override
- public void start() {
- mScreenLifecycle.addObserver(mScreenObserver);
- mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
- mUiModeManager = mContext.getSystemService(UiModeManager.class);
- if (mBubblesOptional.isPresent()) {
- mBubblesOptional.get().setExpandListener(mBubbleExpandListener);
- }
-
- mStatusBarSignalPolicy.init();
- mKeyguardIndicationController.init();
-
- mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
- mStatusBarStateController.addCallback(mStateListener,
- SysuiStatusBarStateController.RANK_STATUS_BAR);
-
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mDreamManager = IDreamManager.Stub.asInterface(
- ServiceManager.checkService(DreamService.DREAM_SERVICE));
-
- mDisplay = mContext.getDisplay();
- mDisplayId = mDisplay.getDisplayId();
- updateDisplaySize();
- mStatusBarHideIconsForBouncerManager.setDisplayId(mDisplayId);
-
- // start old BaseStatusBar.start().
- mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
- mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
-
- mAccessibilityManager = (AccessibilityManager)
- mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-
- mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
- mBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-
- mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mWallpaperSupported = mWallpaperManager.isWallpaperSupported();
-
- RegisterStatusBarResult result = null;
- try {
- result = mBarService.registerStatusBar(mCommandQueue);
- } catch (RemoteException ex) {
- ex.rethrowFromSystemServer();
- }
-
- createAndAddWindows(result);
-
- if (mWallpaperSupported) {
- // Make sure we always have the most current wallpaper info.
- IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
- mBroadcastDispatcher.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter,
- null /* handler */, UserHandle.ALL);
- mWallpaperChangedReceiver.onReceive(mContext, null);
- } else if (DEBUG) {
- Log.v(TAG, "start(): no wallpaper service ");
- }
-
- // Set up the initial notification state. This needs to happen before CommandQueue.disable()
- setUpPresenter();
-
- if (containsType(result.mTransientBarTypes, ITYPE_STATUS_BAR)) {
- showTransientUnchecked();
- }
- mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
- result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
- result.mRequestedVisibilities, result.mPackageName);
-
- // StatusBarManagerService has a back up of IME token and it's restored here.
- mCommandQueueCallbacks.setImeWindowStatus(mDisplayId, result.mImeToken,
- result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher);
-
- // Set up the initial icon state
- int numIcons = result.mIcons.size();
- for (int i = 0; i < numIcons; i++) {
- mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i));
- }
-
- if (DEBUG) {
- Log.d(TAG, String.format(
- "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x",
- numIcons,
- result.mDisabledFlags1,
- result.mAppearance,
- result.mImeWindowVis));
- }
-
- IntentFilter internalFilter = new IntentFilter();
- internalFilter.addAction(BANNER_ACTION_CANCEL);
- internalFilter.addAction(BANNER_ACTION_SETUP);
- mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
- null, Context.RECEIVER_EXPORTED_UNAUDITED);
-
- if (mWallpaperSupported) {
- IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
- ServiceManager.getService(Context.WALLPAPER_SERVICE));
- try {
- wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */);
- } catch (RemoteException e) {
- // Just pass, nothing critical.
- }
- }
-
- // end old BaseStatusBar.start().
-
- // Lastly, call to the icon policy to install/update all the icons.
- mIconPolicy.init();
-
- mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
- @Override
- public void onUnlockedChanged() {
- logStateToEventlog();
- }
- });
- startKeyguard();
-
- mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
- mDozeServiceHost.initialize(
- this,
- mStatusBarKeyguardViewManager,
- mNotificationShadeWindowViewController,
- mNotificationPanelViewController,
- mAmbientIndicationContainer);
- updateLightRevealScrimVisibility();
-
- mConfigurationController.addCallback(mConfigurationListener);
-
- mBatteryController.observe(mLifecycle, mBatteryStateChangeCallback);
- mLifecycle.setCurrentState(RESUMED);
-
- mAccessibilityFloatingMenuController.init();
-
- // set the initial view visibility
- int disabledFlags1 = result.mDisabledFlags1;
- int disabledFlags2 = result.mDisabledFlags2;
- mInitController.addPostInitTask(
- () -> setUpDisableFlags(disabledFlags1, disabledFlags2));
-
- mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener);
-
- mPluginManager.addPluginListener(
- new PluginListener<OverlayPlugin>() {
- private final ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
-
- @Override
- public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
- mMainExecutor.execute(
- () -> plugin.setup(getNotificationShadeWindowView(),
- getNavigationBarView(),
- new Callback(plugin), mDozeParameters));
- }
-
- @Override
- public void onPluginDisconnected(OverlayPlugin plugin) {
- mMainExecutor.execute(() -> {
- mOverlays.remove(plugin);
- mNotificationShadeWindowController
- .setForcePluginOpen(mOverlays.size() != 0, this);
- });
- }
-
- class Callback implements OverlayPlugin.Callback {
- private final OverlayPlugin mPlugin;
-
- Callback(OverlayPlugin plugin) {
- mPlugin = plugin;
- }
-
- @Override
- public void onHoldStatusBarOpenChange() {
- if (mPlugin.holdStatusBarOpen()) {
- mOverlays.add(mPlugin);
- } else {
- mOverlays.remove(mPlugin);
- }
- mMainExecutor.execute(() -> {
- mNotificationShadeWindowController
- .setStateListener(b -> mOverlays.forEach(
- o -> o.setCollapseDesired(b)));
- mNotificationShadeWindowController
- .setForcePluginOpen(mOverlays.size() != 0, this);
- });
- }
- }
- }, OverlayPlugin.class, true /* Allow multiple plugins */);
-
- mStartingSurfaceOptional.ifPresent(startingSurface -> startingSurface.setSysuiProxy(
- (requestTopUi, componentTag) -> mMainExecutor.execute(() ->
- mNotificationShadeWindowController.setRequestTopUi(
- requestTopUi, componentTag))));
- }
-
- private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) {
- Trace.beginSection("CentralSurfaces#onFoldedStateChanged");
- onFoldedStateChangedInternal(isFolded, willGoToSleep);
- Trace.endSection();
- }
-
- private void onFoldedStateChangedInternal(boolean isFolded, boolean willGoToSleep) {
- // Folded state changes are followed by a screen off event.
- // By default turning off the screen also closes the shade.
- // We want to make sure that the shade status is kept after
- // folding/unfolding.
- boolean isShadeOpen = mShadeController.isShadeOpen();
- boolean leaveOpen = isShadeOpen && !willGoToSleep;
- if (DEBUG) {
- Log.d(TAG, String.format(
- "#onFoldedStateChanged(): "
- + "isFolded=%s, "
- + "willGoToSleep=%s, "
- + "isShadeOpen=%s, "
- + "leaveOpen=%s",
- isFolded, willGoToSleep, isShadeOpen, leaveOpen));
- }
- if (leaveOpen) {
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
- if (mKeyguardStateController.isShowing()) {
- // When device state changes on keyguard we don't want to keep the state of
- // the shade and instead we open clean state of keyguard with shade closed.
- // Normally some parts of QS state (like expanded/collapsed) are persisted and
- // that causes incorrect UI rendering, especially when changing state with QS
- // expanded. To prevent that we can close QS which resets QS and some parts of
- // the shade to its default state. Read more in b/201537421
- mCloseQsBeforeScreenOff = true;
- }
- }
+ static Bundle getActivityOptions(int displayId,
+ @Nullable RemoteAnimationAdapter animationAdapter, boolean isKeyguardShowing,
+ long eventTime) {
+ ActivityOptions options = getDefaultActivityOptions(animationAdapter);
+ options.setSourceInfo(isKeyguardShowing ? ActivityOptions.SourceInfo.TYPE_LOCKSCREEN
+ : ActivityOptions.SourceInfo.TYPE_NOTIFICATION, eventTime);
+ options.setLaunchDisplayId(displayId);
+ options.setCallerDisplayId(displayId);
+ return options.toBundle();
}
- // ================================================================================
- // Constructing the view
- // ================================================================================
- protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
- updateDisplaySize(); // populates mDisplayMetrics
- updateResources();
- updateTheme();
-
- inflateStatusBarWindow();
- mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener());
- mWallpaperController.setRootView(mNotificationShadeWindowView);
-
- // TODO: Deal with the ugliness that comes from having some of the status bar broken out
- // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
- mNotificationLogger.setUpWithContainer(mNotifListContainer);
- mNotificationIconAreaController.setupShelf(mNotificationShelfController);
- mPanelExpansionStateManager.addExpansionListener(mWakeUpCoordinator);
- mUserSwitcherController.init(mNotificationShadeWindowView);
-
- // Allow plugins to reference DarkIconDispatcher and StatusBarStateController
- mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
- mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
-
- // Set up CollapsedStatusBarFragment and PhoneStatusBarView
- StatusBarInitializer initializer = mCentralSurfacesComponent.getStatusBarInitializer();
- initializer.setStatusBarViewUpdatedListener(
- (statusBarView, statusBarViewController, statusBarTransitions) -> {
- mStatusBarView = statusBarView;
- mPhoneStatusBarViewController = statusBarViewController;
- mStatusBarTransitions = statusBarTransitions;
- mNotificationShadeWindowViewController
- .setStatusBarViewController(mPhoneStatusBarViewController);
- // Ensure we re-propagate panel expansion values to the panel controller and
- // any listeners it may have, such as PanelBar. This will also ensure we
- // re-display the notification panel if necessary (for example, if
- // a heads-up notification was being displayed and should continue being
- // displayed).
- mNotificationPanelViewController.updatePanelExpansionAndVisibility();
- setBouncerShowingForStatusBarComponents(mBouncerShowing);
- checkBarModes();
- });
- initializer.initializeStatusBar(mCentralSurfacesComponent);
-
- mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
- mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
- if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
- mHeadsUpManager.addListener(mVisualStabilityManager);
- }
- mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
-
- createNavigationBar(result);
-
- if (ENABLE_LOCKSCREEN_WALLPAPER && mWallpaperSupported) {
- mLockscreenWallpaper = mLockscreenWallpaperLazy.get();
- }
-
- mNotificationPanelViewController.setKeyguardIndicationController(
- mKeyguardIndicationController);
-
- mAmbientIndicationContainer = mNotificationShadeWindowView.findViewById(
- R.id.ambient_indication_container);
-
- mAutoHideController.setStatusBar(new AutoHideUiElement() {
- @Override
- public void synchronizeState() {
- checkBarModes();
- }
-
- @Override
- public boolean shouldHideOnTouch() {
- return !mRemoteInputManager.isRemoteInputActive();
- }
-
- @Override
- public boolean isVisible() {
- return isTransientShown();
- }
-
- @Override
- public void hide() {
- clearTransient();
- }
- });
-
- ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind);
- ScrimView notificationsScrim = mNotificationShadeWindowView
- .findViewById(R.id.scrim_notifications);
- ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front);
-
- mScrimController.setScrimVisibleListener(scrimsVisible -> {
- mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);
- });
- mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
-
- mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
- mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> {
- Runnable updateOpaqueness = () -> {
- mNotificationShadeWindowController.setLightRevealScrimOpaque(
- mLightRevealScrim.isScrimOpaque());
- mScreenOffAnimationController
- .onScrimOpaqueChanged(mLightRevealScrim.isScrimOpaque());
- };
- if (opaque) {
- // Delay making the view opaque for a frame, because it needs some time to render
- // otherwise this can lead to a flicker where the scrim doesn't cover the screen
- mLightRevealScrim.post(updateOpaqueness);
+ static ActivityOptions getDefaultActivityOptions(
+ @Nullable RemoteAnimationAdapter animationAdapter) {
+ ActivityOptions options;
+ if (animationAdapter != null) {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ options = ActivityOptions.makeRemoteTransition(
+ RemoteTransitionAdapter.adaptRemoteAnimation(animationAdapter));
} else {
- updateOpaqueness.run();
+ options = ActivityOptions.makeRemoteAnimation(animationAdapter);
}
- });
-
- mScreenOffAnimationController.initialize(this, mLightRevealScrim);
- updateLightRevealScrimVisibility();
-
- mNotificationPanelViewController.initDependencies(
- this,
- this::makeExpandedInvisible,
- mNotificationShelfController);
-
- BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop);
- mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
- backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
- float maxWallpaperZoom = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_wallpaperMaxScale);
- mNotificationShadeDepthControllerLazy.get().addListener(depth -> {
- float scale = MathUtils.lerp(maxWallpaperZoom, 1f, depth);
- backdrop.setPivotX(backdrop.getWidth() / 2f);
- backdrop.setPivotY(backdrop.getHeight() / 2f);
- backdrop.setScaleX(scale);
- backdrop.setScaleY(scale);
- });
-
- mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
-
- // Set up the quick settings tile panel
- final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
- if (container != null) {
- FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
- ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
- mExtensionController
- .newExtension(QS.class)
- .withPlugin(QS.class)
- .withDefault(this::createDefaultQSFragment)
- .build());
- mBrightnessMirrorController = new BrightnessMirrorController(
- mNotificationShadeWindowView,
- mNotificationPanelViewController,
- mNotificationShadeDepthControllerLazy.get(),
- mBrightnessSliderFactory,
- (visible) -> {
- mBrightnessMirrorVisible = visible;
- updateScrimController();
- });
- fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
- QS qs = (QS) f;
- if (qs instanceof QSFragment) {
- mQSPanelController = ((QSFragment) qs).getQSPanelController();
- ((QSFragment) qs).setBrightnessMirrorController(mBrightnessMirrorController);
- }
- });
- }
-
- mReportRejectedTouch = mNotificationShadeWindowView
- .findViewById(R.id.report_rejected_touch);
- if (mReportRejectedTouch != null) {
- updateReportRejectedTouchVisibility();
- mReportRejectedTouch.setOnClickListener(v -> {
- Uri session = mFalsingManager.reportRejectedTouch();
- if (session == null) { return; }
-
- StringWriter message = new StringWriter();
- message.write("Build info: ");
- message.write(SystemProperties.get("ro.build.description"));
- message.write("\nSerial number: ");
- message.write(SystemProperties.get("ro.serialno"));
- message.write("\n");
-
- startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND)
- .setType("*/*")
- .putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report")
- .putExtra(Intent.EXTRA_STREAM, session)
- .putExtra(Intent.EXTRA_TEXT, message.toString()),
- "Share rejected touch report")
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
- true /* onlyProvisioned */, true /* dismissShade */);
- });
- }
-
- if (!mPowerManager.isInteractive()) {
- mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
- }
- mGestureWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
- "sysui:GestureWakeLock");
-
- // receive broadcasts
- registerBroadcastReceiver();
-
- IntentFilter demoFilter = new IntentFilter();
- if (DEBUG_MEDIA_FAKE_ARTWORK) {
- demoFilter.addAction(ACTION_FAKE_ARTWORK);
+ } else {
+ options = ActivityOptions.makeBasic();
}
- mContext.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
- android.Manifest.permission.DUMP, null,
- Context.RECEIVER_EXPORTED_UNAUDITED);
-
- // listen for USER_SETUP_COMPLETE setting (per-user)
- mDeviceProvisionedController.addCallback(mUserSetupObserver);
- mUserSetupObserver.onUserSetupChanged();
-
- // disable profiling bars, since they overlap and clutter the output on app windows
- ThreadedRenderer.overrideProperty("disableProfileBars", "true");
-
- // Private API call to make the shadows look better for Recents
- ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
+ options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR);
+ return options;
}
-
/**
- * When swiping up to dismiss the lock screen, the panel expansion fraction goes from 1f to 0f.
- * This results in the clock/notifications/other content disappearing off the top of the screen.
- *
- * We also use the expansion fraction to animate in the app/launcher surface from the bottom of
- * the screen, 'pushing' off the notifications and other content. To do this, we dispatch the
- * expansion fraction to the KeyguardViewMediator if we're in the process of dismissing the
- * keyguard.
+ * @return a PackageManager for userId or if userId is < 0 (USER_ALL etc) then
+ * return PackageManager for mContext
*/
- private void dispatchPanelExpansionForKeyguardDismiss(float fraction, boolean trackingTouch) {
- // Things that mean we're not swiping to dismiss the keyguard, and should ignore this
- // expansion:
- // - Keyguard isn't even visible.
- // - Keyguard is occluded. Expansion changes here are the shade being expanded over the
- // occluding activity.
- // - Keyguard is visible, but can't be dismissed (swiping up will show PIN/password prompt).
- // - The SIM is locked, you can't swipe to unlock. If the SIM is locked but there is no
- // device lock set, canDismissLockScreen returns true even though you should not be able
- // to dismiss the lock screen until entering the SIM PIN.
- // - QS is expanded and we're swiping - swiping up now will hide QS, not dismiss the
- // keyguard.
- if (!isKeyguardShowing()
- || isOccluded()
- || !mKeyguardStateController.canDismissLockScreen()
- || mKeyguardViewMediator.isAnySimPinSecure()
- || (mNotificationPanelViewController.isQsExpanded() && trackingTouch)) {
- return;
- }
-
- // Otherwise, we should let the keyguard know about this if we're tracking touch, or if we
- // are already animating the keyguard dismiss (since we will need to either finish or cancel
- // the animation).
- if (trackingTouch
- || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()
- || mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
- mKeyguardStateController.notifyKeyguardDismissAmountChanged(
- 1f - fraction, trackingTouch);
- }
- }
-
- private void onPanelExpansionChanged(PanelExpansionChangeEvent event) {
- float fraction = event.getFraction();
- boolean tracking = event.getTracking();
- dispatchPanelExpansionForKeyguardDismiss(fraction, tracking);
-
- if (fraction == 0 || fraction == 1) {
- if (getNavigationBarView() != null) {
- getNavigationBarView().onStatusBarPanelStateChanged();
- }
- if (getNotificationPanelViewController() != null) {
- getNotificationPanelViewController().updateSystemUiStateFlags();
+ static PackageManager getPackageManagerForUser(Context context, int userId) {
+ Context contextForUser = context;
+ // UserHandle defines special userId as negative values, e.g. USER_ALL
+ if (userId >= 0) {
+ try {
+ // Create a context for the correct user so if a package isn't installed
+ // for user 0 we can still load information about the package.
+ contextForUser =
+ context.createPackageContextAsUser(context.getPackageName(),
+ Context.CONTEXT_RESTRICTED,
+ new UserHandle(userId));
+ } catch (PackageManager.NameNotFoundException e) {
+ // Shouldn't fail to find the package name for system ui.
}
}
+ return contextForUser.getPackageManager();
}
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycle;
- }
-
- @VisibleForTesting
- protected void registerBroadcastReceiver() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, null, UserHandle.ALL);
- }
+ void animateExpandNotificationsPanel();
- protected QS createDefaultQSFragment() {
- return FragmentHostManager.get(mNotificationShadeWindowView).create(QSFragment.class);
- }
+ void animateExpandSettingsPanel(@Nullable String subpanel);
- private void setUpPresenter() {
- // Set up the initial notification state.
- mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback);
- mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
- mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
- mNotificationShadeWindowViewController,
- mNotifListContainer,
- mHeadsUpManager,
- mJankMonitor);
- mNotificationShelfController.setOnActivatedListener(mPresenter);
- mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
- mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter);
- mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
- mNotificationsController.initialize(
- mPresenter,
- mNotifListContainer,
- mStackScrollerController.getNotifStackController(),
- mNotificationActivityStarter,
- mCentralSurfacesComponent.getBindRowCallback());
- }
+ void animateCollapsePanels(int flags, boolean force);
- /**
- * Post-init task of {@link #start()}
- * @param state1 disable1 flags
- * @param state2 disable2 flags
- */
- protected void setUpDisableFlags(int state1, int state2) {
- mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */);
- }
+ void collapsePanelOnMainThread();
- /**
- * Ask the display to wake up if currently dozing, else do nothing
- *
- * @param time when to wake up
- * @param where the view requesting the wakeup
- * @param why the reason for the wake up
- */
- public void wakeUpIfDozing(long time, View where, String why) {
- if (mDozing && mScreenOffAnimationController.allowWakeUpIfDozing()) {
- mPowerManager.wakeUp(
- time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
- mWakeUpComingFromTouch = true;
- where.getLocationInWindow(mTmpInt2);
-
- // NOTE, the incoming view can sometimes be the entire container... unsure if
- // this location is valuable enough
- mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
- mTmpInt2[1] + where.getHeight() / 2);
- mFalsingCollector.onScreenOnFromTouch();
- }
- }
+ void collapsePanelWithDuration(int duration);
- // TODO(b/117478341): This was left such that CarStatusBar can override this method.
- // Try to remove this.
- protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
- mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */, result);
- }
+ void togglePanel();
- /**
- * Returns the {@link android.view.View.OnTouchListener} that will be invoked when the
- * background window of the status bar is clicked.
- */
- protected View.OnTouchListener getStatusBarWindowTouchListener() {
- return (v, event) -> {
- mAutoHideController.checkUserAutoHide(event);
- mRemoteInputManager.checkRemoteInputOutside(event);
- if (event.getAction() == MotionEvent.ACTION_UP) {
- if (mExpandedVisible) {
- mShadeController.animateCollapsePanels();
- }
- }
- return mNotificationShadeWindowView.onTouchEvent(event);
- };
- }
+ void start();
- private void inflateStatusBarWindow() {
- if (mCentralSurfacesComponent != null) {
- // Tear down
- for (CentralSurfacesComponent.Startable s : mCentralSurfacesComponent.getStartables()) {
- s.stop();
- }
- }
- mCentralSurfacesComponent = mCentralSurfacesComponentFactory.create();
- mFragmentService.addFragmentInstantiationProvider(mCentralSurfacesComponent);
-
- mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView();
- mNotificationShadeWindowViewController = mCentralSurfacesComponent
- .getNotificationShadeWindowViewController();
- mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
- mNotificationShadeWindowViewController.setupExpandedStatusBar();
- mNotificationPanelViewController =
- mCentralSurfacesComponent.getNotificationPanelViewController();
- mCentralSurfacesComponent.getLockIconViewController().init();
- mStackScrollerController =
- mCentralSurfacesComponent.getNotificationStackScrollLayoutController();
- mStackScroller = mStackScrollerController.getView();
- mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer();
- mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
- mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
- mNotificationShelfController = mCentralSurfacesComponent.getNotificationShelfController();
- mAuthRippleController = mCentralSurfacesComponent.getAuthRippleController();
- mAuthRippleController.init();
-
- mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener());
-
- // Listen for demo mode changes
- mDemoModeController.addCallback(mDemoModeCallback);
-
- if (mCommandQueueCallbacks != null) {
- mCommandQueue.removeCallback(mCommandQueueCallbacks);
- }
- mCommandQueueCallbacks =
- mCentralSurfacesComponent.getCentralSurfacesCommandQueueCallbacks();
- // Connect in to the status bar manager service
- mCommandQueue.addCallback(mCommandQueueCallbacks);
-
- // Perform all other initialization for CentralSurfacesScope
- for (CentralSurfacesComponent.Startable s : mCentralSurfacesComponent.getStartables()) {
- s.start();
- }
- }
+ boolean updateIsKeyguard();
- protected void startKeyguard() {
- Trace.beginSection("CentralSurfaces#startKeyguard");
- mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
- mBiometricUnlockController.setBiometricModeListener(
- new BiometricUnlockController.BiometricModeListener() {
- @Override
- public void onResetMode() {
- setWakeAndUnlocking(false);
- }
-
- @Override
- public void onModeChanged(int mode) {
- switch (mode) {
- case BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM:
- case BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING:
- case BiometricUnlockController.MODE_WAKE_AND_UNLOCK:
- setWakeAndUnlocking(true);
- }
- }
-
- @Override
- public void notifyBiometricAuthModeChanged() {
- CentralSurfaces.this.notifyBiometricAuthModeChanged();
- }
-
- private void setWakeAndUnlocking(boolean wakeAndUnlocking) {
- if (getNavigationBarView() != null) {
- getNavigationBarView().setWakeAndUnlocking(wakeAndUnlocking);
- }
- }
- });
- mStatusBarKeyguardViewManager.registerCentralSurfaces(
- /* statusBar= */ this,
- mNotificationPanelViewController,
- mPanelExpansionStateManager,
- mBiometricUnlockController,
- mStackScroller,
- mKeyguardBypassController);
- mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
- mKeyguardIndicationController
- .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
- mRemoteInputManager.addControllerCallback(mStatusBarKeyguardViewManager);
- mDynamicPrivacyController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
-
- mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
- mMediaManager.setBiometricUnlockController(mBiometricUnlockController);
- mKeyguardDismissUtil.setDismissHandler(this::executeWhenUnlocked);
- Trace.endSection();
- }
+ boolean updateIsKeyguard(boolean forceStateChange);
- public NotificationShadeWindowView getNotificationShadeWindowView() {
- return mNotificationShadeWindowView;
- }
+ @NonNull
+ @Override
+ Lifecycle getLifecycle();
- public NotificationShadeWindowViewController getNotificationShadeWindowViewController() {
- return mNotificationShadeWindowViewController;
- }
+ void wakeUpIfDozing(long time, View where, String why);
- public NotificationPanelViewController getNotificationPanelViewController() {
- return mNotificationPanelViewController;
- }
+ NotificationShadeWindowView getNotificationShadeWindowView();
- public ViewGroup getBouncerContainer() {
- return mNotificationShadeWindowViewController.getBouncerContainer();
- }
+ NotificationShadeWindowViewController getNotificationShadeWindowViewController();
- public int getStatusBarHeight() {
- return mStatusBarWindowController.getStatusBarHeight();
- }
+ NotificationPanelViewController getNotificationPanelViewController();
- /**
- * Disable QS if device not provisioned.
- * If the user switcher is simple then disable QS during setup because
- * the user intends to use the lock screen user switcher, QS in not needed.
- */
- void updateQsExpansionEnabled() {
- final boolean expandEnabled = mDeviceProvisionedController.isDeviceProvisioned()
- && (mUserSetup || mUserSwitcherController == null
- || !mUserSwitcherController.isSimpleUserSwitcher())
- && !isShadeDisabled()
- && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
- && !mDozing
- && !ONLY_CORE_APPS;
- mNotificationPanelViewController.setQsExpansionEnabledPolicy(expandEnabled);
- Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
- }
+ ViewGroup getBouncerContainer();
- public boolean isShadeDisabled() {
- return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
- }
+ int getStatusBarHeight();
- /**
- * Request a notification update
- * @param reason why we're requesting a notification update
- */
- public void requestNotificationUpdate(String reason) {
- mNotificationsController.requestNotificationUpdate(reason);
- }
+ void updateQsExpansionEnabled();
- /**
- * Asks {@link KeyguardUpdateMonitor} to run face auth.
- */
- public void requestFaceAuth(boolean userInitiatedRequest) {
- if (!mKeyguardStateController.canDismissLockScreen()) {
- mKeyguardUpdateMonitor.requestFaceAuth(userInitiatedRequest);
- }
- }
+ boolean isShadeDisabled();
- private void updateReportRejectedTouchVisibility() {
- if (mReportRejectedTouch == null) {
- return;
- }
- mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD && !mDozing
- && mFalsingCollector.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE);
- }
+ void requestNotificationUpdate(String reason);
- boolean areNotificationAlertsDisabled() {
- return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
- }
+ void requestFaceAuth(boolean userInitiatedRequest);
@Override
- public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
- int flags) {
- startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, flags);
- }
+ void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
+ int flags);
@Override
- public void startActivity(Intent intent, boolean dismissShade) {
- startActivityDismissingKeyguard(intent, false /* onlyProvisioned */, dismissShade);
- }
+ void startActivity(Intent intent, boolean dismissShade);
@Override
- public void startActivity(Intent intent, boolean dismissShade,
+ void startActivity(Intent intent, boolean dismissShade,
@Nullable ActivityLaunchAnimator.Controller animationController,
- boolean showOverLockscreenWhenLocked) {
- startActivity(intent, dismissShade, animationController, showOverLockscreenWhenLocked,
- getActivityUserHandle(intent));
- }
+ boolean showOverLockscreenWhenLocked);
@Override
- public void startActivity(Intent intent, boolean dismissShade,
+ void startActivity(Intent intent, boolean dismissShade,
@Nullable ActivityLaunchAnimator.Controller animationController,
- boolean showOverLockscreenWhenLocked, UserHandle userHandle) {
- // Make sure that we dismiss the keyguard if it is directly dismissable or when we don't
- // want to show the activity above it.
- if (mKeyguardStateController.isUnlocked() || !showOverLockscreenWhenLocked) {
- startActivityDismissingKeyguard(intent, false, dismissShade,
- false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
- 0 /* flags */, animationController, userHandle);
- return;
- }
-
- boolean animate =
- animationController != null && shouldAnimateLaunch(true /* isActivityIntent */,
- showOverLockscreenWhenLocked);
-
- ActivityLaunchAnimator.Controller controller = null;
- if (animate) {
- // Wrap the animation controller to dismiss the shade and set
- // mIsLaunchingActivityOverLockscreen during the animation.
- ActivityLaunchAnimator.Controller delegate = wrapAnimationController(
- animationController, dismissShade);
- controller = new DelegateLaunchAnimatorController(delegate) {
- @Override
- public void onIntentStarted(boolean willAnimate) {
- getDelegate().onIntentStarted(willAnimate);
-
- if (willAnimate) {
- CentralSurfaces.this.mIsLaunchingActivityOverLockscreen = true;
- }
- }
-
- @Override
- public void onLaunchAnimationStart(boolean isExpandingFullyAbove) {
- super.onLaunchAnimationStart(isExpandingFullyAbove);
-
- // Double check that the keyguard is still showing and not going away, but if so
- // set the keyguard occluded. Typically, WM will let KeyguardViewMediator know
- // directly, but we're overriding that to play the custom launch animation, so
- // we need to take care of that here. The unocclude animation is not overridden,
- // so WM will call KeyguardViewMediator's unocclude animation runner when the
- // activity is exited.
- if (mKeyguardStateController.isShowing()
- && !mKeyguardStateController.isKeyguardGoingAway()) {
- mKeyguardViewMediator.setOccluded(true /* isOccluded */,
- true /* animate */);
- }
- }
-
- @Override
- public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
- // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the
- // animation so that we can assume that mIsLaunchingActivityOverLockscreen
- // being true means that we will collapse the shade (or at least run the
- // post collapse runnables) later on.
- CentralSurfaces.this.mIsLaunchingActivityOverLockscreen = false;
- getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove);
- }
-
- @Override
- public void onLaunchAnimationCancelled() {
- // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the
- // animation so that we can assume that mIsLaunchingActivityOverLockscreen
- // being true means that we will collapse the shade (or at least run the
- // post collapse runnables) later on.
- CentralSurfaces.this.mIsLaunchingActivityOverLockscreen = false;
- getDelegate().onLaunchAnimationCancelled();
- }
- };
- } else if (dismissShade) {
- // The animation will take care of dismissing the shade at the end of the animation. If
- // we don't animate, collapse it directly.
- collapseShade();
- }
+ boolean showOverLockscreenWhenLocked, UserHandle userHandle);
- mActivityLaunchAnimator.startIntentWithAnimation(controller, animate,
- intent.getPackage(), showOverLockscreenWhenLocked, (adapter) -> TaskStackBuilder
- .create(mContext)
- .addNextIntent(intent)
- .startActivities(getActivityOptions(getDisplayId(), adapter),
- userHandle));
- }
-
- /**
- * Whether we are currently animating an activity launch above the lockscreen (occluding
- * activity).
- */
- public boolean isLaunchingActivityOverLockscreen() {
- return mIsLaunchingActivityOverLockscreen;
- }
+ boolean isLaunchingActivityOverLockscreen();
@Override
- public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
- startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade);
- }
+ void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
@Override
- public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
- startActivityDismissingKeyguard(intent, false, dismissShade,
- false /* disallowEnterPictureInPictureWhileLaunching */, callback, 0,
- null /* animationController */, getActivityUserHandle(intent));
- }
+ void startActivity(Intent intent, boolean dismissShade, Callback callback);
- public void setQsExpanded(boolean expanded) {
- mNotificationShadeWindowController.setQsExpanded(expanded);
- mNotificationPanelViewController.setStatusAccessibilityImportance(expanded
- ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- mNotificationPanelViewController.updateSystemUiStateFlags();
- if (getNavigationBarView() != null) {
- getNavigationBarView().onStatusBarPanelStateChanged();
- }
- }
+ void setQsExpanded(boolean expanded);
- public boolean isWakeUpComingFromTouch() {
- return mWakeUpComingFromTouch;
- }
+ boolean isWakeUpComingFromTouch();
- public boolean isFalsingThresholdNeeded() {
- return true;
- }
+ boolean isFalsingThresholdNeeded();
- /**
- * To be called when there's a state change in StatusBarKeyguardViewManager.
- */
- public void onKeyguardViewManagerStatesUpdated() {
- logStateToEventlog();
- }
+ void onKeyguardViewManagerStatesUpdated();
- 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();
- }
+ void setPanelExpanded(boolean isExpanded);
- if (!isExpanded) {
- mRemoteInputManager.onPanelCollapsed();
- }
- }
+ ViewGroup getNotificationScrollLayout();
- public ViewGroup getNotificationScrollLayout() {
- return mStackScroller;
- }
-
- public boolean isPulsing() {
- return mDozeServiceHost.isPulsing();
- }
+ boolean isPulsing();
@Nullable
- public View getAmbientIndicationContainer() {
- return mAmbientIndicationContainer;
- }
+ View getAmbientIndicationContainer();
- /**
- * When the keyguard is showing and covered by a "showWhenLocked" activity it
- * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager}
- *
- * @return whether the keyguard is currently occluded
- */
- public boolean isOccluded() {
- return mKeyguardStateController.isOccluded();
- }
+ boolean isOccluded();
- /** A launch animation was cancelled. */
//TODO: These can / should probably be moved to NotificationPresenter or ShadeController
- public void onLaunchAnimationCancelled(boolean isLaunchForActivity) {
- if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
- && isLaunchForActivity) {
- onClosingFinished();
- } else {
- mShadeController.collapsePanel(true /* animate */);
- }
- }
+ void onLaunchAnimationCancelled(boolean isLaunchForActivity);
- /** A launch animation ended. */
- public void onLaunchAnimationEnd(boolean launchIsFullScreen) {
- if (!mPresenter.isCollapsing()) {
- onClosingFinished();
- }
- if (launchIsFullScreen) {
- instantCollapseNotificationPanel();
- }
- }
+ void onLaunchAnimationEnd(boolean launchIsFullScreen);
- /**
- * Whether we should animate an activity launch.
- *
- * Note: This method must be called *before* dismissing the keyguard.
- */
- public boolean shouldAnimateLaunch(boolean isActivityIntent, boolean showOverLockscreen) {
- // TODO(b/184121838): Support launch animations when occluded.
- if (isOccluded()) {
- return false;
- }
+ boolean shouldAnimateLaunch(boolean isActivityIntent, boolean showOverLockscreen);
- // Always animate if we are not showing the keyguard or if we animate over the lockscreen
- // (without unlocking it).
- if (showOverLockscreen || !mKeyguardStateController.isShowing()) {
- return true;
- }
+ boolean shouldAnimateLaunch(boolean isActivityIntent);
- // If we are locked and have to dismiss the keyguard, only animate if remote unlock
- // animations are enabled. We also don't animate non-activity launches as they can break the
- // animation.
- // TODO(b/184121838): Support non activity launches on the lockscreen.
- return isActivityIntent && KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation;
- }
+ boolean isDeviceInVrMode();
- /** Whether we should animate an activity launch. */
- public boolean shouldAnimateLaunch(boolean isActivityIntent) {
- return shouldAnimateLaunch(isActivityIntent, false /* showOverLockscreen */);
- }
+ NotificationPresenter getPresenter();
- public boolean isDeviceInVrMode() {
- return mPresenter.isDeviceInVrMode();
- }
+ void postAnimateCollapsePanels();
- public NotificationPresenter getPresenter() {
- return mPresenter;
- }
+ void postAnimateForceCollapsePanels();
- @VisibleForTesting
- void setBarStateForTest(int state) {
- mState = state;
- }
+ void postAnimateOpenPanels();
- static class KeyboardShortcutsMessage {
- final int mDeviceId;
+ boolean isExpandedVisible();
- KeyboardShortcutsMessage(int deviceId) {
- mDeviceId = deviceId;
- }
- }
-
- static class AnimateExpandSettingsPanelMessage {
- final String mSubpanel;
-
- AnimateExpandSettingsPanelMessage(String subpanel) {
- mSubpanel = subpanel;
- }
- }
-
- private void maybeEscalateHeadsUp() {
- mHeadsUpManager.getAllEntries().forEach(entry -> {
- final StatusBarNotification sbn = entry.getSbn();
- final Notification notification = sbn.getNotification();
- if (notification.fullScreenIntent != null) {
- if (DEBUG) {
- Log.d(TAG, "converting a heads up to fullScreen");
- }
- try {
- EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
- sbn.getKey());
- wakeUpForFullScreenIntent();
- notification.fullScreenIntent.send();
- entry.notifyFullScreenIntentLaunched();
- } catch (PendingIntent.CanceledException e) {
- }
- }
- });
- mHeadsUpManager.releaseAllImmediately();
- }
+ boolean isPanelExpanded();
- void wakeUpForFullScreenIntent() {
- if (isGoingToSleep() || mDozing) {
- mPowerManager.wakeUp(
- SystemClock.uptimeMillis(),
- PowerManager.WAKE_REASON_APPLICATION,
- "com.android.systemui:full_screen_intent");
- mWakeUpComingFromTouch = false;
- mWakeUpTouchLocation = null;
- }
- }
+ void onInputFocusTransfer(boolean start, boolean cancel, float velocity);
- void makeExpandedVisible(boolean force) {
- if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
- if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) {
- return;
- }
+ void animateCollapseQuickSettings();
- mExpandedVisible = true;
+ void onTouchEvent(MotionEvent event);
- // Expand the window to encompass the full screen in anticipation of the drag.
- // This is only possible to do atomically because the status bar is at the top of the screen!
- mNotificationShadeWindowController.setPanelVisible(true);
+ GestureRecorder getGestureRecorder();
- visibilityChanged(true);
- mCommandQueue.recomputeDisableFlags(mDisplayId, !force /* animate */);
- setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
- }
+ BiometricUnlockController getBiometricUnlockController();
- public void postAnimateCollapsePanels() {
- mMainExecutor.execute(mShadeController::animateCollapsePanels);
- }
+ void showWirelessChargingAnimation(int batteryLevel);
- public void postAnimateForceCollapsePanels() {
- mMainExecutor.execute(
- () -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE,
- true /* force */));
- }
-
- public void postAnimateOpenPanels() {
- mMessageRouter.sendMessage(MSG_OPEN_SETTINGS_PANEL);
- }
-
- public boolean isExpandedVisible() {
- return mExpandedVisible;
- }
-
- public boolean isPanelExpanded() {
- return mPanelExpanded;
- }
-
- /**
- * Called when another window is about to transfer it's input focus.
- */
- public void onInputFocusTransfer(boolean start, boolean cancel, float velocity) {
- if (!mCommandQueue.panelsEnabled()) {
- return;
- }
-
- if (start) {
- mNotificationPanelViewController.startWaitingForOpenPanelGesture();
- } else {
- mNotificationPanelViewController.stopWaitingForOpenPanelGesture(cancel, velocity);
- }
- }
-
- public void animateCollapseQuickSettings() {
- if (mState == StatusBarState.SHADE) {
- mNotificationPanelViewController.collapsePanel(
- true, false /* delayed */, 1.0f /* speedUpFactor */);
- }
- }
-
- void makeExpandedInvisible() {
- if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
- + " mExpandedVisible=" + mExpandedVisible);
-
- if (!mExpandedVisible || mNotificationShadeWindowView == null) {
- return;
- }
-
- // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
- mNotificationPanelViewController.collapsePanel(/*animate=*/ false, false /* delayed*/,
- 1.0f /* speedUpFactor */);
-
- mNotificationPanelViewController.closeQs();
-
- mExpandedVisible = false;
- visibilityChanged(false);
-
- // Update the visibility of notification shade and status bar window.
- mNotificationShadeWindowController.setPanelVisible(false);
- mStatusBarWindowController.setForceStatusBarVisible(false);
-
- // Close any guts that might be visible
- mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
-
- mShadeController.runPostCollapseRunnables();
- setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
- if (!mNotificationActivityStarter.isCollapsingToShowActivityOverLockscreen()) {
- showBouncerOrLockScreenIfKeyguard();
- } else if (DEBUG) {
- Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
- }
- mCommandQueue.recomputeDisableFlags(
- mDisplayId,
- mNotificationPanelViewController.hideStatusBarIconsWhenExpanded() /* animate */);
-
- // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
- // the bouncer appear animation.
- if (!mStatusBarKeyguardViewManager.isShowing()) {
- WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
- }
- }
-
- /** Called when a touch event occurred on {@link PhoneStatusBarView}. */
- public void onTouchEvent(MotionEvent event) {
- // TODO(b/202981994): Move this touch debugging to a central location. (Right now, it's
- // split between NotificationPanelViewController and here.)
- if (DEBUG_GESTURES) {
- if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
- EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
- event.getActionMasked(), (int) event.getX(), (int) event.getY(),
- mDisabled1, mDisabled2);
- }
-
- }
-
- if (SPEW) {
- Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1="
- + mDisabled1 + " mDisabled2=" + mDisabled2);
- } else if (CHATTY) {
- if (event.getAction() != MotionEvent.ACTION_MOVE) {
- Log.d(TAG, String.format(
- "panel: %s at (%f, %f) mDisabled1=0x%08x mDisabled2=0x%08x",
- MotionEvent.actionToString(event.getAction()),
- event.getRawX(), event.getRawY(), mDisabled1, mDisabled2));
- }
- }
-
- if (DEBUG_GESTURES) {
- mGestureRec.add(event);
- }
-
- if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
- final boolean upOrCancel =
- event.getAction() == MotionEvent.ACTION_UP ||
- event.getAction() == MotionEvent.ACTION_CANCEL;
- setInteracting(StatusBarManager.WINDOW_STATUS_BAR, !upOrCancel || mExpandedVisible);
- }
- }
-
- public GestureRecorder getGestureRecorder() {
- return mGestureRec;
- }
-
- public BiometricUnlockController getBiometricUnlockController() {
- return mBiometricUnlockController;
- }
-
- void showTransientUnchecked() {
- if (!mTransientShown) {
- mTransientShown = true;
- mNoAnimationOnNextBarModeChange = true;
- maybeUpdateBarMode();
- }
- }
-
-
- void clearTransient() {
- if (mTransientShown) {
- mTransientShown = false;
- maybeUpdateBarMode();
- }
- }
-
- private void maybeUpdateBarMode() {
- final int barMode = barMode(mTransientShown, mAppearance);
- if (updateBarMode(barMode)) {
- mLightBarController.onStatusBarModeChanged(barMode);
- updateBubblesVisibility();
- }
- }
-
- private boolean updateBarMode(int barMode) {
- if (mStatusBarMode != barMode) {
- mStatusBarMode = barMode;
- checkBarModes();
- mAutoHideController.touchAutoHide();
- return true;
- }
- return false;
- }
-
- private @TransitionMode int barMode(boolean isTransient, int appearance) {
- final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
- if (mOngoingCallController.hasOngoingCall() && mIsFullscreen) {
- return MODE_SEMI_TRANSPARENT;
- } else if (isTransient) {
- return MODE_SEMI_TRANSPARENT;
- } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
- return MODE_LIGHTS_OUT;
- } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) {
- return MODE_LIGHTS_OUT_TRANSPARENT;
- } else if ((appearance & APPEARANCE_OPAQUE_STATUS_BARS) != 0) {
- return MODE_OPAQUE;
- } else if ((appearance & APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS) != 0) {
- return MODE_SEMI_TRANSPARENT;
- } else {
- return MODE_TRANSPARENT;
- }
- }
-
- protected void showWirelessChargingAnimation(int batteryLevel) {
- showChargingAnimation(batteryLevel, UNKNOWN_BATTERY_LEVEL, 0);
- }
-
- protected void showChargingAnimation(int batteryLevel, int transmittingBatteryLevel,
- long animationDelay) {
- WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
- transmittingBatteryLevel, batteryLevel,
- new WirelessChargingAnimation.Callback() {
- @Override
- public void onAnimationStarting() {
- mNotificationShadeWindowController.setRequestTopUi(true, TAG);
- }
-
- @Override
- public void onAnimationEnded() {
- mNotificationShadeWindowController.setRequestTopUi(false, TAG);
- }
- }, false, sUiEventLogger).show(animationDelay);
- }
-
- public void checkBarModes() {
- if (mDemoModeController.isInDemoMode()) return;
- if (mStatusBarTransitions != null) {
- checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarTransitions);
- }
- mNavigationBarController.checkNavBarModes(mDisplayId);
- mNoAnimationOnNextBarModeChange = false;
- }
+ void checkBarModes();
// Called by NavigationBarFragment
- public void setQsScrimEnabled(boolean scrimEnabled) {
- mNotificationPanelViewController.setQsScrimEnabled(scrimEnabled);
- }
-
- /** Temporarily hides Bubbles if the status bar is hidden. */
- void updateBubblesVisibility() {
- mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged(
- mStatusBarMode != MODE_LIGHTS_OUT
- && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT
- && !mStatusBarWindowHidden));
- }
-
- void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
- BarTransitions transitions) {
- final boolean anim = !mNoAnimationOnNextBarModeChange && mDeviceInteractive
- && windowState != WINDOW_STATE_HIDDEN;
- transitions.transitionTo(mode, anim);
- }
-
- private void finishBarAnimations() {
- if (mStatusBarTransitions != null) {
- mStatusBarTransitions.finishAnimations();
- }
- mNavigationBarController.finishBarAnimations(mDisplayId);
- }
-
- private final Runnable mCheckBarModes = this::checkBarModes;
+ void setQsScrimEnabled(boolean scrimEnabled);
- public void setInteracting(int barWindow, boolean interacting) {
- mInteractingWindows = interacting
- ? (mInteractingWindows | barWindow)
- : (mInteractingWindows & ~barWindow);
- if (mInteractingWindows != 0) {
- mAutoHideController.suspendAutoHide();
- } else {
- mAutoHideController.resumeSuspendedAutoHide();
- }
- checkBarModes();
- }
+ void updateBubblesVisibility();
- private void dismissVolumeDialog() {
- if (mVolumeComponent != null) {
- mVolumeComponent.dismissNow();
- }
- }
-
- public static String viewInfo(View v) {
- return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
- + ") " + v.getWidth() + "x" + v.getHeight() + "]";
- }
+ void setInteracting(int barWindow, boolean interacting);
@Override
- public void dump(PrintWriter pwOriginal, String[] args) {
- IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
- synchronized (mQueueLock) {
- pw.println("Current Status Bar state:");
- pw.println(" mExpandedVisible=" + mExpandedVisible);
- pw.println(" mDisplayMetrics=" + mDisplayMetrics);
- pw.println(" mStackScroller: " + viewInfo(mStackScroller));
- pw.println(" mStackScroller: " + viewInfo(mStackScroller)
- + " scroll " + mStackScroller.getScrollX()
- + "," + mStackScroller.getScrollY());
- }
+ void dump(PrintWriter pwOriginal, String[] args);
- pw.print(" mInteractingWindows="); pw.println(mInteractingWindows);
- pw.print(" mStatusBarWindowState=");
- pw.println(windowStateToString(mStatusBarWindowState));
- pw.print(" mStatusBarMode=");
- pw.println(BarTransitions.modeToString(mStatusBarMode));
- pw.print(" mDozing="); pw.println(mDozing);
- pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported);
-
- pw.println(" ShadeWindowView: ");
- if (mNotificationShadeWindowViewController != null) {
- mNotificationShadeWindowViewController.dump(pw, args);
- dumpBarTransitions(pw, "PhoneStatusBarTransitions", mStatusBarTransitions);
- }
+ void createAndAddWindows(@Nullable RegisterStatusBarResult result);
- pw.println(" mMediaManager: ");
- if (mMediaManager != null) {
- mMediaManager.dump(pw, args);
- }
+ float getDisplayWidth();
- 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: ");
- if (mStackScroller != null) {
- // Double indent until we rewrite the rest of this dump()
- pw.increaseIndent();
- pw.increaseIndent();
- mStackScroller.dump(pw, args);
- pw.decreaseIndent();
- pw.decreaseIndent();
- }
- pw.println(" Theme:");
- String nightMode = mUiModeManager == null ? "null" : mUiModeManager.getNightMode() + "";
- pw.println(" dark theme: " + nightMode +
- " (auto: " + UiModeManager.MODE_NIGHT_AUTO +
- ", yes: " + UiModeManager.MODE_NIGHT_YES +
- ", no: " + UiModeManager.MODE_NIGHT_NO + ")");
- final boolean lightWpTheme = mContext.getThemeResId()
- == R.style.Theme_SystemUI_LightWallpaper;
- pw.println(" light wallpaper theme: " + lightWpTheme);
-
- if (mKeyguardIndicationController != null) {
- mKeyguardIndicationController.dump(pw, args);
- }
+ float getDisplayHeight();
- if (mScrimController != null) {
- mScrimController.dump(pw, args);
- }
-
- if (mLightRevealScrim != null) {
- pw.println(
- "mLightRevealScrim.getRevealEffect(): " + mLightRevealScrim.getRevealEffect());
- pw.println(
- "mLightRevealScrim.getRevealAmount(): " + mLightRevealScrim.getRevealAmount());
- }
+ void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned,
+ boolean dismissShade, int flags);
- if (mStatusBarKeyguardViewManager != null) {
- mStatusBarKeyguardViewManager.dump(pw);
- }
-
- mNotificationsController.dump(pw, args, DUMPTRUCK);
-
- if (DEBUG_GESTURES) {
- pw.print(" status bar gestures: ");
- mGestureRec.dump(pw, args);
- }
+ void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned,
+ boolean dismissShade);
- if (mHeadsUpManager != null) {
- mHeadsUpManager.dump(pw, args);
- } else {
- pw.println(" mHeadsUpManager: null");
- }
-
- if (mStatusBarTouchableRegionManager != null) {
- mStatusBarTouchableRegionManager.dump(pw, args);
- } else {
- pw.println(" mStatusBarTouchableRegionManager: null");
- }
-
- if (mLightBarController != null) {
- mLightBarController.dump(pw, args);
- }
-
- pw.println("SharedPreferences:");
- for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
- pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
- }
-
- pw.println("Camera gesture intents:");
- pw.println(" Insecure camera: " + CameraIntents.getInsecureCameraIntent(mContext));
- pw.println(" Secure camera: " + CameraIntents.getSecureCameraIntent(mContext));
- pw.println(" Override package: "
- + CameraIntents.getOverrideCameraPackage(mContext));
- }
-
- public static void dumpBarTransitions(
- PrintWriter pw, String var, @Nullable BarTransitions transitions) {
- pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode=");
- if (transitions != null) {
- pw.println(BarTransitions.modeToString(transitions.getMode()));
- } else {
- pw.println("Unknown");
- }
- }
-
- public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
- makeStatusBarView(result);
- mNotificationShadeWindowController.attach();
- mStatusBarWindowController.attach();
- }
-
- // called by makeStatusbar and also by PhoneStatusBarView
- void updateDisplaySize() {
- mDisplay.getMetrics(mDisplayMetrics);
- mDisplay.getSize(mCurrentDisplaySize);
- if (DEBUG_GESTURES) {
- mGestureRec.tag("display",
- String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
- }
- }
-
- float getDisplayDensity() {
- return mDisplayMetrics.density;
- }
-
- public float getDisplayWidth() {
- return mDisplayMetrics.widthPixels;
- }
-
- public float getDisplayHeight() {
- return mDisplayMetrics.heightPixels;
- }
-
- int getRotation() {
- return mDisplay.getRotation();
- }
-
- int getDisplayId() {
- return mDisplayId;
- }
-
- public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
- boolean dismissShade, int flags) {
- startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
- false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
- flags, null /* animationController */, getActivityUserHandle(intent));
- }
-
- public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
- boolean dismissShade) {
- startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, 0);
- }
-
- void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
- final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
- final Callback callback, int flags,
+ void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned,
+ boolean dismissShade, boolean disallowEnterPictureInPictureWhileLaunching,
+ Callback callback, int flags,
@Nullable ActivityLaunchAnimator.Controller animationController,
- final UserHandle userHandle) {
- if (onlyProvisioned && !mDeviceProvisionedController.isDeviceProvisioned()) return;
-
- final boolean willLaunchResolverActivity =
- mActivityIntentHelper.wouldLaunchResolverActivity(intent,
- mLockscreenUserManager.getCurrentUserId());
-
- boolean animate =
- animationController != null && !willLaunchResolverActivity && shouldAnimateLaunch(
- true /* isActivityIntent */);
- ActivityLaunchAnimator.Controller animController =
- animationController != null ? wrapAnimationController(animationController,
- dismissShade) : null;
-
- // If we animate, we will dismiss the shade only once the animation is done. This is taken
- // care of by the StatusBarLaunchAnimationController.
- boolean dismissShadeDirectly = dismissShade && animController == null;
-
- Runnable runnable = () -> {
- mAssistManagerLazy.get().hideAssist();
- intent.setFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.addFlags(flags);
- int[] result = new int[]{ActivityManager.START_CANCELED};
-
- mActivityLaunchAnimator.startIntentWithAnimation(animController,
- animate, intent.getPackage(), (adapter) -> {
- ActivityOptions options = new ActivityOptions(
- getActivityOptions(mDisplayId, adapter));
- options.setDisallowEnterPictureInPictureWhileLaunching(
- disallowEnterPictureInPictureWhileLaunching);
- if (CameraIntents.isInsecureCameraIntent(intent)) {
- // Normally an activity will set it's requested rotation
- // animation on its window. However when launching an activity
- // causes the orientation to change this is too late. In these cases
- // the default animation is used. This doesn't look good for
- // the camera (as it rotates the camera contents out of sync
- // with physical reality). So, we ask the WindowManager to
- // force the crossfade animation if an orientation change
- // happens to occur during the launch.
- options.setRotationAnimationHint(
- WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
- }
- if (Settings.Panel.ACTION_VOLUME.equals(intent.getAction())) {
- // Settings Panel is implemented as activity(not a dialog), so
- // underlying app is paused and may enter picture-in-picture mode
- // as a result.
- // So we need to disable picture-in-picture mode here
- // if it is volume panel.
- options.setDisallowEnterPictureInPictureWhileLaunching(true);
- }
-
- try {
- result[0] = ActivityTaskManager.getService().startActivityAsUser(
- null, mContext.getBasePackageName(),
- mContext.getAttributionTag(),
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
- options.toBundle(), userHandle.getIdentifier());
- } catch (RemoteException e) {
- Log.w(TAG, "Unable to start activity", e);
- }
- return result[0];
- });
-
- if (callback != null) {
- callback.onActivityStarted(result[0]);
- }
- };
- Runnable cancelRunnable = () -> {
- if (callback != null) {
- callback.onActivityStarted(ActivityManager.START_CANCELED);
- }
- };
- executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly,
- willLaunchResolverActivity, true /* deferred */, animate);
- }
+ UserHandle userHandle);
- @Nullable
- private ActivityLaunchAnimator.Controller wrapAnimationController(
- ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
- View rootView = animationController.getLaunchContainer().getRootView();
-
- Optional<ActivityLaunchAnimator.Controller> controllerFromStatusBar =
- mStatusBarWindowController.wrapAnimationControllerIfInStatusBar(
- rootView, animationController);
- if (controllerFromStatusBar.isPresent()) {
- return controllerFromStatusBar.get();
- }
-
- if (dismissShade) {
- // If the view is not in the status bar, then we are animating a view in the shade.
- // We have to make sure that we collapse it when the animation ends or is cancelled.
- return new StatusBarLaunchAnimatorController(animationController, this,
- true /* isLaunchForActivity */);
- }
+ void readyForKeyguardDone();
- return animationController;
- }
+ void executeRunnableDismissingKeyguard(Runnable runnable,
+ Runnable cancelAction,
+ boolean dismissShade,
+ boolean afterKeyguardGone,
+ boolean deferred);
- public void readyForKeyguardDone() {
- mStatusBarKeyguardViewManager.readyForKeyguardDone();
- }
+ void executeRunnableDismissingKeyguard(Runnable runnable,
+ Runnable cancelAction,
+ boolean dismissShade,
+ boolean afterKeyguardGone,
+ boolean deferred,
+ boolean willAnimateOnKeyguard);
- public void executeRunnableDismissingKeyguard(final Runnable runnable,
- final Runnable cancelAction,
- final boolean dismissShade,
- final boolean afterKeyguardGone,
- final boolean deferred) {
- executeRunnableDismissingKeyguard(runnable, cancelAction, dismissShade, afterKeyguardGone,
- deferred, false /* willAnimateOnKeyguard */);
- }
-
- public void executeRunnableDismissingKeyguard(final Runnable runnable,
- final Runnable cancelAction,
- final boolean dismissShade,
- final boolean afterKeyguardGone,
- final boolean deferred,
- final boolean willAnimateOnKeyguard) {
- OnDismissAction onDismissAction = new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- if (runnable != null) {
- if (mStatusBarKeyguardViewManager.isShowing()
- && mStatusBarKeyguardViewManager.isOccluded()) {
- mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
- } else {
- mMainExecutor.execute(runnable);
- }
- }
- if (dismissShade) {
- if (mExpandedVisible && !mBouncerShowing) {
- mShadeController.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed*/);
- } else {
-
- // Do it after DismissAction has been processed to conserve the needed
- // ordering.
- mMainExecutor.execute(mShadeController::runPostCollapseRunnables);
- }
- } else if (CentralSurfaces.this.isInLaunchTransition()
- && mNotificationPanelViewController.isLaunchTransitionFinished()) {
-
- // We are not dismissing the shade, but the launch transition is already
- // finished,
- // so nobody will call readyForKeyguardDone anymore. Post it such that
- // keyguardDonePending gets called first.
- mMainExecutor.execute(mStatusBarKeyguardViewManager::readyForKeyguardDone);
- }
- return deferred;
- }
-
- @Override
- public boolean willRunAnimationOnKeyguard() {
- return willAnimateOnKeyguard;
- }
- };
- dismissKeyguardThenExecute(onDismissAction, cancelAction, afterKeyguardGone);
- }
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- Trace.beginSection("CentralSurfaces#onReceive");
- if (DEBUG) Log.v(TAG, "onReceive: " + intent);
- String action = intent.getAction();
- String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
- KeyboardShortcuts.dismiss();
- mRemoteInputManager.closeRemoteInputs();
- if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
- int flags = CommandQueue.FLAG_EXCLUDE_NONE;
- if (reason != null) {
- if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
- flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
- }
- // Do not collapse notifications when starting dreaming if the notifications
- // shade is used for the screen off animation. It might require expanded
- // state for the scrims to be visible
- if (reason.equals(SYSTEM_DIALOG_REASON_DREAM)
- && mScreenOffAnimationController.shouldExpandNotifications()) {
- flags |= CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL;
- }
- }
- mShadeController.animateCollapsePanels(flags);
- }
- } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- if (mNotificationShadeWindowController != null) {
- mNotificationShadeWindowController.setNotTouchable(false);
- }
- finishBarAnimations();
- resetUserExpandedStates();
- }
- Trace.endSection();
- }
- };
-
- private final BroadcastReceiver mDemoReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) Log.v(TAG, "onReceive: " + intent);
- String action = intent.getAction();
- if (ACTION_FAKE_ARTWORK.equals(action)) {
- if (DEBUG_MEDIA_FAKE_ARTWORK) {
- mPresenter.updateMediaMetaData(true, true);
- }
- }
- }
- };
-
- public void resetUserExpandedStates() {
- mNotificationsController.resetUserExpandedStates();
- }
-
- private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen,
- boolean afterKeyguardGone) {
- if (mStatusBarKeyguardViewManager.isShowing() && requiresShadeOpen) {
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
- }
- dismissKeyguardThenExecute(action, null /* cancelAction */,
- afterKeyguardGone /* afterKeyguardGone */);
- }
-
- protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
- dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone);
- }
+ void resetUserExpandedStates();
@Override
- public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
- boolean afterKeyguardGone) {
- if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
- && mKeyguardStateController.canDismissLockScreen()
- && !mStatusBarStateController.leaveOpenOnKeyguardHide()
- && mDozeServiceHost.isPulsing()) {
- // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse.
- // TODO: Factor this transition out of BiometricUnlockController.
- mBiometricUnlockController.startWakeAndUnlock(
- BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
- }
- if (mStatusBarKeyguardViewManager.isShowing()) {
- mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
- afterKeyguardGone);
- } else {
- action.onDismiss();
- }
- }
- /**
- * Notify the shade controller that the current user changed
- *
- * @param newUserId userId of the new user
- */
- public void setLockscreenUser(int newUserId) {
- if (mLockscreenWallpaper != null) {
- mLockscreenWallpaper.setCurrentUser(newUserId);
- }
- mScrimController.setCurrentUser(newUserId);
- if (mWallpaperSupported) {
- mWallpaperChangedReceiver.onReceive(mContext, null);
- }
- }
+ void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
+ boolean afterKeyguardGone);
- /**
- * Reload some of our resources when the configuration changes.
- *
- * We don't reload everything when the configuration changes -- we probably
- * should, but getting that smooth is tough. Someday we'll fix that. In the
- * meantime, just update the things that we know change.
- */
- void updateResources() {
- // Update the quick setting tiles
- if (mQSPanelController != null) {
- mQSPanelController.updateResources();
- }
-
- if (mStatusBarWindowController != null) {
- mStatusBarWindowController.refreshStatusBarHeight();
- }
-
- if (mNotificationPanelViewController != null) {
- mNotificationPanelViewController.updateResources();
- }
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.updateResources();
- }
- if (mStatusBarKeyguardViewManager != null) {
- mStatusBarKeyguardViewManager.updateResources();
- }
-
- mPowerButtonReveal = new PowerButtonReveal(mContext.getResources().getDimensionPixelSize(
- com.android.systemui.R.dimen.physical_power_button_center_screen_location_y));
- }
-
- // Visibility reporting
- protected void handleVisibleToUserChanged(boolean visibleToUser) {
- if (visibleToUser) {
- handleVisibleToUserChangedImpl(visibleToUser);
- mNotificationLogger.startNotificationLogging();
- } else {
- mNotificationLogger.stopNotificationLogging();
- handleVisibleToUserChangedImpl(visibleToUser);
- }
- }
-
- // Visibility reporting
- void handleVisibleToUserChangedImpl(boolean visibleToUser) {
- if (visibleToUser) {
- /* The LEDs are turned off when the notification panel is shown, even just a little bit.
- * See also CentralSurfaces.setPanelExpanded for another place where we attempt to do
- * this.
- */
- boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
- boolean clearNotificationEffects =
- !mPresenter.isPresenterFullyCollapsed() &&
- (mState == StatusBarState.SHADE
- || mState == StatusBarState.SHADE_LOCKED);
- int notificationLoad = mNotificationsController.getActiveNotificationsCount();
- if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
- notificationLoad = 1;
- }
- final int finalNotificationLoad = notificationLoad;
- mUiBgExecutor.execute(() -> {
- try {
- mBarService.onPanelRevealed(clearNotificationEffects,
- finalNotificationLoad);
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- });
- } else {
- mUiBgExecutor.execute(() -> {
- try {
- mBarService.onPanelHidden();
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- });
- }
-
- }
-
- private void logStateToEventlog() {
- boolean isShowing = mStatusBarKeyguardViewManager.isShowing();
- boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded();
- boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
- boolean isSecure = mKeyguardStateController.isMethodSecure();
- boolean unlocked = mKeyguardStateController.canDismissLockScreen();
- int stateFingerprint = getLoggingFingerprint(mState,
- isShowing,
- isOccluded,
- isBouncerShowing,
- isSecure,
- unlocked);
- if (stateFingerprint != mLastLoggedStateFingerprint) {
- if (mStatusBarStateLog == null) {
- mStatusBarStateLog = new LogMaker(MetricsEvent.VIEW_UNKNOWN);
- }
- mMetricsLogger.write(mStatusBarStateLog
- .setCategory(isBouncerShowing ? MetricsEvent.BOUNCER : MetricsEvent.LOCKSCREEN)
- .setType(isShowing ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
- .setSubtype(isSecure ? 1 : 0));
- EventLogTags.writeSysuiStatusBarState(mState,
- isShowing ? 1 : 0,
- isOccluded ? 1 : 0,
- isBouncerShowing ? 1 : 0,
- isSecure ? 1 : 0,
- unlocked ? 1 : 0);
- mLastLoggedStateFingerprint = stateFingerprint;
-
- StringBuilder uiEventValueBuilder = new StringBuilder();
- uiEventValueBuilder.append(isBouncerShowing ? "BOUNCER" : "LOCKSCREEN");
- uiEventValueBuilder.append(isShowing ? "_OPEN" : "_CLOSE");
- uiEventValueBuilder.append(isSecure ? "_SECURE" : "_INSECURE");
- sUiEventLogger.log(StatusBarUiEvent.valueOf(uiEventValueBuilder.toString()));
- }
- }
-
- /**
- * Returns a fingerprint of fields logged to eventlog
- */
- private static int getLoggingFingerprint(int statusBarState, boolean keyguardShowing,
- boolean keyguardOccluded, boolean bouncerShowing, boolean secure,
- boolean currentlyInsecure) {
- // Reserve 8 bits for statusBarState. We'll never go higher than
- // that, right? Riiiight.
- return (statusBarState & 0xFF)
- | ((keyguardShowing ? 1 : 0) << 8)
- | ((keyguardOccluded ? 1 : 0) << 9)
- | ((bouncerShowing ? 1 : 0) << 10)
- | ((secure ? 1 : 0) << 11)
- | ((currentlyInsecure ? 1 : 0) << 12);
- }
+ void setLockscreenUser(int newUserId);
@Override
- public void postQSRunnableDismissingKeyguard(final Runnable runnable) {
- mMainExecutor.execute(() -> {
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
- executeRunnableDismissingKeyguard(
- () -> mMainExecutor.execute(runnable), null, false, false, false);
- });
- }
+ void postQSRunnableDismissingKeyguard(Runnable runnable);
@Override
- public void postStartActivityDismissingKeyguard(PendingIntent intent) {
- postStartActivityDismissingKeyguard(intent, null /* animationController */);
- }
+ void postStartActivityDismissingKeyguard(PendingIntent intent);
@Override
- public void postStartActivityDismissingKeyguard(final PendingIntent intent,
- @Nullable ActivityLaunchAnimator.Controller animationController) {
- mMainExecutor.execute(() -> startPendingIntentDismissingKeyguard(intent,
- null /* intentSentUiThreadCallback */, animationController));
- }
+ void postStartActivityDismissingKeyguard(PendingIntent intent,
+ @Nullable ActivityLaunchAnimator.Controller animationController);
@Override
- public void postStartActivityDismissingKeyguard(final Intent intent, int delay) {
- postStartActivityDismissingKeyguard(intent, delay, null /* animationController */);
- }
+ void postStartActivityDismissingKeyguard(Intent intent, int delay);
@Override
- public void postStartActivityDismissingKeyguard(Intent intent, int delay,
- @Nullable ActivityLaunchAnimator.Controller animationController) {
- mMainExecutor.executeDelayed(
- () ->
- startActivityDismissingKeyguard(intent, true /* onlyProvisioned */,
- true /* dismissShade */,
- false /* disallowEnterPictureInPictureWhileLaunching */,
- null /* callback */,
- 0 /* flags */,
- animationController,
- getActivityUserHandle(intent)),
- delay);
- }
+ void postStartActivityDismissingKeyguard(Intent intent, int delay,
+ @Nullable ActivityLaunchAnimator.Controller animationController);
- public void showKeyguard() {
- mStatusBarStateController.setKeyguardRequested(true);
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
- updateIsKeyguard();
- mAssistManagerLazy.get().onLockscreenShown();
- }
+ void showKeyguard();
- public boolean hideKeyguard() {
- mStatusBarStateController.setKeyguardRequested(false);
- return updateIsKeyguard();
- }
+ boolean hideKeyguard();
- boolean updateIsKeyguard() {
- return updateIsKeyguard(false /* forceStateChange */);
- }
+ void showKeyguardImpl();
- boolean updateIsKeyguard(boolean forceStateChange) {
- boolean wakeAndUnlocking = mBiometricUnlockController.isWakeAndUnlock();
-
- // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise
- // there's no surface we can show to the user. Note that the device goes fully interactive
- // late in the transition, so we also allow the device to start dozing once the screen has
- // turned off fully.
- boolean keyguardForDozing = mDozeServiceHost.getDozingRequested()
- && (!mDeviceInteractive || (isGoingToSleep()
- && (isScreenFullyOff()
- || (mKeyguardStateController.isShowing() && !isOccluded()))));
- boolean isWakingAndOccluded = isOccluded() && isWakingOrAwake();
- boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested()
- || keyguardForDozing) && !wakeAndUnlocking && !isWakingAndOccluded;
- if (keyguardForDozing) {
- updatePanelExpansionForKeyguard();
- }
- if (shouldBeKeyguard) {
- if (mScreenOffAnimationController.isKeyguardShowDelayed()
- || (isGoingToSleep()
- && mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_TURNING_OFF)) {
- // Delay showing the keyguard until screen turned off.
- } else {
- showKeyguardImpl();
- }
- } else {
- // During folding a foldable device this might be called as a result of
- // 'onScreenTurnedOff' call for the inner display.
- // In this case:
- // * When phone is locked on folding: it doesn't make sense to hide keyguard as it
- // will be immediately locked again
- // * When phone is unlocked: we still don't want to execute hiding of the keyguard
- // as the animation could prepare 'fake AOD' interface (without actually
- // transitioning to keyguard state) and this might reset the view states
- if (!mScreenOffAnimationController.isKeyguardHideDelayed()) {
- return hideKeyguardImpl(forceStateChange);
- }
- }
- return false;
- }
+ boolean isInLaunchTransition();
- public void showKeyguardImpl() {
- Trace.beginSection("CentralSurfaces#showKeyguard");
- // In case we're locking while a smartspace transition is in progress, reset it.
- mKeyguardUnlockAnimationController.resetSmartspaceTransition();
- if (mKeyguardStateController.isLaunchTransitionFadingAway()) {
- mNotificationPanelViewController.cancelAnimation();
- onLaunchTransitionFadingEnded();
- }
- mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
- if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) {
- mStatusBarStateController.setState(StatusBarState.KEYGUARD);
- }
- updatePanelExpansionForKeyguard();
- Trace.endSection();
- }
+ void fadeKeyguardAfterLaunchTransition(Runnable beforeFading,
+ Runnable endRunnable, Runnable cancelRunnable);
- private void updatePanelExpansionForKeyguard() {
- if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode()
- != BiometricUnlockController.MODE_WAKE_AND_UNLOCK && !mBouncerShowing) {
- mShadeController.instantExpandNotificationsPanel();
- }
- }
+ void fadeKeyguardWhilePulsing();
- private void onLaunchTransitionFadingEnded() {
- mNotificationPanelViewController.resetAlpha();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
- releaseGestureWakeLock();
- runLaunchTransitionEndRunnable();
- mKeyguardStateController.setLaunchTransitionFadingAway(false);
- mPresenter.updateMediaMetaData(true /* metaDataChanged */, true);
- }
+ void animateKeyguardUnoccluding();
- public boolean isInLaunchTransition() {
- return mNotificationPanelViewController.isLaunchTransitionRunning()
- || mNotificationPanelViewController.isLaunchTransitionFinished();
- }
+ void startLaunchTransitionTimeout();
- /**
- * Fades the content of the keyguard away after the launch transition is done.
- *
- * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
- * starts
- * @param endRunnable the runnable to be run when the transition is done. Will not run
- * if the transition is cancelled, instead cancelRunnable will run
- * @param cancelRunnable the runnable to be run if the transition is cancelled
- */
- public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
- Runnable endRunnable, Runnable cancelRunnable) {
- mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
- mLaunchTransitionEndRunnable = endRunnable;
- mLaunchTransitionCancelRunnable = cancelRunnable;
- Runnable hideRunnable = () -> {
- mKeyguardStateController.setLaunchTransitionFadingAway(true);
- if (beforeFading != null) {
- beforeFading.run();
- }
- updateScrimController();
- mPresenter.updateMediaMetaData(false, true);
- mNotificationPanelViewController.resetAlpha();
- mNotificationPanelViewController.fadeOut(
- FADE_KEYGUARD_START_DELAY, FADE_KEYGUARD_DURATION,
- this::onLaunchTransitionFadingEnded);
- mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
- LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
- };
- if (mNotificationPanelViewController.isLaunchTransitionRunning()) {
- mNotificationPanelViewController.setLaunchTransitionEndRunnable(hideRunnable);
- } else {
- hideRunnable.run();
- }
- }
+ boolean hideKeyguardImpl(boolean forceStateChange);
- private void cancelAfterLaunchTransitionRunnables() {
- if (mLaunchTransitionCancelRunnable != null) {
- mLaunchTransitionCancelRunnable.run();
- }
- mLaunchTransitionEndRunnable = null;
- mLaunchTransitionCancelRunnable = null;
- mNotificationPanelViewController.setLaunchTransitionEndRunnable(null);
- }
+ void keyguardGoingAway();
- /**
- * Fades the content of the Keyguard while we are dozing and makes it invisible when finished
- * fading.
- */
- public void fadeKeyguardWhilePulsing() {
- mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
- ()-> {
- hideKeyguard();
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
- }).start();
- }
+ void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration,
+ boolean isBypassFading);
- /**
- * Plays the animation when an activity that was occluding Keyguard goes away.
- */
- public void animateKeyguardUnoccluding() {
- mNotificationPanelViewController.setExpandedFraction(0f);
- mCommandQueueCallbacks.animateExpandNotificationsPanel();
- mScrimController.setUnocclusionAnimationRunning(true);
- }
+ void finishKeyguardFadingAway();
- /**
- * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that
- * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen
- * because the launched app crashed or something else went wrong.
- */
- public void startLaunchTransitionTimeout() {
- mMessageRouter.sendMessageDelayed(
- MSG_LAUNCH_TRANSITION_TIMEOUT, LAUNCH_TRANSITION_TIMEOUT_MS);
- }
+ void userActivity();
- private void onLaunchTransitionTimeout() {
- Log.w(TAG, "Launch transition: Timeout!");
- mNotificationPanelViewController.onAffordanceLaunchEnded();
- releaseGestureWakeLock();
- mNotificationPanelViewController.resetViews(false /* animate */);
- }
+ boolean interceptMediaKey(KeyEvent event);
- private void runLaunchTransitionEndRunnable() {
- mLaunchTransitionCancelRunnable = null;
- if (mLaunchTransitionEndRunnable != null) {
- Runnable r = mLaunchTransitionEndRunnable;
+ boolean dispatchKeyEventPreIme(KeyEvent event);
- // mLaunchTransitionEndRunnable might call showKeyguard, which would execute it again,
- // which would lead to infinite recursion. Protect against it.
- mLaunchTransitionEndRunnable = null;
- r.run();
- }
- }
+ boolean onMenuPressed();
- /**
- * @return true if we would like to stay in the shade, false if it should go away entirely
- */
- public boolean hideKeyguardImpl(boolean forceStateChange) {
- Trace.beginSection("CentralSurfaces#hideKeyguard");
- boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
- int previousState = mStatusBarStateController.getState();
- if (!(mStatusBarStateController.setState(StatusBarState.SHADE, forceStateChange))) {
- //TODO: StatusBarStateController should probably know about hiding the keyguard and
- // notify listeners.
-
- // If the state didn't change, we may still need to update public mode
- mLockscreenUserManager.updatePublicMode();
- }
- if (mStatusBarStateController.leaveOpenOnKeyguardHide()) {
- if (!mStatusBarStateController.isKeyguardRequested()) {
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
- }
- long delay = mKeyguardStateController.calculateGoingToFullShadeDelay();
- mLockscreenShadeTransitionController.onHideKeyguard(delay, previousState);
-
- // Disable layout transitions in navbar for this transition because the load is just
- // too heavy for the CPU and GPU on any device.
- mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay);
- } else if (!mNotificationPanelViewController.isCollapsing()) {
- instantCollapseNotificationPanel();
- }
+ void endAffordanceLaunch();
- // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
- // visibilities so next time we open the panel we know the correct height already.
- if (mQSPanelController != null) {
- mQSPanelController.refreshAllTiles();
- }
- mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
- releaseGestureWakeLock();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
- mNotificationPanelViewController.resetAlpha();
- mNotificationPanelViewController.resetTranslation();
- mNotificationPanelViewController.resetViewGroupFade();
- updateDozingState();
- updateScrimController();
- Trace.endSection();
- return staying;
- }
+ boolean onBackPressed();
- private void releaseGestureWakeLock() {
- if (mGestureWakeLock.isHeld()) {
- mGestureWakeLock.release();
- }
- }
-
- /**
- * Notifies the status bar that Keyguard is going away very soon.
- */
- public void keyguardGoingAway() {
- // Treat Keyguard exit animation as an app transition to achieve nice transition for status
- // bar.
- mKeyguardStateController.notifyKeyguardGoingAway(true);
- mCommandQueue.appTransitionPending(mDisplayId, true /* forced */);
- updateScrimController();
- }
-
- /**
- * Notifies the status bar the Keyguard is fading away with the specified timings.
- * @param startTime the start time of the animations in uptime millis
- * @param delay the precalculated animation delay in milliseconds
- * @param fadeoutDuration the duration of the exit animation, in milliseconds
- * @param isBypassFading is this a fading away animation while bypassing
- */
- public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration,
- boolean isBypassFading) {
- mCommandQueue.appTransitionStarting(mDisplayId, startTime + fadeoutDuration
- - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
- LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
- mCommandQueue.recomputeDisableFlags(mDisplayId, fadeoutDuration > 0 /* animate */);
- mCommandQueue.appTransitionStarting(mDisplayId,
- startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
- LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
- mKeyguardStateController.notifyKeyguardFadingAway(delay, fadeoutDuration, isBypassFading);
- }
-
- /**
- * Notifies that the Keyguard fading away animation is done.
- */
- public void finishKeyguardFadingAway() {
- mKeyguardStateController.notifyKeyguardDoneFading();
- mScrimController.setExpansionAffectsAlpha(true);
-
- // If the device was re-locked while unlocking, we might have a pending lock that was
- // delayed because the keyguard was in the middle of going away.
- mKeyguardViewMediator.maybeHandlePendingLock();
- }
-
- /**
- * Switches theme from light to dark and vice-versa.
- */
- protected void updateTheme() {
- // Set additional scrim only if the lock and system wallpaper are different to prevent
- // applying the dimming effect twice.
- mUiBgExecutor.execute(() -> {
- float dimAmount = 0f;
- if (mWallpaperManager.lockScreenWallpaperExists()) {
- dimAmount = mWallpaperManager.getWallpaperDimAmount();
- }
- final float scrimDimAmount = dimAmount;
- mMainExecutor.execute(() -> {
- mScrimController.setAdditionalScrimBehindAlphaKeyguard(scrimDimAmount);
- mScrimController.applyCompositeAlphaOnScrimBehindKeyguard();
- });
- });
-
- // Lock wallpaper defines the color of the majority of the views, hence we'll use it
- // to set our default theme.
- final boolean lockDarkText = mColorExtractor.getNeutralColors().supportsDarkText();
- final int themeResId = lockDarkText ? R.style.Theme_SystemUI_LightWallpaper
- : R.style.Theme_SystemUI;
- if (mContext.getThemeResId() != themeResId) {
- mContext.setTheme(themeResId);
- mConfigurationController.notifyThemeChanged();
- }
- }
-
- private void updateDozingState() {
- Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
- Trace.beginSection("CentralSurfaces#updateDozingState");
-
- boolean visibleNotOccluded = mStatusBarKeyguardViewManager.isShowing()
- && !mStatusBarKeyguardViewManager.isOccluded();
- // If we're dozing and we'll be animating the screen off, the keyguard isn't currently
- // visible but will be shortly for the animation, so we should proceed as if it's visible.
- boolean visibleNotOccludedOrWillBe =
- visibleNotOccluded || (mDozing && mDozeParameters.shouldDelayKeyguardShow());
-
- boolean wakeAndUnlock = mBiometricUnlockController.getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
- boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock)
- || (mDozing && mDozeParameters.shouldControlScreenOff()
- && visibleNotOccludedOrWillBe);
-
- mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation);
- updateQsExpansionEnabled();
- Trace.endSection();
- }
-
- public void userActivity() {
- if (mState == StatusBarState.KEYGUARD) {
- mKeyguardViewMediatorCallback.userActivity();
- }
- }
-
- public boolean interceptMediaKey(KeyEvent event) {
- return mState == StatusBarState.KEYGUARD
- && mStatusBarKeyguardViewManager.interceptMediaKey(event);
- }
-
- /**
- * While IME is active and a BACK event is detected, check with
- * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event
- * should be handled before routing to IME, in order to prevent the user having to hit back
- * twice to exit bouncer.
- */
- public boolean dispatchKeyEventPreIme(KeyEvent event) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_BACK:
- if (mState == StatusBarState.KEYGUARD
- && mStatusBarKeyguardViewManager.dispatchBackKeyEventPreIme()) {
- return onBackPressed();
- }
- }
- return false;
- }
-
- protected boolean shouldUnlockOnMenuPressed() {
- return mDeviceInteractive && mState != StatusBarState.SHADE
- && mStatusBarKeyguardViewManager.shouldDismissOnMenuPressed();
- }
-
- public boolean onMenuPressed() {
- if (shouldUnlockOnMenuPressed()) {
- mShadeController.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
- return true;
- }
- return false;
- }
-
- public void endAffordanceLaunch() {
- releaseGestureWakeLock();
- mNotificationPanelViewController.onAffordanceLaunchEnded();
- }
-
- public boolean onBackPressed() {
- boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
- if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) {
- if (isScrimmedBouncer) {
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
- } else {
- mNotificationPanelViewController.expandWithoutQs();
- }
- return true;
- }
- if (mNotificationPanelViewController.isQsCustomizing()) {
- mNotificationPanelViewController.closeQsCustomizer();
- return true;
- }
- if (mNotificationPanelViewController.isQsExpanded()) {
- if (mNotificationPanelViewController.isQsDetailShowing()) {
- mNotificationPanelViewController.closeQsDetail();
- } else {
- mNotificationPanelViewController.animateCloseQs(false /* animateAway */);
- }
- return true;
- }
- if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) {
- return true;
- }
- if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
- if (mNotificationPanelViewController.canPanelBeCollapsed()) {
- mShadeController.animateCollapsePanels();
- }
- return true;
- }
- return false;
- }
-
- public boolean onSpacePressed() {
- if (mDeviceInteractive && mState != StatusBarState.SHADE) {
- mShadeController.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
- return true;
- }
- return false;
- }
-
- private void showBouncerOrLockScreenIfKeyguard() {
- // If the keyguard is animating away, we aren't really the keyguard anymore and should not
- // show the bouncer/lockscreen.
- if (!mKeyguardViewMediator.isHiding()
- && !mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
- if (mState == StatusBarState.SHADE_LOCKED
- && mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
- // shade is showing while locked on the keyguard, so go back to showing the
- // lock screen where users can use the UDFPS affordance to enter the device
- mStatusBarKeyguardViewManager.reset(true);
- } else if ((mState == StatusBarState.KEYGUARD
- && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing())
- || mState == StatusBarState.SHADE_LOCKED) {
- mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
- }
- }
- }
+ boolean onSpacePressed();
- /**
- * Show the bouncer if we're currently on the keyguard or shade locked and aren't hiding.
- * @param performAction the action to perform when the bouncer is dismissed.
- * @param cancelAction the action to perform when unlock is aborted.
- */
- public void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction,
- Runnable cancelAction) {
- if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
- && !mKeyguardViewMediator.isHiding()) {
- mStatusBarKeyguardViewManager.dismissWithAction(performAction, cancelAction,
- false /* afterKeyguardGone */);
- } else if (cancelAction != null) {
- cancelAction.run();
- }
- }
+ void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction,
+ Runnable cancelAction);
- void instantCollapseNotificationPanel() {
- mNotificationPanelViewController.instantCollapse();
- mShadeController.runPostCollapseRunnables();
- }
+ LightRevealScrim getLightRevealScrim();
- /**
- * Collapse the panel directly if we are on the main thread, post the collapsing on the main
- * thread if we are not.
- */
- void collapsePanelOnMainThread() {
- if (Looper.getMainLooper().isCurrentThread()) {
- mShadeController.collapsePanel();
- } else {
- mContext.getMainExecutor().execute(mShadeController::collapsePanel);
- }
- }
+ void onTrackingStarted();
- /** Collapse the panel. The collapsing will be animated for the given {@code duration}. */
- void collapsePanelWithDuration(int duration) {
- mNotificationPanelViewController.collapseWithDuration(duration);
- }
+ void onClosingFinished();
- /**
- * Updates the light reveal effect to reflect the reason we're waking or sleeping (for example,
- * from the power button).
- * @param wakingUp Whether we're updating because we're waking up (true) or going to sleep
- * (false).
- */
- private void updateRevealEffect(boolean wakingUp) {
- if (mLightRevealScrim == null) {
- return;
- }
+ void onUnlockHintStarted();
- final boolean wakingUpFromPowerButton = wakingUp
- && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
- && mWakefulnessLifecycle.getLastWakeReason()
- == PowerManager.WAKE_REASON_POWER_BUTTON;
- final boolean sleepingFromPowerButton = !wakingUp
- && mWakefulnessLifecycle.getLastSleepReason()
- == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON;
-
- if (wakingUpFromPowerButton || sleepingFromPowerButton) {
- mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
- mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
- } else if (!wakingUp || !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
- // If we're going to sleep, but it's not from the power button, use the default reveal.
- // If we're waking up, only use the default reveal if the biometric controller didn't
- // already set it to the circular reveal because we're waking up from a fingerprint/face
- // auth.
- mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
- mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
- }
- }
-
- public LightRevealScrim getLightRevealScrim() {
- return mLightRevealScrim;
- }
-
- public void onTrackingStarted() {
- mShadeController.runPostCollapseRunnables();
- }
-
- public void onClosingFinished() {
- mShadeController.runPostCollapseRunnables();
- if (!mPresenter.isPresenterFullyCollapsed()) {
- // if we set it not to be focusable when collapsing, we have to undo it when we aborted
- // the closing
- mNotificationShadeWindowController.setNotificationShadeFocusable(true);
- }
- }
+ void onHintFinished();
- public void onUnlockHintStarted() {
- mFalsingCollector.onUnlockHintStarted();
- mKeyguardIndicationController.showActionToUnlock();
- }
+ void onCameraHintStarted();
- public void onHintFinished() {
- // Delay the reset a bit so the user can read the text.
- mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
- }
+ void onVoiceAssistHintStarted();
- public void onCameraHintStarted() {
- mFalsingCollector.onCameraHintStarted();
- mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
- }
+ void onPhoneHintStarted();
- public void onVoiceAssistHintStarted() {
- mFalsingCollector.onLeftAffordanceHintStarted();
- mKeyguardIndicationController.showTransientIndication(R.string.voice_hint);
- }
-
- public void onPhoneHintStarted() {
- mFalsingCollector.onLeftAffordanceHintStarted();
- mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
- }
-
- public void onTrackingStopped(boolean expand) {
- if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
- if (!expand && !mKeyguardStateController.canDismissLockScreen()) {
- mStatusBarKeyguardViewManager.showBouncer(false /* scrimmed */);
- }
- }
- }
+ void onTrackingStopped(boolean expand);
// TODO: Figure out way to remove these.
- public NavigationBarView getNavigationBarView() {
- return mNavigationBarController.getNavigationBarView(mDisplayId);
- }
+ NavigationBarView getNavigationBarView();
- public boolean isOverviewEnabled() {
- return mNavigationBarController.isOverviewEnabled(mDisplayId);
- }
+ boolean isOverviewEnabled();
- public void showPinningEnterExitToast(boolean entering) {
- mNavigationBarController.showPinningEnterExitToast(mDisplayId, entering);
- }
+ void showPinningEnterExitToast(boolean entering);
- public void showPinningEscapeToast() {
- mNavigationBarController.showPinningEscapeToast(mDisplayId);
- }
+ void showPinningEscapeToast();
- /**
- * TODO: Remove this method. Views should not be passed forward. Will cause theme issues.
- * @return bottom area view
- */
- public KeyguardBottomAreaView getKeyguardBottomAreaView() {
- return mNotificationPanelViewController.getKeyguardBottomAreaView();
- }
+ KeyguardBottomAreaView getKeyguardBottomAreaView();
- /**
- * Propagation of the bouncer state, indicating that it's fully visible.
- */
- public void setBouncerShowing(boolean bouncerShowing) {
- mBouncerShowing = bouncerShowing;
- mKeyguardBypassController.setBouncerShowing(bouncerShowing);
- mPulseExpansionHandler.setBouncerShowing(bouncerShowing);
- setBouncerShowingForStatusBarComponents(bouncerShowing);
- mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing);
- mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
- updateScrimController();
- if (!mBouncerShowing) {
- updatePanelExpansionForKeyguard();
- }
- }
+ void setBouncerShowing(boolean bouncerShowing);
- /**
- * Propagate the bouncer state to status bar components.
- *
- * Separate from {@link #setBouncerShowing} because we sometimes re-create the status bar and
- * should update only the status bar components.
- */
- private void setBouncerShowingForStatusBarComponents(boolean bouncerShowing) {
- int importance = bouncerShowing
- ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
- if (mPhoneStatusBarViewController != null) {
- mPhoneStatusBarViewController.setImportantForAccessibility(importance);
- }
- mNotificationPanelViewController.setImportantForAccessibility(importance);
- mNotificationPanelViewController.setBouncerShowing(bouncerShowing);
- }
+ void collapseShade();
- /**
- * Collapses the notification shade if it is tracking or expanded.
- */
- public void collapseShade() {
- if (mNotificationPanelViewController.isTracking()) {
- mNotificationShadeWindowViewController.cancelCurrentTouch();
- }
- if (mPanelExpanded && mState == StatusBarState.SHADE) {
- mShadeController.animateCollapsePanels();
- }
- }
+ int getWakefulnessState();
- @VisibleForTesting
- final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
- @Override
- public void onFinishedGoingToSleep() {
- mNotificationPanelViewController.onAffordanceLaunchEnded();
- releaseGestureWakeLock();
- mLaunchCameraWhenFinishedWaking = false;
- mDeviceInteractive = false;
- mWakeUpComingFromTouch = false;
- mWakeUpTouchLocation = null;
- updateVisibleToUser();
-
- updateNotificationPanelTouchState();
- mNotificationShadeWindowViewController.cancelCurrentTouch();
- if (mLaunchCameraOnFinishedGoingToSleep) {
- mLaunchCameraOnFinishedGoingToSleep = false;
-
- // This gets executed before we will show Keyguard, so post it in order that the state
- // is correct.
- mMainExecutor.execute(() -> mCommandQueueCallbacks.onCameraLaunchGestureDetected(
- mLastCameraLaunchSource));
- }
+ boolean isScreenFullyOff();
- if (mLaunchEmergencyActionOnFinishedGoingToSleep) {
- mLaunchEmergencyActionOnFinishedGoingToSleep = false;
+ void showScreenPinningRequest(int taskId, boolean allowCancel);
- // This gets executed before we will show Keyguard, so post it in order that the
- // state is correct.
- mMainExecutor.execute(
- () -> mCommandQueueCallbacks.onEmergencyActionLaunchGestureDetected());
- }
- updateIsKeyguard();
- }
-
- @Override
- public void onStartedGoingToSleep() {
- String tag = "CentralSurfaces#onStartedGoingToSleep";
- DejankUtils.startDetectingBlockingIpcs(tag);
-
- // cancel stale runnables that could put the device in the wrong state
- cancelAfterLaunchTransitionRunnables();
-
- updateRevealEffect(false /* wakingUp */);
- updateNotificationPanelTouchState();
- maybeEscalateHeadsUp();
- dismissVolumeDialog();
- mWakeUpCoordinator.setFullyAwake(false);
- mKeyguardBypassController.onStartedGoingToSleep();
-
- // The unlocked screen off and fold to aod animations might use our LightRevealScrim -
- // we need to be expanded for it to be visible.
- if (mDozeParameters.shouldShowLightRevealScrim()) {
- makeExpandedVisible(true);
- }
-
- DejankUtils.stopDetectingBlockingIpcs(tag);
- }
-
- @Override
- public void onStartedWakingUp() {
- String tag = "CentralSurfaces#onStartedWakingUp";
- DejankUtils.startDetectingBlockingIpcs(tag);
- mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
- mDeviceInteractive = true;
- mWakeUpCoordinator.setWakingUp(true);
- if (!mKeyguardBypassController.getBypassEnabled()) {
- mHeadsUpManager.releaseAllImmediately();
- }
- updateVisibleToUser();
- updateIsKeyguard();
- mDozeServiceHost.stopDozing();
- // This is intentionally below the stopDozing call above, since it avoids that we're
- // unnecessarily animating the wakeUp transition. Animations should only be enabled
- // once we fully woke up.
- updateRevealEffect(true /* wakingUp */);
- updateNotificationPanelTouchState();
-
- // If we are waking up during the screen off animation, we should undo making the
- // expanded visible (we did that so the LightRevealScrim would be visible).
- if (mScreenOffAnimationController.shouldHideLightRevealScrimOnWakeUp()) {
- makeExpandedInvisible();
- }
-
- });
- DejankUtils.stopDetectingBlockingIpcs(tag);
- }
-
- @Override
- public void onFinishedWakingUp() {
- mWakeUpCoordinator.setFullyAwake(true);
- mWakeUpCoordinator.setWakingUp(false);
- if (mLaunchCameraWhenFinishedWaking) {
- mNotificationPanelViewController.launchCamera(
- false /* animate */, mLastCameraLaunchSource);
- mLaunchCameraWhenFinishedWaking = false;
- }
- if (mLaunchEmergencyActionWhenFinishedWaking) {
- mLaunchEmergencyActionWhenFinishedWaking = false;
- Intent emergencyIntent = getEmergencyActionIntent();
- if (emergencyIntent != null) {
- mContext.startActivityAsUser(emergencyIntent,
- getActivityUserHandle(emergencyIntent));
- }
- }
- updateScrimController();
- }
- };
-
- /**
- * We need to disable touch events because these might
- * collapse the panel after we expanded it, and thus we would end up with a blank
- * Keyguard.
- */
- void updateNotificationPanelTouchState() {
- boolean goingToSleepWithoutAnimation = isGoingToSleep()
- && !mDozeParameters.shouldControlScreenOff();
- boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
- || goingToSleepWithoutAnimation;
- mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
- mNotificationIconAreaController.setAnimationsEnabled(!disabled);
- }
-
- final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
- @Override
- public void onScreenTurningOn(Runnable onDrawn) {
- mFalsingCollector.onScreenTurningOn();
- mNotificationPanelViewController.onScreenTurningOn();
- }
-
- @Override
- public void onScreenTurnedOn() {
- mScrimController.onScreenTurnedOn();
- }
-
- @Override
- public void onScreenTurnedOff() {
- Trace.beginSection("CentralSurfaces#onScreenTurnedOff");
- mFalsingCollector.onScreenOff();
- mScrimController.onScreenTurnedOff();
- if (mCloseQsBeforeScreenOff) {
- mNotificationPanelViewController.closeQs();
- mCloseQsBeforeScreenOff = false;
- }
- updateIsKeyguard();
- Trace.endSection();
- }
- };
-
- public int getWakefulnessState() {
- return mWakefulnessLifecycle.getWakefulness();
- }
-
- /**
- * @return true if the screen is currently fully off, i.e. has finished turning off and has
- * since not started turning on.
- */
- public boolean isScreenFullyOff() {
- return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF;
- }
-
- public void showScreenPinningRequest(int taskId, boolean allowCancel) {
- mScreenPinningRequest.showPrompt(taskId, allowCancel);
- }
-
- @Nullable Intent getEmergencyActionIntent() {
- Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
- PackageManager pm = mContext.getPackageManager();
- List<ResolveInfo> emergencyActivities = pm.queryIntentActivities(emergencyIntent,
- PackageManager.MATCH_SYSTEM_ONLY);
- ResolveInfo resolveInfo = getTopEmergencySosInfo(emergencyActivities);
- if (resolveInfo == null) {
- Log.wtf(TAG, "Couldn't find an app to process the emergency intent.");
- return null;
- }
- emergencyIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName,
- resolveInfo.activityInfo.name));
- emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return emergencyIntent;
- }
-
- /**
- * Select and return the "best" ResolveInfo for Emergency SOS Activity.
- */
- private @Nullable ResolveInfo getTopEmergencySosInfo(List<ResolveInfo> emergencyActivities) {
- // No matched activity.
- if (emergencyActivities == null || emergencyActivities.isEmpty()) {
- return null;
- }
-
- // Of multiple matched Activities, give preference to the pre-set package name.
- String preferredAppPackageName =
- mContext.getString(R.string.config_preferredEmergencySosPackage);
-
- // If there is no preferred app, then return first match.
- if (TextUtils.isEmpty(preferredAppPackageName)) {
- return emergencyActivities.get(0);
- }
-
- for (ResolveInfo emergencyInfo: emergencyActivities) {
- // If activity is from the preferred app, use it.
- if (TextUtils.equals(emergencyInfo.activityInfo.packageName, preferredAppPackageName)) {
- return emergencyInfo;
- }
- }
- // No matching activity: return first match
- return emergencyActivities.get(0);
- }
-
- boolean isCameraAllowedByAdmin() {
- if (mDevicePolicyManager.getCameraDisabled(null,
- mLockscreenUserManager.getCurrentUserId())) {
- return false;
- } else if (mStatusBarKeyguardViewManager == null
- || (isKeyguardShowing() && isKeyguardSecure())) {
- // Check if the admin has disabled the camera specifically for the keyguard
- return (mDevicePolicyManager.getKeyguardDisabledFeatures(null,
- mLockscreenUserManager.getCurrentUserId())
- & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
- }
- return true;
- }
+ @Nullable
+ Intent getEmergencyActionIntent();
- boolean isGoingToSleep() {
- return mWakefulnessLifecycle.getWakefulness()
- == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
- }
+ boolean isCameraAllowedByAdmin();
- boolean isWakingOrAwake() {
- return mWakefulnessLifecycle.getWakefulness() == WakefulnessLifecycle.WAKEFULNESS_WAKING
- || mWakefulnessLifecycle.getWakefulness() == WakefulnessLifecycle.WAKEFULNESS_AWAKE;
- }
+ boolean isGoingToSleep();
- public void notifyBiometricAuthModeChanged() {
- mDozeServiceHost.updateDozing();
- updateScrimController();
- }
+ void notifyBiometricAuthModeChanged();
- /**
- * Set the amount of progress we are currently in if we're transitioning to the full shade.
- * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
- * shade.
- */
- public void setTransitionToFullShadeProgress(float transitionToFullShadeProgress) {
- mTransitionToFullShadeProgress = transitionToFullShadeProgress;
- }
+ void setTransitionToFullShadeProgress(float transitionToFullShadeProgress);
- /**
- * 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.
- */
- public void setBouncerHiddenFraction(float expansion) {
- mScrimController.setBouncerHiddenFraction(expansion);
- }
+ void setBouncerHiddenFraction(float expansion);
@VisibleForTesting
- public void updateScrimController() {
- Trace.beginSection("CentralSurfaces#updateScrimController");
-
- boolean unlocking = mKeyguardStateController.isShowing() && (
- mBiometricUnlockController.isWakeAndUnlock()
- || mKeyguardStateController.isKeyguardFadingAway()
- || mKeyguardStateController.isKeyguardGoingAway()
- || mKeyguardViewMediator.requestedShowSurfaceBehindKeyguard()
- || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind());
-
- mScrimController.setExpansionAffectsAlpha(!unlocking);
-
- boolean launchingAffordanceWithPreview =
- mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
- mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
-
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
- if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
- || mTransitionToFullShadeProgress > 0f) {
- mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
- } else {
- mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
- }
- } else if (mBouncerShowing && !unlocking) {
- // 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.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
- mScrimController.transitionTo(state);
- } else if (launchingAffordanceWithPreview) {
- // We want to avoid animating when launching with a preview.
- mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
- } else if (mBrightnessMirrorVisible) {
- mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
- } else if (mState == StatusBarState.SHADE_LOCKED) {
- mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
- } else if (mDozeServiceHost.isPulsing()) {
- mScrimController.transitionTo(ScrimState.PULSING,
- mDozeScrimController.getScrimCallback());
- } else if (mDozeServiceHost.hasPendingScreenOffCallback()) {
- mScrimController.transitionTo(ScrimState.OFF, new ScrimController.Callback() {
- @Override
- public void onFinished() {
- mDozeServiceHost.executePendingScreenOffCallback();
- }
- });
- } else if (mDozing && !unlocking) {
- mScrimController.transitionTo(ScrimState.AOD);
- } else if (mKeyguardStateController.isShowing() && !isOccluded() && !unlocking) {
- mScrimController.transitionTo(ScrimState.KEYGUARD);
- } else if (mKeyguardStateController.isShowing() && mKeyguardUpdateMonitor.isDreaming()) {
- mScrimController.transitionTo(ScrimState.DREAMING);
- } else {
- mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
- }
- updateLightRevealScrimVisibility();
-
- Trace.endSection();
- }
-
- public boolean isKeyguardShowing() {
- if (mStatusBarKeyguardViewManager == null) {
- Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true");
- return true;
- }
- return mStatusBarKeyguardViewManager.isShowing();
- }
-
- public boolean shouldIgnoreTouch() {
- return (mStatusBarStateController.isDozing()
- && mDozeServiceHost.getIgnoreTouchWhilePulsing())
- || mScreenOffAnimationController.shouldIgnoreKeyguardTouches();
- }
-
- // Begin Extra BaseStatusBar methods.
-
- protected final CommandQueue mCommandQueue;
- protected IStatusBarService mBarService;
-
- // all notifications
- protected NotificationStackScrollLayout mStackScroller;
+ void updateScrimController();
- // handling reordering
- private final VisualStabilityManager mVisualStabilityManager;
+ boolean isKeyguardShowing();
- protected AccessibilityManager mAccessibilityManager;
+ boolean shouldIgnoreTouch();
- protected boolean mDeviceInteractive;
+ boolean isDeviceInteractive();
- protected boolean mVisible;
+ void setNotificationSnoozed(StatusBarNotification sbn,
+ NotificationSwipeActionHelper.SnoozeOption snoozeOption);
- // mScreenOnFromKeyguard && mVisible.
- private boolean mVisibleToUser;
+ void awakenDreams();
- protected DevicePolicyManager mDevicePolicyManager;
- private final PowerManager mPowerManager;
- protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-
- protected KeyguardManager mKeyguardManager;
- private final DeviceProvisionedController mDeviceProvisionedController;
-
- private final NavigationBarController mNavigationBarController;
- private final AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
-
- // UI-specific methods
-
- protected WindowManager mWindowManager;
- protected IWindowManager mWindowManagerService;
- private IDreamManager mDreamManager;
-
- protected Display mDisplay;
- private int mDisplayId;
-
- protected NotificationShelfController mNotificationShelfController;
-
- private final Lazy<AssistManager> mAssistManagerLazy;
-
- public boolean isDeviceInteractive() {
- return mDeviceInteractive;
- }
-
- private final BroadcastReceiver mBannerActionBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
- NotificationManager noMan = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- noMan.cancel(com.android.internal.messages.nano.SystemMessageProto.SystemMessage.
- NOTE_HIDDEN_NOTIFICATIONS);
-
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
- if (BANNER_ACTION_SETUP.equals(action)) {
- mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */);
- mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-
- );
- }
- }
- }
- };
-
- public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
- mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
- }
+ @Override
+ void startPendingIntentDismissingKeyguard(PendingIntent intent);
+ @Override
+ void startPendingIntentDismissingKeyguard(
+ PendingIntent intent, @Nullable Runnable intentSentUiThreadCallback);
- public void awakenDreams() {
- mUiBgExecutor.execute(() -> {
- try {
- mDreamManager.awaken();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- });
- }
+ @Override
+ void startPendingIntentDismissingKeyguard(PendingIntent intent,
+ Runnable intentSentUiThreadCallback, View associatedView);
- protected void toggleKeyboardShortcuts(int deviceId) {
- KeyboardShortcuts.toggle(mContext, deviceId);
- }
+ @Override
+ void startPendingIntentDismissingKeyguard(
+ PendingIntent intent, @Nullable Runnable intentSentUiThreadCallback,
+ @Nullable ActivityLaunchAnimator.Controller animationController);
- protected void dismissKeyboardShortcuts() {
- KeyboardShortcuts.dismiss();
- }
+ void clearNotificationEffects();
- /**
- * Dismiss the keyguard then execute an action.
- *
- * @param action The action to execute after dismissing the keyguard.
- * @param collapsePanel Whether we should collapse the panel after dismissing the keyguard.
- * @param willAnimateOnKeyguard Whether {@param action} will run an animation on the keyguard if
- * we are locked.
- */
- private void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone,
- boolean collapsePanel, boolean willAnimateOnKeyguard) {
- if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
-
- OnDismissAction onDismissAction = new OnDismissAction() {
- @Override
- public boolean onDismiss() {
- new Thread(() -> {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- action.run();
- }).start();
-
- return collapsePanel ? mShadeController.collapsePanel() : willAnimateOnKeyguard;
- }
+ boolean isBouncerShowing();
- @Override
- public boolean willRunAnimationOnKeyguard() {
- return willAnimateOnKeyguard;
- }
- };
- dismissKeyguardThenExecute(onDismissAction, afterKeyguardGone);
- }
+ boolean isBouncerShowingScrimmed();
- @Override
- public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
- startPendingIntentDismissingKeyguard(intent, null);
- }
+ boolean isBouncerShowingOverDream();
- @Override
- public void startPendingIntentDismissingKeyguard(
- final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) {
- startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback,
- (ActivityLaunchAnimator.Controller) null);
- }
+ void onBouncerPreHideAnimation();
- @Override
- public void startPendingIntentDismissingKeyguard(PendingIntent intent,
- Runnable intentSentUiThreadCallback, View associatedView) {
- ActivityLaunchAnimator.Controller animationController = null;
- if (associatedView instanceof ExpandableNotificationRow) {
- animationController = mNotificationAnimationProvider.getAnimatorController(
- ((ExpandableNotificationRow) associatedView));
- }
+ boolean isKeyguardSecure();
- startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback,
- animationController);
- }
+ NotificationPanelViewController getPanelController();
- @Override
- public void startPendingIntentDismissingKeyguard(
- final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback,
- @Nullable ActivityLaunchAnimator.Controller animationController) {
- final boolean willLaunchResolverActivity = intent.isActivity()
- && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
- mLockscreenUserManager.getCurrentUserId());
-
- boolean animate = !willLaunchResolverActivity
- && animationController != null
- && shouldAnimateLaunch(intent.isActivity());
-
- // If we animate, don't collapse the shade and defer the keyguard dismiss (in case we run
- // the animation on the keyguard). The animation will take care of (instantly) collapsing
- // the shade and hiding the keyguard once it is done.
- boolean collapse = !animate;
- executeActionDismissingKeyguard(() -> {
- try {
- // We wrap animationCallback with a StatusBarLaunchAnimatorController so that the
- // shade is collapsed after the animation (or when it is cancelled, aborted, etc).
- ActivityLaunchAnimator.Controller controller =
- animationController != null ? new StatusBarLaunchAnimatorController(
- animationController, this, intent.isActivity()) : null;
-
- mActivityLaunchAnimator.startPendingIntentWithAnimation(
- controller, animate, intent.getCreatorPackage(),
- (animationAdapter) -> {
- ActivityOptions options = new ActivityOptions(
- getActivityOptions(mDisplayId, animationAdapter));
- // TODO b/221255671: restrict this to only be set for notifications
- options.setEligibleForLegacyPermissionPrompt(true);
- return intent.sendAndReturnResult(null, 0, null, null, null,
- null, options.toBundle());
- });
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here.
- // Just log the exception message.
- Log.w(TAG, "Sending intent failed: " + e);
- if (!collapse) {
- // executeActionDismissingKeyguard did not collapse for us already.
- collapsePanelOnMainThread();
- }
- // TODO: Dismiss Keyguard.
- }
- if (intent.isActivity()) {
- mAssistManagerLazy.get().hideAssist();
- }
- if (intentSentUiThreadCallback != null) {
- postOnUiThread(intentSentUiThreadCallback);
- }
- }, willLaunchResolverActivity, collapse, animate);
- }
+ NotificationGutsManager getGutsManager();
- private void postOnUiThread(Runnable runnable) {
- mMainExecutor.execute(runnable);
- }
+ void updateNotificationPanelTouchState();
- /**
- * Returns an ActivityOptions bundle created using the given parameters.
- *
- * @param displayId The ID of the display to launch the activity in. Typically this would be the
- * display the status bar is on.
- * @param animationAdapter The animation adapter used to start this activity, or {@code null}
- * for the default animation.
- */
- public static Bundle getActivityOptions(int displayId,
- @Nullable RemoteAnimationAdapter animationAdapter) {
- ActivityOptions options = getDefaultActivityOptions(animationAdapter);
- options.setLaunchDisplayId(displayId);
- options.setCallerDisplayId(displayId);
- return options.toBundle();
- }
+ void makeExpandedVisible(boolean force);
- /**
- * Returns an ActivityOptions bundle created using the given parameters.
- *
- * @param displayId The ID of the display to launch the activity in. Typically this would be the
- * display the status bar is on.
- * @param animationAdapter The animation adapter used to start this activity, or {@code null}
- * for the default animation.
- * @param isKeyguardShowing Whether keyguard is currently showing.
- * @param eventTime The event time in milliseconds since boot, not including sleep. See
- * {@link ActivityOptions#setSourceInfo}.
- */
- public static Bundle getActivityOptions(int displayId,
- @Nullable RemoteAnimationAdapter animationAdapter, boolean isKeyguardShowing,
- long eventTime) {
- ActivityOptions options = getDefaultActivityOptions(animationAdapter);
- options.setSourceInfo(isKeyguardShowing ? ActivityOptions.SourceInfo.TYPE_LOCKSCREEN
- : ActivityOptions.SourceInfo.TYPE_NOTIFICATION, eventTime);
- options.setLaunchDisplayId(displayId);
- options.setCallerDisplayId(displayId);
- return options.toBundle();
- }
+ void instantCollapseNotificationPanel();
- public static ActivityOptions getDefaultActivityOptions(
- @Nullable RemoteAnimationAdapter animationAdapter) {
- ActivityOptions options;
- if (animationAdapter != null) {
- if (ENABLE_SHELL_TRANSITIONS) {
- options = ActivityOptions.makeRemoteTransition(
- RemoteTransitionAdapter.adaptRemoteAnimation(animationAdapter));
- } else {
- options = ActivityOptions.makeRemoteAnimation(animationAdapter);
- }
- } else {
- options = ActivityOptions.makeBasic();
- }
- options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR);
- return options;
- }
+ void visibilityChanged(boolean visible);
- void visibilityChanged(boolean visible) {
- if (mVisible != visible) {
- mVisible = visible;
- if (!visible) {
- mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
- }
- }
- updateVisibleToUser();
- }
+ int getDisplayId();
- protected void updateVisibleToUser() {
- boolean oldVisibleToUser = mVisibleToUser;
- mVisibleToUser = mVisible && mDeviceInteractive;
+ int getRotation();
- if (oldVisibleToUser != mVisibleToUser) {
- handleVisibleToUserChanged(mVisibleToUser);
- }
- }
+ @VisibleForTesting
+ void setBarStateForTest(int state);
- /**
- * Clear Buzz/Beep/Blink.
- */
- public void clearNotificationEffects() {
- try {
- mBarService.clearNotificationEffects();
- } catch (RemoteException e) {
- // Won't fail unless the world has ended.
- }
- }
+ void wakeUpForFullScreenIntent();
- /**
- * @return Whether the security bouncer from Keyguard is showing.
- */
- public boolean isBouncerShowing() {
- return mBouncerShowing;
- }
+ void showTransientUnchecked();
- /**
- * @return Whether the security bouncer from Keyguard is showing.
- */
- public boolean isBouncerShowingScrimmed() {
- return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming();
- }
+ void clearTransient();
- public boolean isBouncerShowingOverDream() {
- return isBouncerShowing() && mDreamOverlayStateController.isOverlayActive();
- }
+ void acquireGestureWakeLock(long time);
- /**
- * When {@link KeyguardBouncer} starts to be dismissed, playing its animation.
- */
- public void onBouncerPreHideAnimation() {
- mNotificationPanelViewController.onBouncerPreHideAnimation();
+ boolean setAppearance(int appearance);
- }
+ int getBarMode();
- /**
- * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
- * return PackageManager for mContext
- */
- public static PackageManager getPackageManagerForUser(Context context, int userId) {
- Context contextForUser = context;
- // UserHandle defines special userId as negative values, e.g. USER_ALL
- if (userId >= 0) {
- try {
- // Create a context for the correct user so if a package isn't installed
- // for user 0 we can still load information about the package.
- contextForUser =
- context.createPackageContextAsUser(context.getPackageName(),
- Context.CONTEXT_RESTRICTED,
- new UserHandle(userId));
- } catch (NameNotFoundException e) {
- // Shouldn't fail to find the package name for system ui.
- }
- }
- return contextForUser.getPackageManager();
- }
+ void resendMessage(int msg);
- public boolean isKeyguardSecure() {
- if (mStatusBarKeyguardViewManager == null) {
- // startKeyguard() hasn't been called yet, so we don't know.
- // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
- // value onVisibilityChanged().
- Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
- new Throwable());
- return false;
- }
- return mStatusBarKeyguardViewManager.isSecure();
- }
- public NotificationPanelViewController getPanelController() {
- return mNotificationPanelViewController;
- }
- // End Extra BaseStatusBarMethods.
+ void resendMessage(Object msg);
- public NotificationGutsManager getGutsManager() {
- return mGutsManager;
- }
+ int getDisabled1();
- boolean isTransientShown() {
- return mTransientShown;
- }
+ void setDisabled1(int disabled);
- private void updateLightRevealScrimVisibility() {
- if (mLightRevealScrim == null) {
- // status bar may not be inflated yet
- return;
- }
+ int getDisabled2();
- mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
- }
+ void setDisabled2(int disabled);
- private final KeyguardUpdateMonitorCallback mUpdateCallback =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onDreamingStateChanged(boolean dreaming) {
- updateScrimController();
- if (dreaming) {
- maybeEscalateHeadsUp();
- }
- }
-
- // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
- // KeyguardCoordinator
- @Override
- public void onStrongAuthStateChanged(int userId) {
- super.onStrongAuthStateChanged(userId);
- mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged");
- }
- };
-
-
- private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener =
- new FalsingManager.FalsingBeliefListener() {
- @Override
- public void onFalse() {
- // Hides quick settings, bouncer, and quick-quick settings.
- mStatusBarKeyguardViewManager.reset(true);
- }
- };
-
- // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over,
- // this animation is tied to the scrim for historic reasons.
- // TODO: notify when keyguard has faded away instead of the scrim.
- private final ScrimController.Callback mUnlockScrimCallback = new ScrimController
- .Callback() {
- @Override
- public void onFinished() {
- if (mStatusBarKeyguardViewManager == null) {
- Log.w(TAG, "Tried to notify keyguard visibility when "
- + "mStatusBarKeyguardViewManager was null");
- return;
- }
- if (mKeyguardStateController.isKeyguardFadingAway()) {
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
- }
- }
+ void setLastCameraLaunchSource(int source);
- @Override
- public void onCancelled() {
- onFinished();
- }
- };
-
- private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
- @Override
- public void onUserSetupChanged() {
- final boolean userSetup = mDeviceProvisionedController.isCurrentUserSetup();
- Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for "
- + "current user");
- if (MULTIUSER_DEBUG) {
- Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s",
- userSetup, mUserSetup));
- }
+ void setLaunchCameraOnFinishedGoingToSleep(boolean launch);
- if (userSetup != mUserSetup) {
- mUserSetup = userSetup;
- if (!mUserSetup) {
- animateCollapseQuickSettings();
- }
- if (mNotificationPanelViewController != null) {
- mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
- }
- updateQsExpansionEnabled();
- }
- }
- };
-
- private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!mWallpaperSupported) {
- // Receiver should not have been registered at all...
- Log.wtf(TAG, "WallpaperManager not supported");
- return;
- }
- WallpaperInfo info = mWallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
- mWallpaperController.onWallpaperInfoUpdated(info);
-
- final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
- // If WallpaperInfo is null, it must be ImageWallpaper.
- final boolean supportsAmbientMode = deviceSupportsAodWallpaper
- && (info != null && info.supportsAmbientMode());
-
- mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
- mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
- mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode);
- }
- };
+ void setLaunchCameraOnFinishedWaking(boolean launch);
- private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
- @Override
- public void onConfigChanged(Configuration newConfig) {
- updateResources();
- updateDisplaySize(); // populates mDisplayMetrics
+ void setLaunchEmergencyActionOnFinishedGoingToSleep(boolean launch);
- if (DEBUG) {
- Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
- }
+ void setLaunchEmergencyActionOnFinishedWaking(boolean launch);
- if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
- mViewHierarchyManager.updateRowStates();
- }
- mScreenPinningRequest.onConfigurationChanged();
- }
+ void setTopHidesStatusBar(boolean hides);
- @Override
- public void onDensityOrFontScaleChanged() {
- // TODO: Remove this.
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.onDensityOrFontScaleChanged();
- }
- // TODO: Bring these out of CentralSurfaces.
- mUserInfoControllerImpl.onDensityOrFontScaleChanged();
- mUserSwitcherController.onDensityOrFontScaleChanged();
- mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
- mHeadsUpManager.onDensityOrFontScaleChanged();
- }
+ QSPanelController getQSPanelController();
- @Override
- public void onThemeChanged() {
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.onOverlayChanged();
- }
- // We need the new R.id.keyguard_indication_area before recreating
- // mKeyguardIndicationController
- mNotificationPanelViewController.onThemeChanged();
+ boolean areNotificationAlertsDisabled();
- if (mStatusBarKeyguardViewManager != null) {
- mStatusBarKeyguardViewManager.onThemeChanged();
- }
- if (mAmbientIndicationContainer instanceof AutoReinflateContainer) {
- ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout();
- }
- mNotificationIconAreaController.onThemeChanged();
- }
+ float getDisplayDensity();
- @Override
- public void onUiModeChanged() {
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.onUiModeChanged();
- }
- }
- };
-
- private StatusBarStateController.StateListener mStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onStatePreChange(int oldState, int newState) {
- // If we're visible and switched to SHADE_LOCKED (the user dragged
- // down on the lockscreen), clear notification LED, vibration,
- // ringing.
- // Other transitions are covered in handleVisibleToUserChanged().
- if (mVisible && (newState == StatusBarState.SHADE_LOCKED
- || mStatusBarStateController.goingToFullShade())) {
- clearNotificationEffects();
- }
- if (newState == StatusBarState.KEYGUARD) {
- mRemoteInputManager.onPanelCollapsed();
- maybeEscalateHeadsUp();
- }
- }
-
- @Override
- public void onStateChanged(int newState) {
- mState = newState;
- updateReportRejectedTouchVisibility();
- mDozeServiceHost.updateDozing();
- updateTheme();
- mNavigationBarController.touchAutoDim(mDisplayId);
- Trace.beginSection("CentralSurfaces#updateKeyguardState");
- if (mState == StatusBarState.KEYGUARD) {
- mNotificationPanelViewController.cancelPendingPanelCollapse();
- }
- updateDozingState();
- checkBarModes();
- updateScrimController();
- mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
- Trace.endSection();
- }
-
- @Override
- public void onDozeAmountChanged(float linear, float eased) {
- if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
- && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
- mLightRevealScrim.setRevealAmount(1f - linear);
- }
- }
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- Trace.beginSection("CentralSurfaces#updateDozing");
- mDozing = isDozing;
-
- // Collapse the notification panel if open
- boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
- && mDozeParameters.shouldControlScreenOff();
- mNotificationPanelViewController.resetViews(dozingAnimated);
-
- updateQsExpansionEnabled();
- mKeyguardViewMediator.setDozing(mDozing);
-
- mNotificationsController.requestNotificationUpdate("onDozingChanged");
- updateDozingState();
- mDozeServiceHost.updateDozing();
- updateScrimController();
- updateReportRejectedTouchVisibility();
- Trace.endSection();
- }
-
- @Override
- public void onFullscreenStateChanged(boolean isFullscreen) {
- mIsFullscreen = isFullscreen;
- maybeUpdateBarMode();
- }
- };
-
- private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
- new BatteryController.BatteryStateChangeCallback() {
- @Override
- public void onPowerSaveChanged(boolean isPowerSave) {
- mMainExecutor.execute(mCheckBarModes);
- if (mDozeServiceHost != null) {
- mDozeServiceHost.firePowerSaveChanged(isPowerSave);
- }
- }
- };
-
- private final ActivityLaunchAnimator.Callback mActivityLaunchAnimatorCallback =
- new ActivityLaunchAnimator.Callback() {
- @Override
- public boolean isOnKeyguard() {
- return mKeyguardStateController.isShowing();
- }
-
- @Override
- public void hideKeyguardWithAnimation(IRemoteAnimationRunner runner) {
- // We post to the main thread for 2 reasons:
- // 1. KeyguardViewMediator is not thread-safe.
- // 2. To ensure that ViewMediatorCallback#keyguardDonePending is called before
- // ViewMediatorCallback#readyForKeyguardDone. The wrong order could occur
- // when doing
- // dismissKeyguardThenExecute { hideKeyguardWithAnimation(runner) }.
- mMainExecutor.execute(() -> mKeyguardViewMediator.hideWithAnimation(runner));
- }
-
- @Override
- public int getBackgroundColor(TaskInfo task) {
- if (!mStartingSurfaceOptional.isPresent()) {
- Log.w(TAG, "No starting surface, defaulting to SystemBGColor");
- return SplashscreenContentDrawer.getSystemBGColor();
- }
-
- return mStartingSurfaceOptional.get().getBackgroundColor(task);
- }
- };
-
- private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener =
- new ActivityLaunchAnimator.Listener() {
- @Override
- public void onLaunchAnimationStart() {
- mKeyguardViewMediator.setBlursDisabledForAppLaunch(true);
- }
-
- @Override
- public void onLaunchAnimationEnd() {
- mKeyguardViewMediator.setBlursDisabledForAppLaunch(false);
- }
- };
-
- private final DemoMode mDemoModeCallback = new DemoMode() {
- @Override
- public void onDemoModeFinished() {
- checkBarModes();
- }
+ void extendDozePulse();
- @Override
- public void dispatchDemoCommand(String command, Bundle args) { }
- };
+ public static class KeyboardShortcutsMessage {
+ final int mDeviceId;
- /**
- * Determines what UserHandle to use when launching an activity.
- *
- * We want to ensure that activities that are launched within the systemui process should be
- * launched as user of the current process.
- * @param intent
- * @return UserHandle
- */
- private UserHandle getActivityUserHandle(Intent intent) {
- String[] packages = mContext.getResources().getStringArray(R.array.system_ui_packages);
- for (String pkg : packages) {
- if (intent.getComponent() == null) break;
- if (pkg.equals(intent.getComponent().getPackageName())) {
- return new UserHandle(UserHandle.myUserId());
- }
+ KeyboardShortcutsMessage(int deviceId) {
+ mDeviceId = deviceId;
}
- return UserHandle.CURRENT;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
new file mode 100644
index 000000000000..e4efb98528fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -0,0 +1,4551 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.WindowVisibleState;
+import static android.app.StatusBarManager.windowStateToString;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.containsType;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
+
+import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
+import static androidx.lifecycle.Lifecycle.State.RESUMED;
+
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.IWallpaperManager;
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.app.TaskInfo;
+import android.app.TaskStackBuilder;
+import android.app.UiModeManager;
+import android.app.WallpaperInfo;
+import android.app.WallpaperManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentCallbacks2;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.hardware.devicestate.DeviceStateManager;
+import android.metrics.LogMaker;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.DisplayMetrics;
+import android.util.EventLog;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.Slog;
+import android.view.Display;
+import android.view.IRemoteAnimationRunner;
+import android.view.IWindowManager;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ThreadedRenderer;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.DateTimeView;
+import android.window.SplashScreen;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleRegistry;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.colorextraction.ColorExtractor;
+import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.ActivityIntentHelper;
+import com.android.systemui.AutoReinflateContainer;
+import com.android.systemui.CoreStartable;
+import com.android.systemui.DejankUtils;
+import com.android.systemui.EventLogTags;
+import com.android.systemui.InitController;
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
+import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.DelegateLaunchAnimatorController;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.camera.CameraIntents;
+import com.android.systemui.charging.WirelessChargingAnimation;
+import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.demomode.DemoMode;
+import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.emergency.EmergencyGesture;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.fragments.ExtensionFragmentListener;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.keyguard.KeyguardService;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.navigationbar.NavigationBarController;
+import com.android.systemui.navigationbar.NavigationBarView;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.OverlayPlugin;
+import com.android.systemui.plugins.PluginDependencyProvider;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+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.scrim.ScrimView;
+import com.android.systemui.settings.brightness.BrightnessSliderController;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.AutoHideUiElement;
+import com.android.systemui.statusbar.BackDropView;
+import com.android.systemui.statusbar.CircleReveal;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.KeyboardShortcuts;
+import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LiftReveal;
+import com.android.systemui.statusbar.LightRevealScrim;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.PowerButtonReveal;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.charging.WiredChargingRippleController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+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.util.DumpUtilsKt;
+import com.android.systemui.util.WallpaperController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.MessageRouter;
+import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.wmshell.BubblesManager;
+import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
+import com.android.wm.shell.startingsurface.StartingSurface;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import dagger.Lazy;
+
+/**
+ * A class handling initialization and coordination between some of the key central surfaces in
+ * System UI: The notification shade, the keyguard (lockscreen), and the status bar.
+ *
+ * This class is not our ideal architecture because it doesn't enforce much isolation between these
+ * three mostly disparate surfaces. In an ideal world, this class would not exist. Instead, we would
+ * break it up into three modules -- one for each of those three surfaces -- and we would define any
+ * APIs that are needed for these surfaces to communicate with each other when necessary.
+ *
+ * <b>If at all possible, please avoid adding additional code to this monstrous class! Our goal is
+ * to break up this class into many small classes, and any code added here will slow down that goal.
+ * </b>
+ */
+@SysUISingleton
+public class CentralSurfacesImpl extends CoreStartable implements
+ CentralSurfaces {
+
+ private static final String BANNER_ACTION_CANCEL =
+ "com.android.systemui.statusbar.banner_action_cancel";
+ private static final String BANNER_ACTION_SETUP =
+ "com.android.systemui.statusbar.banner_action_setup";
+
+ private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
+ private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003;
+ // 1020-1040 reserved for BaseStatusBar
+
+ /**
+ * The delay to reset the hint text when the hint animation is finished running.
+ */
+ private static final int HINT_RESET_DELAY_MS = 1200;
+
+ private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
+
+ /**
+ * If true, the system is in the half-boot-to-decryption-screen state.
+ * Prudently disable QS and notifications.
+ */
+ public static final boolean ONLY_CORE_APPS;
+
+ static {
+ boolean onlyCoreApps;
+ try {
+ IPackageManager packageManager =
+ IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ onlyCoreApps = packageManager != null && packageManager.isOnlyCoreApps();
+ } catch (RemoteException e) {
+ onlyCoreApps = false;
+ }
+ ONLY_CORE_APPS = onlyCoreApps;
+ }
+
+ private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
+ private float mTransitionToFullShadeProgress = 0f;
+ private NotificationListContainer mNotifListContainer;
+
+ private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ boolean occluded = mKeyguardStateController.isOccluded();
+ mStatusBarHideIconsForBouncerManager.setIsOccludedAndTriggerUpdate(occluded);
+ mScrimController.setKeyguardOccluded(occluded);
+ }
+ };
+
+ void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
+ updateBubblesVisibility();
+ mStatusBarWindowState = state;
+ }
+
+ @Override
+ public void acquireGestureWakeLock(long time) {
+ mGestureWakeLock.acquire(time);
+ }
+
+ @Override
+ public boolean setAppearance(int appearance) {
+ if (mAppearance != appearance) {
+ mAppearance = appearance;
+ return updateBarMode(barMode(isTransientShown(), appearance));
+ }
+
+ return false;
+ }
+
+ @Override
+ public int getBarMode() {
+ return mStatusBarMode;
+ }
+
+ @Override
+ public void resendMessage(int msg) {
+ mMessageRouter.cancelMessages(msg);
+ mMessageRouter.sendMessage(msg);
+ }
+
+ @Override
+ public void resendMessage(Object msg) {
+ mMessageRouter.cancelMessages(msg.getClass());
+ mMessageRouter.sendMessage(msg);
+ }
+
+ @Override
+ public int getDisabled1() {
+ return mDisabled1;
+ }
+
+ @Override
+ public void setDisabled1(int disabled) {
+ mDisabled1 = disabled;
+ }
+
+ @Override
+ public int getDisabled2() {
+ return mDisabled2;
+ }
+
+ @Override
+ public void setDisabled2(int disabled) {
+ mDisabled2 = disabled;
+ }
+
+ @Override
+ public void setLastCameraLaunchSource(int source) {
+ mLastCameraLaunchSource = source;
+ }
+
+ @Override
+ public void setLaunchCameraOnFinishedGoingToSleep(boolean launch) {
+ mLaunchCameraOnFinishedGoingToSleep = launch;
+ }
+
+ @Override
+ public void setLaunchCameraOnFinishedWaking(boolean launch) {
+ mLaunchCameraWhenFinishedWaking = launch;
+ }
+
+ @Override
+ public void setLaunchEmergencyActionOnFinishedGoingToSleep(boolean launch) {
+ mLaunchEmergencyActionOnFinishedGoingToSleep = launch;
+ }
+
+ @Override
+ public void setLaunchEmergencyActionOnFinishedWaking(boolean launch) {
+ mLaunchEmergencyActionWhenFinishedWaking = launch;
+ }
+
+ @Override
+ public void setTopHidesStatusBar(boolean hides) {
+ mTopHidesStatusBar = hides;
+ }
+
+ @Override
+ public QSPanelController getQSPanelController() {
+ return mQSPanelController;
+ }
+
+ /** */
+ @Override
+ public void animateExpandNotificationsPanel() {
+ mCommandQueueCallbacks.animateExpandNotificationsPanel();
+ }
+
+ /** */
+ @Override
+ public void animateExpandSettingsPanel(@Nullable String subpanel) {
+ mCommandQueueCallbacks.animateExpandSettingsPanel(subpanel);
+ }
+
+ /** */
+ @Override
+ public void animateCollapsePanels(int flags, boolean force) {
+ mCommandQueueCallbacks.animateCollapsePanels(flags, force);
+ }
+
+ /** */
+ @Override
+ public void togglePanel() {
+ mCommandQueueCallbacks.togglePanel();
+ }
+ /**
+ * The {@link StatusBarState} of the status bar.
+ */
+ protected int mState; // TODO: remove this. Just use StatusBarStateController
+ protected boolean mBouncerShowing;
+
+ private final PhoneStatusBarPolicy mIconPolicy;
+
+ private final VolumeComponent mVolumeComponent;
+ private BrightnessMirrorController mBrightnessMirrorController;
+ private boolean mBrightnessMirrorVisible;
+ private BiometricUnlockController mBiometricUnlockController;
+ private final LightBarController mLightBarController;
+ private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
+ private final LockscreenGestureLogger mLockscreenGestureLogger;
+ @Nullable
+ protected LockscreenWallpaper mLockscreenWallpaper;
+ private final AutoHideController mAutoHideController;
+
+ private final Point mCurrentDisplaySize = new Point();
+
+ protected NotificationShadeWindowView mNotificationShadeWindowView;
+ protected PhoneStatusBarView mStatusBarView;
+ private PhoneStatusBarViewController mPhoneStatusBarViewController;
+ private PhoneStatusBarTransitions mStatusBarTransitions;
+ private AuthRippleController mAuthRippleController;
+ @WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
+ protected final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final StatusBarWindowController mStatusBarWindowController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @VisibleForTesting
+ DozeServiceHost mDozeServiceHost;
+ private boolean mWakeUpComingFromTouch;
+ private PointF mWakeUpTouchLocation;
+ private LightRevealScrim mLightRevealScrim;
+ private PowerButtonReveal mPowerButtonReveal;
+
+ private final Object mQueueLock = new Object();
+
+ private final PulseExpansionHandler mPulseExpansionHandler;
+ private final NotificationWakeUpCoordinator mWakeUpCoordinator;
+ private final KeyguardBypassController mKeyguardBypassController;
+ private final KeyguardStateController mKeyguardStateController;
+ private final HeadsUpManagerPhone mHeadsUpManager;
+ private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ private final DynamicPrivacyController mDynamicPrivacyController;
+ private final FalsingCollector mFalsingCollector;
+ private final FalsingManager mFalsingManager;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final ConfigurationController mConfigurationController;
+ protected NotificationShadeWindowViewController mNotificationShadeWindowViewController;
+ private final DozeParameters mDozeParameters;
+ private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+ private final CentralSurfacesComponent.Factory mCentralSurfacesComponentFactory;
+ private final PluginManager mPluginManager;
+ private final ShadeController mShadeController;
+ private final InitController mInitController;
+
+ private final PluginDependencyProvider mPluginDependencyProvider;
+ private final KeyguardDismissUtil mKeyguardDismissUtil;
+ private final ExtensionController mExtensionController;
+ private final UserInfoControllerImpl mUserInfoControllerImpl;
+ private final DemoModeController mDemoModeController;
+ private final NotificationsController mNotificationsController;
+ private final OngoingCallController mOngoingCallController;
+ private final StatusBarSignalPolicy mStatusBarSignalPolicy;
+ private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
+
+ // expanded notifications
+ // the sliding/resizing panel within the notification window
+ protected NotificationPanelViewController mNotificationPanelViewController;
+
+ // settings
+ private QSPanelController mQSPanelController;
+
+ KeyguardIndicationController mKeyguardIndicationController;
+
+ private View mReportRejectedTouch;
+
+ private boolean mExpandedVisible;
+
+ private final int[] mAbsPos = new int[2];
+
+ private final NotifShadeEventSource mNotifShadeEventSource;
+ protected final NotificationEntryManager mEntryManager;
+ private final NotificationGutsManager mGutsManager;
+ private final NotificationLogger mNotificationLogger;
+ private final NotificationViewHierarchyManager mViewHierarchyManager;
+ private final PanelExpansionStateManager mPanelExpansionStateManager;
+ private final KeyguardViewMediator mKeyguardViewMediator;
+ protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ private final BrightnessSliderController.Factory mBrightnessSliderFactory;
+ private final FeatureFlags mFeatureFlags;
+ private final FragmentService mFragmentService;
+ private final ScreenOffAnimationController mScreenOffAnimationController;
+ private final WallpaperController mWallpaperController;
+ private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ private final MessageRouter mMessageRouter;
+ private final WallpaperManager mWallpaperManager;
+
+ private CentralSurfacesComponent mCentralSurfacesComponent;
+
+ // Flags for disabling the status bar
+ // Two variables becaseu the first one evidently ran out of room for new flags.
+ private int mDisabled1 = 0;
+ private int mDisabled2 = 0;
+
+ /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
+ private @Appearance int mAppearance;
+
+ private boolean mTransientShown;
+
+ private final DisplayMetrics mDisplayMetrics;
+
+ // XXX: gesture research
+ private final GestureRecorder mGestureRec = DEBUG_GESTURES
+ ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
+ : null;
+
+ private final ScreenPinningRequest mScreenPinningRequest;
+
+ private final MetricsLogger mMetricsLogger;
+
+ // ensure quick settings is disabled until the current user makes it through the setup wizard
+ @VisibleForTesting
+ protected boolean mUserSetup = false;
+
+ @VisibleForTesting
+ public enum StatusBarUiEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Secured lockscreen is opened.")
+ LOCKSCREEN_OPEN_SECURE(405),
+
+ @UiEvent(doc = "Lockscreen without security is opened.")
+ LOCKSCREEN_OPEN_INSECURE(406),
+
+ @UiEvent(doc = "Secured lockscreen is closed.")
+ LOCKSCREEN_CLOSE_SECURE(407),
+
+ @UiEvent(doc = "Lockscreen without security is closed.")
+ LOCKSCREEN_CLOSE_INSECURE(408),
+
+ @UiEvent(doc = "Secured bouncer is opened.")
+ BOUNCER_OPEN_SECURE(409),
+
+ @UiEvent(doc = "Bouncer without security is opened.")
+ BOUNCER_OPEN_INSECURE(410),
+
+ @UiEvent(doc = "Secured bouncer is closed.")
+ BOUNCER_CLOSE_SECURE(411),
+
+ @UiEvent(doc = "Bouncer without security is closed.")
+ BOUNCER_CLOSE_INSECURE(412);
+
+ private final int mId;
+
+ StatusBarUiEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+
+ private Handler mMainHandler;
+ private final DelayableExecutor mMainExecutor;
+
+ private int mInteractingWindows;
+ private @TransitionMode int mStatusBarMode;
+
+ private final ViewMediatorCallback mKeyguardViewMediatorCallback;
+ private final ScrimController mScrimController;
+ protected DozeScrimController mDozeScrimController;
+ private final Executor mUiBgExecutor;
+
+ protected boolean mDozing;
+ private boolean mIsFullscreen;
+
+ boolean mCloseQsBeforeScreenOff;
+
+ private final NotificationMediaManager mMediaManager;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final NotificationRemoteInputManager mRemoteInputManager;
+ private boolean mWallpaperSupported;
+
+ private Runnable mLaunchTransitionEndRunnable;
+ private Runnable mLaunchTransitionCancelRunnable;
+ private boolean mLaunchCameraWhenFinishedWaking;
+ private boolean mLaunchCameraOnFinishedGoingToSleep;
+ private boolean mLaunchEmergencyActionWhenFinishedWaking;
+ private boolean mLaunchEmergencyActionOnFinishedGoingToSleep;
+ private int mLastCameraLaunchSource;
+ protected PowerManager.WakeLock mGestureWakeLock;
+
+ private final int[] mTmpInt2 = new int[2];
+
+ // Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
+ private int mLastLoggedStateFingerprint;
+ private boolean mTopHidesStatusBar;
+ private boolean mStatusBarWindowHidden;
+ private boolean mIsLaunchingActivityOverLockscreen;
+
+ private final UserSwitcherController mUserSwitcherController;
+ private final NetworkController mNetworkController;
+ private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+ protected final BatteryController mBatteryController;
+ protected boolean mPanelExpanded;
+ private UiModeManager mUiModeManager;
+ private LogMaker mStatusBarStateLog;
+ protected final NotificationIconAreaController mNotificationIconAreaController;
+ @Nullable private View mAmbientIndicationContainer;
+ private final SysuiColorExtractor mColorExtractor;
+ private final ScreenLifecycle mScreenLifecycle;
+ private final WakefulnessLifecycle mWakefulnessLifecycle;
+
+ private boolean mNoAnimationOnNextBarModeChange;
+ private final SysuiStatusBarStateController mStatusBarStateController;
+
+ private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
+ protected NotificationPresenter mPresenter;
+ private NotificationActivityStarter mNotificationActivityStarter;
+ private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
+ private final Optional<BubblesManager> mBubblesManagerOptional;
+ private final Optional<Bubbles> mBubblesOptional;
+ private final Bubbles.BubbleExpandListener mBubbleExpandListener;
+ private final Optional<StartingSurface> mStartingSurfaceOptional;
+ private final NotifPipelineFlags mNotifPipelineFlags;
+
+ private final ActivityIntentHelper mActivityIntentHelper;
+ private NotificationStackScrollLayoutController mStackScrollerController;
+
+ private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
+ (extractor, which) -> updateTheme();
+
+ private final InteractionJankMonitor mJankMonitor;
+
+
+ /**
+ * Public constructor for CentralSurfaces.
+ *
+ * CentralSurfaces is considered optional, and therefore can not be marked as @Inject directly.
+ * Instead, an @Provide method is included. See {@link StatusBarPhoneModule}.
+ */
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+ @Inject
+ public CentralSurfacesImpl(
+ Context context,
+ NotificationsController notificationsController,
+ FragmentService fragmentService,
+ LightBarController lightBarController,
+ AutoHideController autoHideController,
+ StatusBarWindowController statusBarWindowController,
+ StatusBarWindowStateController statusBarWindowStateController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ StatusBarSignalPolicy statusBarSignalPolicy,
+ PulseExpansionHandler pulseExpansionHandler,
+ NotificationWakeUpCoordinator notificationWakeUpCoordinator,
+ KeyguardBypassController keyguardBypassController,
+ KeyguardStateController keyguardStateController,
+ HeadsUpManagerPhone headsUpManagerPhone,
+ DynamicPrivacyController dynamicPrivacyController,
+ FalsingManager falsingManager,
+ FalsingCollector falsingCollector,
+ BroadcastDispatcher broadcastDispatcher,
+ NotifShadeEventSource notifShadeEventSource,
+ NotificationEntryManager notificationEntryManager,
+ NotificationGutsManager notificationGutsManager,
+ NotificationLogger notificationLogger,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
+ NotificationViewHierarchyManager notificationViewHierarchyManager,
+ PanelExpansionStateManager panelExpansionStateManager,
+ KeyguardViewMediator keyguardViewMediator,
+ DisplayMetrics displayMetrics,
+ MetricsLogger metricsLogger,
+ @UiBackground Executor uiBgExecutor,
+ NotificationMediaManager notificationMediaManager,
+ NotificationLockscreenUserManager lockScreenUserManager,
+ NotificationRemoteInputManager remoteInputManager,
+ UserSwitcherController userSwitcherController,
+ NetworkController networkController,
+ BatteryController batteryController,
+ SysuiColorExtractor colorExtractor,
+ ScreenLifecycle screenLifecycle,
+ WakefulnessLifecycle wakefulnessLifecycle,
+ SysuiStatusBarStateController statusBarStateController,
+ Optional<BubblesManager> bubblesManagerOptional,
+ Optional<Bubbles> bubblesOptional,
+ VisualStabilityManager visualStabilityManager,
+ DeviceProvisionedController deviceProvisionedController,
+ NavigationBarController navigationBarController,
+ AccessibilityFloatingMenuController accessibilityFloatingMenuController,
+ Lazy<AssistManager> assistManagerLazy,
+ ConfigurationController configurationController,
+ NotificationShadeWindowController notificationShadeWindowController,
+ DozeParameters dozeParameters,
+ ScrimController scrimController,
+ Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+ LockscreenGestureLogger lockscreenGestureLogger,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ DozeServiceHost dozeServiceHost,
+ PowerManager powerManager,
+ ScreenPinningRequest screenPinningRequest,
+ DozeScrimController dozeScrimController,
+ VolumeComponent volumeComponent,
+ CommandQueue commandQueue,
+ CentralSurfacesComponent.Factory centralSurfacesComponentFactory,
+ PluginManager pluginManager,
+ ShadeController shadeController,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ ViewMediatorCallback viewMediatorCallback,
+ InitController initController,
+ @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
+ PluginDependencyProvider pluginDependencyProvider,
+ KeyguardDismissUtil keyguardDismissUtil,
+ ExtensionController extensionController,
+ UserInfoControllerImpl userInfoControllerImpl,
+ PhoneStatusBarPolicy phoneStatusBarPolicy,
+ KeyguardIndicationController keyguardIndicationController,
+ DemoModeController demoModeController,
+ Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
+ StatusBarTouchableRegionManager statusBarTouchableRegionManager,
+ NotificationIconAreaController notificationIconAreaController,
+ BrightnessSliderController.Factory brightnessSliderFactory,
+ ScreenOffAnimationController screenOffAnimationController,
+ WallpaperController wallpaperController,
+ OngoingCallController ongoingCallController,
+ StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
+ LockscreenShadeTransitionController lockscreenShadeTransitionController,
+ FeatureFlags featureFlags,
+ KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+ @Main Handler mainHandler,
+ @Main DelayableExecutor delayableExecutor,
+ @Main MessageRouter messageRouter,
+ WallpaperManager wallpaperManager,
+ Optional<StartingSurface> startingSurfaceOptional,
+ ActivityLaunchAnimator activityLaunchAnimator,
+ NotifPipelineFlags notifPipelineFlags,
+ InteractionJankMonitor jankMonitor,
+ DeviceStateManager deviceStateManager,
+ DreamOverlayStateController dreamOverlayStateController,
+ WiredChargingRippleController wiredChargingRippleController) {
+ super(context);
+ mNotificationsController = notificationsController;
+ mFragmentService = fragmentService;
+ mLightBarController = lightBarController;
+ mAutoHideController = autoHideController;
+ mStatusBarWindowController = statusBarWindowController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mPulseExpansionHandler = pulseExpansionHandler;
+ mWakeUpCoordinator = notificationWakeUpCoordinator;
+ mKeyguardBypassController = keyguardBypassController;
+ mKeyguardStateController = keyguardStateController;
+ mHeadsUpManager = headsUpManagerPhone;
+ mKeyguardIndicationController = keyguardIndicationController;
+ mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
+ mDynamicPrivacyController = dynamicPrivacyController;
+ mFalsingCollector = falsingCollector;
+ mFalsingManager = falsingManager;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mNotifShadeEventSource = notifShadeEventSource;
+ mEntryManager = notificationEntryManager;
+ mGutsManager = notificationGutsManager;
+ mNotificationLogger = notificationLogger;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
+ mViewHierarchyManager = notificationViewHierarchyManager;
+ mPanelExpansionStateManager = panelExpansionStateManager;
+ mKeyguardViewMediator = keyguardViewMediator;
+ mDisplayMetrics = displayMetrics;
+ mMetricsLogger = metricsLogger;
+ mUiBgExecutor = uiBgExecutor;
+ mMediaManager = notificationMediaManager;
+ mLockscreenUserManager = lockScreenUserManager;
+ mRemoteInputManager = remoteInputManager;
+ mUserSwitcherController = userSwitcherController;
+ mNetworkController = networkController;
+ mBatteryController = batteryController;
+ mColorExtractor = colorExtractor;
+ mScreenLifecycle = screenLifecycle;
+ mWakefulnessLifecycle = wakefulnessLifecycle;
+ mStatusBarStateController = statusBarStateController;
+ mBubblesManagerOptional = bubblesManagerOptional;
+ mBubblesOptional = bubblesOptional;
+ mVisualStabilityManager = visualStabilityManager;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mNavigationBarController = navigationBarController;
+ mAccessibilityFloatingMenuController = accessibilityFloatingMenuController;
+ mAssistManagerLazy = assistManagerLazy;
+ mConfigurationController = configurationController;
+ mNotificationShadeWindowController = notificationShadeWindowController;
+ mDozeServiceHost = dozeServiceHost;
+ mPowerManager = powerManager;
+ mDozeParameters = dozeParameters;
+ mScrimController = scrimController;
+ mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
+ mLockscreenGestureLogger = lockscreenGestureLogger;
+ mScreenPinningRequest = screenPinningRequest;
+ mDozeScrimController = dozeScrimController;
+ mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
+ mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
+ mVolumeComponent = volumeComponent;
+ mCommandQueue = commandQueue;
+ mCentralSurfacesComponentFactory = centralSurfacesComponentFactory;
+ mPluginManager = pluginManager;
+ mShadeController = shadeController;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mKeyguardViewMediatorCallback = viewMediatorCallback;
+ mInitController = initController;
+ mPluginDependencyProvider = pluginDependencyProvider;
+ mKeyguardDismissUtil = keyguardDismissUtil;
+ mExtensionController = extensionController;
+ mUserInfoControllerImpl = userInfoControllerImpl;
+ mIconPolicy = phoneStatusBarPolicy;
+ mDemoModeController = demoModeController;
+ mNotificationIconAreaController = notificationIconAreaController;
+ mBrightnessSliderFactory = brightnessSliderFactory;
+ mWallpaperController = wallpaperController;
+ mOngoingCallController = ongoingCallController;
+ mStatusBarSignalPolicy = statusBarSignalPolicy;
+ mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
+ mFeatureFlags = featureFlags;
+ mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+ mMainHandler = mainHandler;
+ mMainExecutor = delayableExecutor;
+ mMessageRouter = messageRouter;
+ mWallpaperManager = wallpaperManager;
+ mJankMonitor = jankMonitor;
+ mDreamOverlayStateController = dreamOverlayStateController;
+
+ mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
+ mStartingSurfaceOptional = startingSurfaceOptional;
+ mNotifPipelineFlags = notifPipelineFlags;
+ lockscreenShadeTransitionController.setCentralSurfaces(this);
+ statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
+
+ mScreenOffAnimationController = screenOffAnimationController;
+
+ mPanelExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged);
+
+ mBubbleExpandListener =
+ (isExpanding, key) -> mContext.getMainExecutor().execute(() -> {
+ mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
+ updateScrimController();
+ });
+
+ mActivityIntentHelper = new ActivityIntentHelper(mContext);
+ mActivityLaunchAnimator = activityLaunchAnimator;
+
+ // The status bar background may need updating when the ongoing call status changes.
+ mOngoingCallController.addCallback((animate) -> maybeUpdateBarMode());
+
+ // TODO(b/190746471): Find a better home for this.
+ DateTimeView.setReceiverHandler(timeTickHandler);
+
+ mMessageRouter.subscribeTo(KeyboardShortcutsMessage.class,
+ data -> toggleKeyboardShortcuts(data.mDeviceId));
+ mMessageRouter.subscribeTo(MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU,
+ id -> dismissKeyboardShortcuts());
+ mMessageRouter.subscribeTo(AnimateExpandSettingsPanelMessage.class,
+ data -> mCommandQueueCallbacks.animateExpandSettingsPanel(data.mSubpanel));
+ mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT,
+ id -> onLaunchTransitionTimeout());
+
+ deviceStateManager.registerCallback(mMainExecutor,
+ new FoldStateListener(mContext, this::onFoldedStateChanged));
+ wiredChargingRippleController.registerCallbacks();
+ }
+
+ @Override
+ public void start() {
+ mScreenLifecycle.addObserver(mScreenObserver);
+ mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ mUiModeManager = mContext.getSystemService(UiModeManager.class);
+ if (mBubblesOptional.isPresent()) {
+ mBubblesOptional.get().setExpandListener(mBubbleExpandListener);
+ }
+
+ mStatusBarSignalPolicy.init();
+ mKeyguardIndicationController.init();
+
+ mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
+ mStatusBarStateController.addCallback(mStateListener,
+ SysuiStatusBarStateController.RANK_STATUS_BAR);
+
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mDreamManager = IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE));
+
+ mDisplay = mContext.getDisplay();
+ mDisplayId = mDisplay.getDisplayId();
+ updateDisplaySize();
+ mStatusBarHideIconsForBouncerManager.setDisplayId(mDisplayId);
+
+ // start old BaseStatusBar.start().
+ mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
+ mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+
+ mAccessibilityManager = (AccessibilityManager)
+ mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+
+ mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+
+ mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mWallpaperSupported = mWallpaperManager.isWallpaperSupported();
+
+ RegisterStatusBarResult result = null;
+ try {
+ result = mBarService.registerStatusBar(mCommandQueue);
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+
+ createAndAddWindows(result);
+
+ if (mWallpaperSupported) {
+ // Make sure we always have the most current wallpaper info.
+ IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
+ mBroadcastDispatcher.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter,
+ null /* handler */, UserHandle.ALL);
+ mWallpaperChangedReceiver.onReceive(mContext, null);
+ } else if (DEBUG) {
+ Log.v(TAG, "start(): no wallpaper service ");
+ }
+
+ // Set up the initial notification state. This needs to happen before CommandQueue.disable()
+ setUpPresenter();
+
+ if (containsType(result.mTransientBarTypes, ITYPE_STATUS_BAR)) {
+ showTransientUnchecked();
+ }
+ mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
+ result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
+ result.mRequestedVisibilities, result.mPackageName);
+
+ // StatusBarManagerService has a back up of IME token and it's restored here.
+ mCommandQueueCallbacks.setImeWindowStatus(mDisplayId, result.mImeToken,
+ result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher);
+
+ // Set up the initial icon state
+ int numIcons = result.mIcons.size();
+ for (int i = 0; i < numIcons; i++) {
+ mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i));
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x",
+ numIcons,
+ result.mDisabledFlags1,
+ result.mAppearance,
+ result.mImeWindowVis));
+ }
+
+ IntentFilter internalFilter = new IntentFilter();
+ internalFilter.addAction(BANNER_ACTION_CANCEL);
+ internalFilter.addAction(BANNER_ACTION_SETUP);
+ mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
+ null, Context.RECEIVER_EXPORTED_UNAUDITED);
+
+ if (mWallpaperSupported) {
+ IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
+ ServiceManager.getService(Context.WALLPAPER_SERVICE));
+ try {
+ wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */);
+ } catch (RemoteException e) {
+ // Just pass, nothing critical.
+ }
+ }
+
+ // end old BaseStatusBar.start().
+
+ // Lastly, call to the icon policy to install/update all the icons.
+ mIconPolicy.init();
+
+ mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ @Override
+ public void onUnlockedChanged() {
+ logStateToEventlog();
+ }
+ });
+ startKeyguard();
+
+ mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
+ mDozeServiceHost.initialize(
+ this,
+ mStatusBarKeyguardViewManager,
+ mNotificationShadeWindowViewController,
+ mNotificationPanelViewController,
+ mAmbientIndicationContainer);
+ updateLightRevealScrimVisibility();
+
+ mConfigurationController.addCallback(mConfigurationListener);
+
+ mBatteryController.observe(mLifecycle, mBatteryStateChangeCallback);
+ mLifecycle.setCurrentState(RESUMED);
+
+ mAccessibilityFloatingMenuController.init();
+
+ // set the initial view visibility
+ int disabledFlags1 = result.mDisabledFlags1;
+ int disabledFlags2 = result.mDisabledFlags2;
+ mInitController.addPostInitTask(
+ () -> setUpDisableFlags(disabledFlags1, disabledFlags2));
+
+ mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener);
+
+ mPluginManager.addPluginListener(
+ new PluginListener<OverlayPlugin>() {
+ private final ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
+
+ @Override
+ public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
+ mMainExecutor.execute(
+ () -> plugin.setup(getNotificationShadeWindowView(),
+ getNavigationBarView(),
+ new Callback(plugin), mDozeParameters));
+ }
+
+ @Override
+ public void onPluginDisconnected(OverlayPlugin plugin) {
+ mMainExecutor.execute(() -> {
+ mOverlays.remove(plugin);
+ mNotificationShadeWindowController
+ .setForcePluginOpen(mOverlays.size() != 0, this);
+ });
+ }
+
+ class Callback implements OverlayPlugin.Callback {
+ private final OverlayPlugin mPlugin;
+
+ Callback(OverlayPlugin plugin) {
+ mPlugin = plugin;
+ }
+
+ @Override
+ public void onHoldStatusBarOpenChange() {
+ if (mPlugin.holdStatusBarOpen()) {
+ mOverlays.add(mPlugin);
+ } else {
+ mOverlays.remove(mPlugin);
+ }
+ mMainExecutor.execute(() -> {
+ mNotificationShadeWindowController
+ .setStateListener(b -> mOverlays.forEach(
+ o -> o.setCollapseDesired(b)));
+ mNotificationShadeWindowController
+ .setForcePluginOpen(mOverlays.size() != 0, this);
+ });
+ }
+ }
+ }, OverlayPlugin.class, true /* Allow multiple plugins */);
+
+ mStartingSurfaceOptional.ifPresent(startingSurface -> startingSurface.setSysuiProxy(
+ (requestTopUi, componentTag) -> mMainExecutor.execute(() ->
+ mNotificationShadeWindowController.setRequestTopUi(
+ requestTopUi, componentTag))));
+ }
+
+ private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) {
+ Trace.beginSection("CentralSurfaces#onFoldedStateChanged");
+ onFoldedStateChangedInternal(isFolded, willGoToSleep);
+ Trace.endSection();
+ }
+
+ private void onFoldedStateChangedInternal(boolean isFolded, boolean willGoToSleep) {
+ // Folded state changes are followed by a screen off event.
+ // By default turning off the screen also closes the shade.
+ // We want to make sure that the shade status is kept after
+ // folding/unfolding.
+ boolean isShadeOpen = mShadeController.isShadeOpen();
+ boolean leaveOpen = isShadeOpen && !willGoToSleep;
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "#onFoldedStateChanged(): "
+ + "isFolded=%s, "
+ + "willGoToSleep=%s, "
+ + "isShadeOpen=%s, "
+ + "leaveOpen=%s",
+ isFolded, willGoToSleep, isShadeOpen, leaveOpen));
+ }
+ if (leaveOpen) {
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+ if (mKeyguardStateController.isShowing()) {
+ // When device state changes on keyguard we don't want to keep the state of
+ // the shade and instead we open clean state of keyguard with shade closed.
+ // Normally some parts of QS state (like expanded/collapsed) are persisted and
+ // that causes incorrect UI rendering, especially when changing state with QS
+ // expanded. To prevent that we can close QS which resets QS and some parts of
+ // the shade to its default state. Read more in b/201537421
+ mCloseQsBeforeScreenOff = true;
+ }
+ }
+ }
+
+ // ================================================================================
+ // Constructing the view
+ // ================================================================================
+ protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
+ updateDisplaySize(); // populates mDisplayMetrics
+ updateResources();
+ updateTheme();
+
+ inflateStatusBarWindow();
+ mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener());
+ mWallpaperController.setRootView(mNotificationShadeWindowView);
+
+ // TODO: Deal with the ugliness that comes from having some of the status bar broken out
+ // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
+ mNotificationLogger.setUpWithContainer(mNotifListContainer);
+ mNotificationIconAreaController.setupShelf(mNotificationShelfController);
+ mPanelExpansionStateManager.addExpansionListener(mWakeUpCoordinator);
+ mUserSwitcherController.init(mNotificationShadeWindowView);
+
+ // Allow plugins to reference DarkIconDispatcher and StatusBarStateController
+ mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
+ mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
+
+ // Set up CollapsedStatusBarFragment and PhoneStatusBarView
+ StatusBarInitializer initializer = mCentralSurfacesComponent.getStatusBarInitializer();
+ initializer.setStatusBarViewUpdatedListener(
+ (statusBarView, statusBarViewController, statusBarTransitions) -> {
+ mStatusBarView = statusBarView;
+ mPhoneStatusBarViewController = statusBarViewController;
+ mStatusBarTransitions = statusBarTransitions;
+ mNotificationShadeWindowViewController
+ .setStatusBarViewController(mPhoneStatusBarViewController);
+ // Ensure we re-propagate panel expansion values to the panel controller and
+ // any listeners it may have, such as PanelBar. This will also ensure we
+ // re-display the notification panel if necessary (for example, if
+ // a heads-up notification was being displayed and should continue being
+ // displayed).
+ mNotificationPanelViewController.updatePanelExpansionAndVisibility();
+ setBouncerShowingForStatusBarComponents(mBouncerShowing);
+ checkBarModes();
+ });
+ initializer.initializeStatusBar(mCentralSurfacesComponent);
+
+ mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
+ mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
+ if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
+ mHeadsUpManager.addListener(mVisualStabilityManager);
+ }
+ mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
+
+ createNavigationBar(result);
+
+ if (ENABLE_LOCKSCREEN_WALLPAPER && mWallpaperSupported) {
+ mLockscreenWallpaper = mLockscreenWallpaperLazy.get();
+ }
+
+ mNotificationPanelViewController.setKeyguardIndicationController(
+ mKeyguardIndicationController);
+
+ mAmbientIndicationContainer = mNotificationShadeWindowView.findViewById(
+ R.id.ambient_indication_container);
+
+ mAutoHideController.setStatusBar(new AutoHideUiElement() {
+ @Override
+ public void synchronizeState() {
+ checkBarModes();
+ }
+
+ @Override
+ public boolean shouldHideOnTouch() {
+ return !mRemoteInputManager.isRemoteInputActive();
+ }
+
+ @Override
+ public boolean isVisible() {
+ return isTransientShown();
+ }
+
+ @Override
+ public void hide() {
+ clearTransient();
+ }
+ });
+
+ ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind);
+ ScrimView notificationsScrim = mNotificationShadeWindowView
+ .findViewById(R.id.scrim_notifications);
+ ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front);
+
+ mScrimController.setScrimVisibleListener(scrimsVisible -> {
+ mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);
+ });
+ mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
+
+ mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
+ mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> {
+ Runnable updateOpaqueness = () -> {
+ mNotificationShadeWindowController.setLightRevealScrimOpaque(
+ mLightRevealScrim.isScrimOpaque());
+ mScreenOffAnimationController
+ .onScrimOpaqueChanged(mLightRevealScrim.isScrimOpaque());
+ };
+ if (opaque) {
+ // Delay making the view opaque for a frame, because it needs some time to render
+ // otherwise this can lead to a flicker where the scrim doesn't cover the screen
+ mLightRevealScrim.post(updateOpaqueness);
+ } else {
+ updateOpaqueness.run();
+ }
+ });
+
+ mScreenOffAnimationController.initialize(this, mLightRevealScrim);
+ updateLightRevealScrimVisibility();
+
+ mNotificationPanelViewController.initDependencies(
+ this,
+ this::makeExpandedInvisible,
+ mNotificationShelfController);
+
+ BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop);
+ mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
+ backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
+ float maxWallpaperZoom = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_wallpaperMaxScale);
+ mNotificationShadeDepthControllerLazy.get().addListener(depth -> {
+ float scale = MathUtils.lerp(maxWallpaperZoom, 1f, depth);
+ backdrop.setPivotX(backdrop.getWidth() / 2f);
+ backdrop.setPivotY(backdrop.getHeight() / 2f);
+ backdrop.setScaleX(scale);
+ backdrop.setScaleY(scale);
+ });
+
+ mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
+
+ // Set up the quick settings tile panel
+ final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
+ if (container != null) {
+ FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
+ ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
+ mExtensionController
+ .newExtension(QS.class)
+ .withPlugin(QS.class)
+ .withDefault(this::createDefaultQSFragment)
+ .build());
+ mBrightnessMirrorController = new BrightnessMirrorController(
+ mNotificationShadeWindowView,
+ mNotificationPanelViewController,
+ mNotificationShadeDepthControllerLazy.get(),
+ mBrightnessSliderFactory,
+ (visible) -> {
+ mBrightnessMirrorVisible = visible;
+ updateScrimController();
+ });
+ fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
+ QS qs = (QS) f;
+ if (qs instanceof QSFragment) {
+ mQSPanelController = ((QSFragment) qs).getQSPanelController();
+ ((QSFragment) qs).setBrightnessMirrorController(mBrightnessMirrorController);
+ }
+ });
+ }
+
+ mReportRejectedTouch = mNotificationShadeWindowView
+ .findViewById(R.id.report_rejected_touch);
+ if (mReportRejectedTouch != null) {
+ updateReportRejectedTouchVisibility();
+ mReportRejectedTouch.setOnClickListener(v -> {
+ Uri session = mFalsingManager.reportRejectedTouch();
+ if (session == null) { return; }
+
+ StringWriter message = new StringWriter();
+ message.write("Build info: ");
+ message.write(SystemProperties.get("ro.build.description"));
+ message.write("\nSerial number: ");
+ message.write(SystemProperties.get("ro.serialno"));
+ message.write("\n");
+
+ startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND)
+ .setType("*/*")
+ .putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report")
+ .putExtra(Intent.EXTRA_STREAM, session)
+ .putExtra(Intent.EXTRA_TEXT, message.toString()),
+ "Share rejected touch report")
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ true /* onlyProvisioned */, true /* dismissShade */);
+ });
+ }
+
+ if (!mPowerManager.isInteractive()) {
+ mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+ }
+ mGestureWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+ "sysui:GestureWakeLock");
+
+ // receive broadcasts
+ registerBroadcastReceiver();
+
+ IntentFilter demoFilter = new IntentFilter();
+ if (DEBUG_MEDIA_FAKE_ARTWORK) {
+ demoFilter.addAction(ACTION_FAKE_ARTWORK);
+ }
+ mContext.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
+ android.Manifest.permission.DUMP, null,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
+
+ // listen for USER_SETUP_COMPLETE setting (per-user)
+ mDeviceProvisionedController.addCallback(mUserSetupObserver);
+ mUserSetupObserver.onUserSetupChanged();
+
+ // disable profiling bars, since they overlap and clutter the output on app windows
+ ThreadedRenderer.overrideProperty("disableProfileBars", "true");
+
+ // Private API call to make the shadows look better for Recents
+ ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
+ }
+
+
+ /**
+ * When swiping up to dismiss the lock screen, the panel expansion fraction goes from 1f to 0f.
+ * This results in the clock/notifications/other content disappearing off the top of the screen.
+ *
+ * We also use the expansion fraction to animate in the app/launcher surface from the bottom of
+ * the screen, 'pushing' off the notifications and other content. To do this, we dispatch the
+ * expansion fraction to the KeyguardViewMediator if we're in the process of dismissing the
+ * keyguard.
+ */
+ private void dispatchPanelExpansionForKeyguardDismiss(float fraction, boolean trackingTouch) {
+ // Things that mean we're not swiping to dismiss the keyguard, and should ignore this
+ // expansion:
+ // - Keyguard isn't even visible.
+ // - Keyguard is occluded. Expansion changes here are the shade being expanded over the
+ // occluding activity.
+ // - Keyguard is visible, but can't be dismissed (swiping up will show PIN/password prompt).
+ // - The SIM is locked, you can't swipe to unlock. If the SIM is locked but there is no
+ // device lock set, canDismissLockScreen returns true even though you should not be able
+ // to dismiss the lock screen until entering the SIM PIN.
+ // - QS is expanded and we're swiping - swiping up now will hide QS, not dismiss the
+ // keyguard.
+ if (!isKeyguardShowing()
+ || isOccluded()
+ || !mKeyguardStateController.canDismissLockScreen()
+ || mKeyguardViewMediator.isAnySimPinSecure()
+ || (mNotificationPanelViewController.isQsExpanded() && trackingTouch)) {
+ return;
+ }
+
+ // Otherwise, we should let the keyguard know about this if we're tracking touch, or if we
+ // are already animating the keyguard dismiss (since we will need to either finish or cancel
+ // the animation).
+ if (trackingTouch
+ || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()
+ || mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
+ mKeyguardStateController.notifyKeyguardDismissAmountChanged(
+ 1f - fraction, trackingTouch);
+ }
+ }
+
+ private void onPanelExpansionChanged(PanelExpansionChangeEvent event) {
+ float fraction = event.getFraction();
+ boolean tracking = event.getTracking();
+ dispatchPanelExpansionForKeyguardDismiss(fraction, tracking);
+
+ if (fraction == 0 || fraction == 1) {
+ if (getNavigationBarView() != null) {
+ getNavigationBarView().onStatusBarPanelStateChanged();
+ }
+ if (getNotificationPanelViewController() != null) {
+ getNotificationPanelViewController().updateSystemUiStateFlags();
+ }
+ }
+ }
+
+ @NonNull
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
+ }
+
+ @VisibleForTesting
+ protected void registerBroadcastReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, null, UserHandle.ALL);
+ }
+
+ protected QS createDefaultQSFragment() {
+ return FragmentHostManager.get(mNotificationShadeWindowView).create(QSFragment.class);
+ }
+
+ private void setUpPresenter() {
+ // Set up the initial notification state.
+ mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback);
+ mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
+ mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
+ mNotificationShadeWindowViewController,
+ mNotifListContainer,
+ mHeadsUpManager,
+ mJankMonitor);
+ mNotificationShelfController.setOnActivatedListener(mPresenter);
+ mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
+ mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter);
+ mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
+ mNotificationsController.initialize(
+ mPresenter,
+ mNotifListContainer,
+ mStackScrollerController.getNotifStackController(),
+ mNotificationActivityStarter,
+ mCentralSurfacesComponent.getBindRowCallback());
+ }
+
+ /**
+ * Post-init task of {@link #start()}
+ * @param state1 disable1 flags
+ * @param state2 disable2 flags
+ */
+ protected void setUpDisableFlags(int state1, int state2) {
+ mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */);
+ }
+
+ /**
+ * Ask the display to wake up if currently dozing, else do nothing
+ *
+ * @param time when to wake up
+ * @param where the view requesting the wakeup
+ * @param why the reason for the wake up
+ */
+ @Override
+ public void wakeUpIfDozing(long time, View where, String why) {
+ if (mDozing && mScreenOffAnimationController.allowWakeUpIfDozing()) {
+ mPowerManager.wakeUp(
+ time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
+ mWakeUpComingFromTouch = true;
+ where.getLocationInWindow(mTmpInt2);
+
+ // NOTE, the incoming view can sometimes be the entire container... unsure if
+ // this location is valuable enough
+ mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
+ mTmpInt2[1] + where.getHeight() / 2);
+ mFalsingCollector.onScreenOnFromTouch();
+ }
+ }
+
+ // TODO(b/117478341): This was left such that CarStatusBar can override this method.
+ // Try to remove this.
+ protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
+ mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */, result);
+ }
+
+ /**
+ * Returns the {@link android.view.View.OnTouchListener} that will be invoked when the
+ * background window of the status bar is clicked.
+ */
+ protected View.OnTouchListener getStatusBarWindowTouchListener() {
+ return (v, event) -> {
+ mAutoHideController.checkUserAutoHide(event);
+ mRemoteInputManager.checkRemoteInputOutside(event);
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (mExpandedVisible) {
+ mShadeController.animateCollapsePanels();
+ }
+ }
+ return mNotificationShadeWindowView.onTouchEvent(event);
+ };
+ }
+
+ private void inflateStatusBarWindow() {
+ if (mCentralSurfacesComponent != null) {
+ // Tear down
+ for (CentralSurfacesComponent.Startable s : mCentralSurfacesComponent.getStartables()) {
+ s.stop();
+ }
+ }
+ mCentralSurfacesComponent = mCentralSurfacesComponentFactory.create();
+ mFragmentService.addFragmentInstantiationProvider(mCentralSurfacesComponent);
+
+ mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView();
+ mNotificationShadeWindowViewController = mCentralSurfacesComponent
+ .getNotificationShadeWindowViewController();
+ mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
+ mNotificationShadeWindowViewController.setupExpandedStatusBar();
+ mNotificationPanelViewController =
+ mCentralSurfacesComponent.getNotificationPanelViewController();
+ mCentralSurfacesComponent.getLockIconViewController().init();
+ mStackScrollerController =
+ mCentralSurfacesComponent.getNotificationStackScrollLayoutController();
+ mStackScroller = mStackScrollerController.getView();
+ mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer();
+ mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
+ mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
+ mNotificationShelfController = mCentralSurfacesComponent.getNotificationShelfController();
+ mAuthRippleController = mCentralSurfacesComponent.getAuthRippleController();
+ mAuthRippleController.init();
+
+ mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener());
+
+ // Listen for demo mode changes
+ mDemoModeController.addCallback(mDemoModeCallback);
+
+ if (mCommandQueueCallbacks != null) {
+ mCommandQueue.removeCallback(mCommandQueueCallbacks);
+ }
+ mCommandQueueCallbacks =
+ mCentralSurfacesComponent.getCentralSurfacesCommandQueueCallbacks();
+ // Connect in to the status bar manager service
+ mCommandQueue.addCallback(mCommandQueueCallbacks);
+
+ // Perform all other initialization for CentralSurfacesScope
+ for (CentralSurfacesComponent.Startable s : mCentralSurfacesComponent.getStartables()) {
+ s.start();
+ }
+ }
+
+ protected void startKeyguard() {
+ Trace.beginSection("CentralSurfaces#startKeyguard");
+ mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
+ mBiometricUnlockController.setBiometricModeListener(
+ new BiometricUnlockController.BiometricModeListener() {
+ @Override
+ public void onResetMode() {
+ setWakeAndUnlocking(false);
+ }
+
+ @Override
+ public void onModeChanged(int mode) {
+ switch (mode) {
+ case BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM:
+ case BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING:
+ case BiometricUnlockController.MODE_WAKE_AND_UNLOCK:
+ setWakeAndUnlocking(true);
+ }
+ }
+
+ @Override
+ public void notifyBiometricAuthModeChanged() {
+ CentralSurfacesImpl.this.notifyBiometricAuthModeChanged();
+ }
+
+ private void setWakeAndUnlocking(boolean wakeAndUnlocking) {
+ if (getNavigationBarView() != null) {
+ getNavigationBarView().setWakeAndUnlocking(wakeAndUnlocking);
+ }
+ }
+ });
+ mStatusBarKeyguardViewManager.registerCentralSurfaces(
+ /* statusBar= */ this,
+ mNotificationPanelViewController,
+ mPanelExpansionStateManager,
+ mBiometricUnlockController,
+ mStackScroller,
+ mKeyguardBypassController);
+ mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
+ mKeyguardIndicationController
+ .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+ mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
+ mRemoteInputManager.addControllerCallback(mStatusBarKeyguardViewManager);
+ mDynamicPrivacyController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+
+ mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
+ mMediaManager.setBiometricUnlockController(mBiometricUnlockController);
+ mKeyguardDismissUtil.setDismissHandler(this::executeWhenUnlocked);
+ Trace.endSection();
+ }
+
+ @Override
+ public NotificationShadeWindowView getNotificationShadeWindowView() {
+ return mNotificationShadeWindowView;
+ }
+
+ @Override
+ public NotificationShadeWindowViewController getNotificationShadeWindowViewController() {
+ return mNotificationShadeWindowViewController;
+ }
+
+ @Override
+ public NotificationPanelViewController getNotificationPanelViewController() {
+ return mNotificationPanelViewController;
+ }
+
+ @Override
+ public ViewGroup getBouncerContainer() {
+ return mNotificationShadeWindowViewController.getBouncerContainer();
+ }
+
+ @Override
+ public int getStatusBarHeight() {
+ return mStatusBarWindowController.getStatusBarHeight();
+ }
+
+ /**
+ * Disable QS if device not provisioned.
+ * If the user switcher is simple then disable QS during setup because
+ * the user intends to use the lock screen user switcher, QS in not needed.
+ */
+ @Override
+ public void updateQsExpansionEnabled() {
+ final boolean expandEnabled = mDeviceProvisionedController.isDeviceProvisioned()
+ && (mUserSetup || mUserSwitcherController == null
+ || !mUserSwitcherController.isSimpleUserSwitcher())
+ && !isShadeDisabled()
+ && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
+ && !mDozing
+ && !ONLY_CORE_APPS;
+ mNotificationPanelViewController.setQsExpansionEnabledPolicy(expandEnabled);
+ Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
+ }
+
+ @Override
+ public boolean isShadeDisabled() {
+ return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
+ }
+
+ /**
+ * Request a notification update
+ * @param reason why we're requesting a notification update
+ */
+ @Override
+ public void requestNotificationUpdate(String reason) {
+ mNotificationsController.requestNotificationUpdate(reason);
+ }
+
+ /**
+ * Asks {@link KeyguardUpdateMonitor} to run face auth.
+ */
+ @Override
+ public void requestFaceAuth(boolean userInitiatedRequest) {
+ if (!mKeyguardStateController.canDismissLockScreen()) {
+ mKeyguardUpdateMonitor.requestFaceAuth(userInitiatedRequest);
+ }
+ }
+
+ private void updateReportRejectedTouchVisibility() {
+ if (mReportRejectedTouch == null) {
+ return;
+ }
+ mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD && !mDozing
+ && mFalsingCollector.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ @Override
+ public boolean areNotificationAlertsDisabled() {
+ return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
+ }
+
+ @Override
+ public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
+ int flags) {
+ startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, flags);
+ }
+
+ @Override
+ public void startActivity(Intent intent, boolean dismissShade) {
+ startActivityDismissingKeyguard(intent, false /* onlyProvisioned */, dismissShade);
+ }
+
+ @Override
+ public void startActivity(Intent intent, boolean dismissShade,
+ @Nullable ActivityLaunchAnimator.Controller animationController,
+ boolean showOverLockscreenWhenLocked) {
+ startActivity(intent, dismissShade, animationController, showOverLockscreenWhenLocked,
+ getActivityUserHandle(intent));
+ }
+
+ @Override
+ public void startActivity(Intent intent, boolean dismissShade,
+ @Nullable ActivityLaunchAnimator.Controller animationController,
+ boolean showOverLockscreenWhenLocked, UserHandle userHandle) {
+ // Make sure that we dismiss the keyguard if it is directly dismissable or when we don't
+ // want to show the activity above it.
+ if (mKeyguardStateController.isUnlocked() || !showOverLockscreenWhenLocked) {
+ startActivityDismissingKeyguard(intent, false, dismissShade,
+ false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
+ 0 /* flags */, animationController, userHandle);
+ return;
+ }
+
+ boolean animate =
+ animationController != null && shouldAnimateLaunch(true /* isActivityIntent */,
+ showOverLockscreenWhenLocked);
+
+ ActivityLaunchAnimator.Controller controller = null;
+ if (animate) {
+ // Wrap the animation controller to dismiss the shade and set
+ // mIsLaunchingActivityOverLockscreen during the animation.
+ ActivityLaunchAnimator.Controller delegate = wrapAnimationController(
+ animationController, dismissShade);
+ controller = new DelegateLaunchAnimatorController(delegate) {
+ @Override
+ public void onIntentStarted(boolean willAnimate) {
+ getDelegate().onIntentStarted(willAnimate);
+
+ if (willAnimate) {
+ CentralSurfacesImpl.this.mIsLaunchingActivityOverLockscreen = true;
+ }
+ }
+
+ @Override
+ public void onLaunchAnimationStart(boolean isExpandingFullyAbove) {
+ super.onLaunchAnimationStart(isExpandingFullyAbove);
+
+ // Double check that the keyguard is still showing and not going away, but if so
+ // set the keyguard occluded. Typically, WM will let KeyguardViewMediator know
+ // directly, but we're overriding that to play the custom launch animation, so
+ // we need to take care of that here. The unocclude animation is not overridden,
+ // so WM will call KeyguardViewMediator's unocclude animation runner when the
+ // activity is exited.
+ if (mKeyguardStateController.isShowing()
+ && !mKeyguardStateController.isKeyguardGoingAway()) {
+ mKeyguardViewMediator.setOccluded(true /* isOccluded */,
+ true /* animate */);
+ }
+ }
+
+ @Override
+ public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
+ // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the
+ // animation so that we can assume that mIsLaunchingActivityOverLockscreen
+ // being true means that we will collapse the shade (or at least run the
+ // post collapse runnables) later on.
+ CentralSurfacesImpl.this.mIsLaunchingActivityOverLockscreen = false;
+ getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove);
+ }
+
+ @Override
+ public void onLaunchAnimationCancelled() {
+ // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the
+ // animation so that we can assume that mIsLaunchingActivityOverLockscreen
+ // being true means that we will collapse the shade (or at least run the
+ // post collapse runnables) later on.
+ CentralSurfacesImpl.this.mIsLaunchingActivityOverLockscreen = false;
+ getDelegate().onLaunchAnimationCancelled();
+ }
+ };
+ } else if (dismissShade) {
+ // The animation will take care of dismissing the shade at the end of the animation. If
+ // we don't animate, collapse it directly.
+ collapseShade();
+ }
+
+ mActivityLaunchAnimator.startIntentWithAnimation(controller, animate,
+ intent.getPackage(), showOverLockscreenWhenLocked, (adapter) -> TaskStackBuilder
+ .create(mContext)
+ .addNextIntent(intent)
+ .startActivities(
+ CentralSurfaces.getActivityOptions(getDisplayId(), adapter),
+ userHandle));
+ }
+
+ /**
+ * Whether we are currently animating an activity launch above the lockscreen (occluding
+ * activity).
+ */
+ @Override
+ public boolean isLaunchingActivityOverLockscreen() {
+ return mIsLaunchingActivityOverLockscreen;
+ }
+
+ @Override
+ public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
+ startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade);
+ }
+
+ @Override
+ public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
+ startActivityDismissingKeyguard(intent, false, dismissShade,
+ false /* disallowEnterPictureInPictureWhileLaunching */, callback, 0,
+ null /* animationController */, getActivityUserHandle(intent));
+ }
+
+ @Override
+ public void setQsExpanded(boolean expanded) {
+ mNotificationShadeWindowController.setQsExpanded(expanded);
+ mNotificationPanelViewController.setStatusAccessibilityImportance(expanded
+ ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ mNotificationPanelViewController.updateSystemUiStateFlags();
+ if (getNavigationBarView() != null) {
+ getNavigationBarView().onStatusBarPanelStateChanged();
+ }
+ }
+
+ @Override
+ public boolean isWakeUpComingFromTouch() {
+ return mWakeUpComingFromTouch;
+ }
+
+ @Override
+ public boolean isFalsingThresholdNeeded() {
+ return true;
+ }
+
+ /**
+ * To be called when there's a state change in StatusBarKeyguardViewManager.
+ */
+ @Override
+ public void onKeyguardViewManagerStatesUpdated() {
+ logStateToEventlog();
+ }
+
+ @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;
+ }
+
+ @Override
+ public boolean isPulsing() {
+ return mDozeServiceHost.isPulsing();
+ }
+
+ @androidx.annotation.Nullable
+ @Override
+ public View getAmbientIndicationContainer() {
+ return mAmbientIndicationContainer;
+ }
+
+ /**
+ * When the keyguard is showing and covered by a "showWhenLocked" activity it
+ * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager}
+ *
+ * @return whether the keyguard is currently occluded
+ */
+ @Override
+ public boolean isOccluded() {
+ return mKeyguardStateController.isOccluded();
+ }
+
+ /** A launch animation was cancelled. */
+ //TODO: These can / should probably be moved to NotificationPresenter or ShadeController
+ @Override
+ public void onLaunchAnimationCancelled(boolean isLaunchForActivity) {
+ if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
+ && isLaunchForActivity) {
+ onClosingFinished();
+ } else {
+ mShadeController.collapsePanel(true /* animate */);
+ }
+ }
+
+ /** A launch animation ended. */
+ @Override
+ public void onLaunchAnimationEnd(boolean launchIsFullScreen) {
+ if (!mPresenter.isCollapsing()) {
+ onClosingFinished();
+ }
+ if (launchIsFullScreen) {
+ instantCollapseNotificationPanel();
+ }
+ }
+
+ /**
+ * Whether we should animate an activity launch.
+ *
+ * Note: This method must be called *before* dismissing the keyguard.
+ */
+ @Override
+ public boolean shouldAnimateLaunch(boolean isActivityIntent, boolean showOverLockscreen) {
+ // TODO(b/184121838): Support launch animations when occluded.
+ if (isOccluded()) {
+ return false;
+ }
+
+ // Always animate if we are not showing the keyguard or if we animate over the lockscreen
+ // (without unlocking it).
+ if (showOverLockscreen || !mKeyguardStateController.isShowing()) {
+ return true;
+ }
+
+ // If we are locked and have to dismiss the keyguard, only animate if remote unlock
+ // animations are enabled. We also don't animate non-activity launches as they can break the
+ // animation.
+ // TODO(b/184121838): Support non activity launches on the lockscreen.
+ return isActivityIntent && KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation;
+ }
+
+ /** Whether we should animate an activity launch. */
+ @Override
+ public boolean shouldAnimateLaunch(boolean isActivityIntent) {
+ return shouldAnimateLaunch(isActivityIntent, false /* showOverLockscreen */);
+ }
+
+ @Override
+ public boolean isDeviceInVrMode() {
+ return mPresenter.isDeviceInVrMode();
+ }
+
+ @Override
+ public NotificationPresenter getPresenter() {
+ return mPresenter;
+ }
+
+ @VisibleForTesting
+ @Override
+ public void setBarStateForTest(int state) {
+ mState = state;
+ }
+
+ static class AnimateExpandSettingsPanelMessage {
+ final String mSubpanel;
+
+ AnimateExpandSettingsPanelMessage(String subpanel) {
+ mSubpanel = subpanel;
+ }
+ }
+
+ private void maybeEscalateHeadsUp() {
+ mHeadsUpManager.getAllEntries().forEach(entry -> {
+ final StatusBarNotification sbn = entry.getSbn();
+ final Notification notification = sbn.getNotification();
+ if (notification.fullScreenIntent != null) {
+ if (DEBUG) {
+ Log.d(TAG, "converting a heads up to fullScreen");
+ }
+ try {
+ EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
+ sbn.getKey());
+ wakeUpForFullScreenIntent();
+ notification.fullScreenIntent.send();
+ entry.notifyFullScreenIntentLaunched();
+ } catch (PendingIntent.CanceledException e) {
+ }
+ }
+ });
+ mHeadsUpManager.releaseAllImmediately();
+ }
+
+ @Override
+ public void wakeUpForFullScreenIntent() {
+ if (isGoingToSleep() || mDozing) {
+ mPowerManager.wakeUp(
+ SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_APPLICATION,
+ "com.android.systemui:full_screen_intent");
+ mWakeUpComingFromTouch = false;
+ mWakeUpTouchLocation = null;
+ }
+ }
+
+ @Override
+ public void makeExpandedVisible(boolean force) {
+ if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
+ if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) {
+ return;
+ }
+
+ mExpandedVisible = true;
+
+ // Expand the window to encompass the full screen in anticipation of the drag.
+ // This is only possible to do atomically because the status bar is at the top of the screen!
+ mNotificationShadeWindowController.setPanelVisible(true);
+
+ visibilityChanged(true);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, !force /* animate */);
+ setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
+ }
+
+ @Override
+ public void postAnimateCollapsePanels() {
+ mMainExecutor.execute(mShadeController::animateCollapsePanels);
+ }
+
+ @Override
+ public void postAnimateForceCollapsePanels() {
+ mMainExecutor.execute(
+ () -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE,
+ true /* force */));
+ }
+
+ @Override
+ public void postAnimateOpenPanels() {
+ mMessageRouter.sendMessage(MSG_OPEN_SETTINGS_PANEL);
+ }
+
+ @Override
+ public boolean isExpandedVisible() {
+ return mExpandedVisible;
+ }
+
+ @Override
+ public boolean isPanelExpanded() {
+ return mPanelExpanded;
+ }
+
+ /**
+ * Called when another window is about to transfer it's input focus.
+ */
+ @Override
+ public void onInputFocusTransfer(boolean start, boolean cancel, float velocity) {
+ if (!mCommandQueue.panelsEnabled()) {
+ return;
+ }
+
+ if (start) {
+ mNotificationPanelViewController.startWaitingForOpenPanelGesture();
+ } else {
+ mNotificationPanelViewController.stopWaitingForOpenPanelGesture(cancel, velocity);
+ }
+ }
+
+ @Override
+ public void animateCollapseQuickSettings() {
+ if (mState == StatusBarState.SHADE) {
+ mNotificationPanelViewController.collapsePanel(
+ true, false /* delayed */, 1.0f /* speedUpFactor */);
+ }
+ }
+
+ void makeExpandedInvisible() {
+ if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
+ + " mExpandedVisible=" + mExpandedVisible);
+
+ if (!mExpandedVisible || mNotificationShadeWindowView == null) {
+ return;
+ }
+
+ // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
+ mNotificationPanelViewController.collapsePanel(/*animate=*/ false, false /* delayed*/,
+ 1.0f /* speedUpFactor */);
+
+ mNotificationPanelViewController.closeQs();
+
+ mExpandedVisible = false;
+ visibilityChanged(false);
+
+ // Update the visibility of notification shade and status bar window.
+ mNotificationShadeWindowController.setPanelVisible(false);
+ mStatusBarWindowController.setForceStatusBarVisible(false);
+
+ // Close any guts that might be visible
+ mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
+
+ mShadeController.runPostCollapseRunnables();
+ setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
+ if (!mNotificationActivityStarter.isCollapsingToShowActivityOverLockscreen()) {
+ showBouncerOrLockScreenIfKeyguard();
+ } else if (DEBUG) {
+ Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
+ }
+ mCommandQueue.recomputeDisableFlags(
+ mDisplayId,
+ mNotificationPanelViewController.hideStatusBarIconsWhenExpanded() /* animate */);
+
+ // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
+ // the bouncer appear animation.
+ if (!mStatusBarKeyguardViewManager.isShowing()) {
+ WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+ }
+ }
+
+ /** Called when a touch event occurred on {@link PhoneStatusBarView}. */
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ // TODO(b/202981994): Move this touch debugging to a central location. (Right now, it's
+ // split between NotificationPanelViewController and here.)
+ if (DEBUG_GESTURES) {
+ if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
+ EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
+ event.getActionMasked(), (int) event.getX(), (int) event.getY(),
+ mDisabled1, mDisabled2);
+ }
+
+ }
+
+ if (SPEW) {
+ Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1="
+ + mDisabled1 + " mDisabled2=" + mDisabled2);
+ } else if (CHATTY) {
+ if (event.getAction() != MotionEvent.ACTION_MOVE) {
+ Log.d(TAG, String.format(
+ "panel: %s at (%f, %f) mDisabled1=0x%08x mDisabled2=0x%08x",
+ MotionEvent.actionToString(event.getAction()),
+ event.getRawX(), event.getRawY(), mDisabled1, mDisabled2));
+ }
+ }
+
+ if (DEBUG_GESTURES) {
+ mGestureRec.add(event);
+ }
+
+ if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
+ final boolean upOrCancel =
+ event.getAction() == MotionEvent.ACTION_UP ||
+ event.getAction() == MotionEvent.ACTION_CANCEL;
+ setInteracting(StatusBarManager.WINDOW_STATUS_BAR, !upOrCancel || mExpandedVisible);
+ }
+ }
+
+ @Override
+ public GestureRecorder getGestureRecorder() {
+ return mGestureRec;
+ }
+
+ @Override
+ public BiometricUnlockController getBiometricUnlockController() {
+ return mBiometricUnlockController;
+ }
+
+ @Override
+ public void showTransientUnchecked() {
+ if (!mTransientShown) {
+ mTransientShown = true;
+ mNoAnimationOnNextBarModeChange = true;
+ maybeUpdateBarMode();
+ }
+ }
+
+ @Override
+ public void clearTransient() {
+ if (mTransientShown) {
+ mTransientShown = false;
+ maybeUpdateBarMode();
+ }
+ }
+
+ private void maybeUpdateBarMode() {
+ final int barMode = barMode(mTransientShown, mAppearance);
+ if (updateBarMode(barMode)) {
+ mLightBarController.onStatusBarModeChanged(barMode);
+ updateBubblesVisibility();
+ }
+ }
+
+ private boolean updateBarMode(int barMode) {
+ if (mStatusBarMode != barMode) {
+ mStatusBarMode = barMode;
+ checkBarModes();
+ mAutoHideController.touchAutoHide();
+ return true;
+ }
+ return false;
+ }
+
+ private @TransitionMode int barMode(boolean isTransient, int appearance) {
+ final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
+ if (mOngoingCallController.hasOngoingCall() && mIsFullscreen) {
+ return MODE_SEMI_TRANSPARENT;
+ } else if (isTransient) {
+ return MODE_SEMI_TRANSPARENT;
+ } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
+ return MODE_LIGHTS_OUT;
+ } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) {
+ return MODE_LIGHTS_OUT_TRANSPARENT;
+ } else if ((appearance & APPEARANCE_OPAQUE_STATUS_BARS) != 0) {
+ return MODE_OPAQUE;
+ } else if ((appearance & APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS) != 0) {
+ return MODE_SEMI_TRANSPARENT;
+ } else {
+ return MODE_TRANSPARENT;
+ }
+ }
+
+ @Override
+ public void showWirelessChargingAnimation(int batteryLevel) {
+ showChargingAnimation(batteryLevel, UNKNOWN_BATTERY_LEVEL, 0);
+ }
+
+ protected void showChargingAnimation(int batteryLevel, int transmittingBatteryLevel,
+ long animationDelay) {
+ WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
+ transmittingBatteryLevel, batteryLevel,
+ new WirelessChargingAnimation.Callback() {
+ @Override
+ public void onAnimationStarting() {
+ mNotificationShadeWindowController.setRequestTopUi(true, TAG);
+ }
+
+ @Override
+ public void onAnimationEnded() {
+ mNotificationShadeWindowController.setRequestTopUi(false, TAG);
+ }
+ }, false, sUiEventLogger).show(animationDelay);
+ }
+
+ @Override
+ public void checkBarModes() {
+ if (mDemoModeController.isInDemoMode()) return;
+ if (mStatusBarTransitions != null) {
+ checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarTransitions);
+ }
+ mNavigationBarController.checkNavBarModes(mDisplayId);
+ mNoAnimationOnNextBarModeChange = false;
+ }
+
+ // Called by NavigationBarFragment
+ @Override
+ public void setQsScrimEnabled(boolean scrimEnabled) {
+ mNotificationPanelViewController.setQsScrimEnabled(scrimEnabled);
+ }
+
+ /** Temporarily hides Bubbles if the status bar is hidden. */
+ @Override
+ public void updateBubblesVisibility() {
+ mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged(
+ mStatusBarMode != MODE_LIGHTS_OUT
+ && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT
+ && !mStatusBarWindowHidden));
+ }
+
+ void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
+ BarTransitions transitions) {
+ final boolean anim = !mNoAnimationOnNextBarModeChange && mDeviceInteractive
+ && windowState != WINDOW_STATE_HIDDEN;
+ transitions.transitionTo(mode, anim);
+ }
+
+ private void finishBarAnimations() {
+ if (mStatusBarTransitions != null) {
+ mStatusBarTransitions.finishAnimations();
+ }
+ mNavigationBarController.finishBarAnimations(mDisplayId);
+ }
+
+ private final Runnable mCheckBarModes = this::checkBarModes;
+
+ @Override
+ public void setInteracting(int barWindow, boolean interacting) {
+ mInteractingWindows = interacting
+ ? (mInteractingWindows | barWindow)
+ : (mInteractingWindows & ~barWindow);
+ if (mInteractingWindows != 0) {
+ mAutoHideController.suspendAutoHide();
+ } else {
+ mAutoHideController.resumeSuspendedAutoHide();
+ }
+ checkBarModes();
+ }
+
+ private void dismissVolumeDialog() {
+ if (mVolumeComponent != null) {
+ mVolumeComponent.dismissNow();
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter pwOriginal, String[] args) {
+ IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
+ synchronized (mQueueLock) {
+ pw.println("Current Status Bar state:");
+ pw.println(" mExpandedVisible=" + mExpandedVisible);
+ pw.println(" mDisplayMetrics=" + mDisplayMetrics);
+ pw.println(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller));
+ pw.println(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller)
+ + " scroll " + mStackScroller.getScrollX()
+ + "," + mStackScroller.getScrollY());
+ }
+
+ pw.print(" mInteractingWindows="); pw.println(mInteractingWindows);
+ pw.print(" mStatusBarWindowState=");
+ pw.println(windowStateToString(mStatusBarWindowState));
+ pw.print(" mStatusBarMode=");
+ pw.println(BarTransitions.modeToString(mStatusBarMode));
+ pw.print(" mDozing="); pw.println(mDozing);
+ pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported);
+
+ pw.println(" ShadeWindowView: ");
+ if (mNotificationShadeWindowViewController != null) {
+ mNotificationShadeWindowViewController.dump(pw, args);
+ CentralSurfaces.dumpBarTransitions(
+ pw, "PhoneStatusBarTransitions", mStatusBarTransitions);
+ }
+
+ pw.println(" mMediaManager: ");
+ if (mMediaManager != null) {
+ mMediaManager.dump(pw, args);
+ }
+
+ 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: ");
+ if (mStackScroller != null) {
+ // Double indent until we rewrite the rest of this dump()
+ pw.increaseIndent();
+ pw.increaseIndent();
+ mStackScroller.dump(pw, args);
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
+ pw.println(" Theme:");
+ String nightMode = mUiModeManager == null ? "null" : mUiModeManager.getNightMode() + "";
+ pw.println(" dark theme: " + nightMode +
+ " (auto: " + UiModeManager.MODE_NIGHT_AUTO +
+ ", yes: " + UiModeManager.MODE_NIGHT_YES +
+ ", no: " + UiModeManager.MODE_NIGHT_NO + ")");
+ final boolean lightWpTheme = mContext.getThemeResId()
+ == R.style.Theme_SystemUI_LightWallpaper;
+ pw.println(" light wallpaper theme: " + lightWpTheme);
+
+ if (mKeyguardIndicationController != null) {
+ mKeyguardIndicationController.dump(pw, args);
+ }
+
+ if (mScrimController != null) {
+ mScrimController.dump(pw, args);
+ }
+
+ if (mLightRevealScrim != null) {
+ pw.println(
+ "mLightRevealScrim.getRevealEffect(): " + mLightRevealScrim.getRevealEffect());
+ pw.println(
+ "mLightRevealScrim.getRevealAmount(): " + mLightRevealScrim.getRevealAmount());
+ }
+
+ if (mStatusBarKeyguardViewManager != null) {
+ mStatusBarKeyguardViewManager.dump(pw);
+ }
+
+ mNotificationsController.dump(pw, args, DUMPTRUCK);
+
+ if (DEBUG_GESTURES) {
+ pw.print(" status bar gestures: ");
+ mGestureRec.dump(pw, args);
+ }
+
+ if (mHeadsUpManager != null) {
+ mHeadsUpManager.dump(pw, args);
+ } else {
+ pw.println(" mHeadsUpManager: null");
+ }
+
+ if (mStatusBarTouchableRegionManager != null) {
+ mStatusBarTouchableRegionManager.dump(pw, args);
+ } else {
+ pw.println(" mStatusBarTouchableRegionManager: null");
+ }
+
+ if (mLightBarController != null) {
+ mLightBarController.dump(pw, args);
+ }
+
+ pw.println("SharedPreferences:");
+ for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
+ pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
+ }
+
+ pw.println("Camera gesture intents:");
+ pw.println(" Insecure camera: " + CameraIntents.getInsecureCameraIntent(mContext));
+ pw.println(" Secure camera: " + CameraIntents.getSecureCameraIntent(mContext));
+ pw.println(" Override package: "
+ + CameraIntents.getOverrideCameraPackage(mContext));
+ }
+
+ @Override
+ public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
+ makeStatusBarView(result);
+ mNotificationShadeWindowController.attach();
+ mStatusBarWindowController.attach();
+ }
+
+ // called by makeStatusbar and also by PhoneStatusBarView
+ void updateDisplaySize() {
+ mDisplay.getMetrics(mDisplayMetrics);
+ mDisplay.getSize(mCurrentDisplaySize);
+ if (DEBUG_GESTURES) {
+ mGestureRec.tag("display",
+ String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
+ }
+ }
+
+ @Override
+ public float getDisplayDensity() {
+ return mDisplayMetrics.density;
+ }
+
+ @Override
+ public float getDisplayWidth() {
+ return mDisplayMetrics.widthPixels;
+ }
+
+ @Override
+ public float getDisplayHeight() {
+ return mDisplayMetrics.heightPixels;
+ }
+
+ @Override
+ public int getRotation() {
+ return mDisplay.getRotation();
+ }
+
+ @Override
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @Override
+ public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
+ boolean dismissShade, int flags) {
+ startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
+ false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
+ flags, null /* animationController */, getActivityUserHandle(intent));
+ }
+
+ @Override
+ public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
+ boolean dismissShade) {
+ startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, 0);
+ }
+
+ @Override
+ public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
+ final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
+ final Callback callback, int flags,
+ @Nullable ActivityLaunchAnimator.Controller animationController,
+ final UserHandle userHandle) {
+ if (onlyProvisioned && !mDeviceProvisionedController.isDeviceProvisioned()) return;
+
+ final boolean willLaunchResolverActivity =
+ mActivityIntentHelper.wouldLaunchResolverActivity(intent,
+ mLockscreenUserManager.getCurrentUserId());
+
+ boolean animate =
+ animationController != null && !willLaunchResolverActivity && shouldAnimateLaunch(
+ true /* isActivityIntent */);
+ ActivityLaunchAnimator.Controller animController =
+ animationController != null ? wrapAnimationController(animationController,
+ dismissShade) : null;
+
+ // If we animate, we will dismiss the shade only once the animation is done. This is taken
+ // care of by the StatusBarLaunchAnimationController.
+ boolean dismissShadeDirectly = dismissShade && animController == null;
+
+ Runnable runnable = () -> {
+ mAssistManagerLazy.get().hideAssist();
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.addFlags(flags);
+ int[] result = new int[]{ActivityManager.START_CANCELED};
+
+ mActivityLaunchAnimator.startIntentWithAnimation(animController,
+ animate, intent.getPackage(), (adapter) -> {
+ ActivityOptions options = new ActivityOptions(
+ CentralSurfaces.getActivityOptions(mDisplayId, adapter));
+ options.setDisallowEnterPictureInPictureWhileLaunching(
+ disallowEnterPictureInPictureWhileLaunching);
+ if (CameraIntents.isInsecureCameraIntent(intent)) {
+ // Normally an activity will set it's requested rotation
+ // animation on its window. However when launching an activity
+ // causes the orientation to change this is too late. In these cases
+ // the default animation is used. This doesn't look good for
+ // the camera (as it rotates the camera contents out of sync
+ // with physical reality). So, we ask the WindowManager to
+ // force the crossfade animation if an orientation change
+ // happens to occur during the launch.
+ options.setRotationAnimationHint(
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
+ }
+ if (Settings.Panel.ACTION_VOLUME.equals(intent.getAction())) {
+ // Settings Panel is implemented as activity(not a dialog), so
+ // underlying app is paused and may enter picture-in-picture mode
+ // as a result.
+ // So we need to disable picture-in-picture mode here
+ // if it is volume panel.
+ options.setDisallowEnterPictureInPictureWhileLaunching(true);
+ }
+
+ try {
+ result[0] = ActivityTaskManager.getService().startActivityAsUser(
+ null, mContext.getBasePackageName(),
+ mContext.getAttributionTag(),
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
+ options.toBundle(), userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to start activity", e);
+ }
+ return result[0];
+ });
+
+ if (callback != null) {
+ callback.onActivityStarted(result[0]);
+ }
+ };
+ Runnable cancelRunnable = () -> {
+ if (callback != null) {
+ callback.onActivityStarted(ActivityManager.START_CANCELED);
+ }
+ };
+ executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly,
+ willLaunchResolverActivity, true /* deferred */, animate);
+ }
+
+ @Nullable
+ private ActivityLaunchAnimator.Controller wrapAnimationController(
+ ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
+ View rootView = animationController.getLaunchContainer().getRootView();
+
+ Optional<ActivityLaunchAnimator.Controller> controllerFromStatusBar =
+ mStatusBarWindowController.wrapAnimationControllerIfInStatusBar(
+ rootView, animationController);
+ if (controllerFromStatusBar.isPresent()) {
+ return controllerFromStatusBar.get();
+ }
+
+ if (dismissShade) {
+ // If the view is not in the status bar, then we are animating a view in the shade.
+ // We have to make sure that we collapse it when the animation ends or is cancelled.
+ return new StatusBarLaunchAnimatorController(animationController, this,
+ true /* isLaunchForActivity */);
+ }
+
+ return animationController;
+ }
+
+ @Override
+ public void readyForKeyguardDone() {
+ mStatusBarKeyguardViewManager.readyForKeyguardDone();
+ }
+
+ @Override
+ public void executeRunnableDismissingKeyguard(final Runnable runnable,
+ final Runnable cancelAction,
+ final boolean dismissShade,
+ final boolean afterKeyguardGone,
+ final boolean deferred) {
+ executeRunnableDismissingKeyguard(runnable, cancelAction, dismissShade, afterKeyguardGone,
+ deferred, false /* willAnimateOnKeyguard */);
+ }
+
+ @Override
+ public void executeRunnableDismissingKeyguard(final Runnable runnable,
+ final Runnable cancelAction,
+ final boolean dismissShade,
+ final boolean afterKeyguardGone,
+ final boolean deferred,
+ final boolean willAnimateOnKeyguard) {
+ OnDismissAction onDismissAction = new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ if (runnable != null) {
+ if (mStatusBarKeyguardViewManager.isShowing()
+ && mStatusBarKeyguardViewManager.isOccluded()) {
+ mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
+ } else {
+ mMainExecutor.execute(runnable);
+ }
+ }
+ if (dismissShade) {
+ if (mExpandedVisible && !mBouncerShowing) {
+ mShadeController.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+ true /* force */, true /* delayed*/);
+ } else {
+
+ // Do it after DismissAction has been processed to conserve the needed
+ // ordering.
+ mMainExecutor.execute(mShadeController::runPostCollapseRunnables);
+ }
+ } else if (CentralSurfacesImpl.this.isInLaunchTransition()
+ && mNotificationPanelViewController.isLaunchTransitionFinished()) {
+
+ // We are not dismissing the shade, but the launch transition is already
+ // finished,
+ // so nobody will call readyForKeyguardDone anymore. Post it such that
+ // keyguardDonePending gets called first.
+ mMainExecutor.execute(mStatusBarKeyguardViewManager::readyForKeyguardDone);
+ }
+ return deferred;
+ }
+
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return willAnimateOnKeyguard;
+ }
+ };
+ dismissKeyguardThenExecute(onDismissAction, cancelAction, afterKeyguardGone);
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Trace.beginSection("CentralSurfaces#onReceive");
+ if (DEBUG) Log.v(TAG, "onReceive: " + intent);
+ String action = intent.getAction();
+ String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ KeyboardShortcuts.dismiss();
+ mRemoteInputManager.closeRemoteInputs();
+ if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
+ int flags = CommandQueue.FLAG_EXCLUDE_NONE;
+ if (reason != null) {
+ if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
+ flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
+ }
+ // Do not collapse notifications when starting dreaming if the notifications
+ // shade is used for the screen off animation. It might require expanded
+ // state for the scrims to be visible
+ if (reason.equals(SYSTEM_DIALOG_REASON_DREAM)
+ && mScreenOffAnimationController.shouldExpandNotifications()) {
+ flags |= CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL;
+ }
+ }
+ mShadeController.animateCollapsePanels(flags);
+ }
+ } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ if (mNotificationShadeWindowController != null) {
+ mNotificationShadeWindowController.setNotTouchable(false);
+ }
+ finishBarAnimations();
+ resetUserExpandedStates();
+ }
+ Trace.endSection();
+ }
+ };
+
+ private final BroadcastReceiver mDemoReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) Log.v(TAG, "onReceive: " + intent);
+ String action = intent.getAction();
+ if (ACTION_FAKE_ARTWORK.equals(action)) {
+ if (DEBUG_MEDIA_FAKE_ARTWORK) {
+ mPresenter.updateMediaMetaData(true, true);
+ }
+ }
+ }
+ };
+
+ @Override
+ public void resetUserExpandedStates() {
+ mNotificationsController.resetUserExpandedStates();
+ }
+
+ private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen,
+ boolean afterKeyguardGone) {
+ if (mStatusBarKeyguardViewManager.isShowing() && requiresShadeOpen) {
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+ }
+ dismissKeyguardThenExecute(action, null /* cancelAction */,
+ afterKeyguardGone /* afterKeyguardGone */);
+ }
+
+ protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
+ dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone);
+ }
+
+ @Override
+ public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
+ boolean afterKeyguardGone) {
+ if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
+ && mKeyguardStateController.canDismissLockScreen()
+ && !mStatusBarStateController.leaveOpenOnKeyguardHide()
+ && mDozeServiceHost.isPulsing()) {
+ // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse.
+ // TODO: Factor this transition out of BiometricUnlockController.
+ mBiometricUnlockController.startWakeAndUnlock(
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
+ }
+ if (mStatusBarKeyguardViewManager.isShowing()) {
+ mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
+ afterKeyguardGone);
+ } else {
+ action.onDismiss();
+ }
+ }
+ /**
+ * Notify the shade controller that the current user changed
+ *
+ * @param newUserId userId of the new user
+ */
+ @Override
+ public void setLockscreenUser(int newUserId) {
+ if (mLockscreenWallpaper != null) {
+ mLockscreenWallpaper.setCurrentUser(newUserId);
+ }
+ mScrimController.setCurrentUser(newUserId);
+ if (mWallpaperSupported) {
+ mWallpaperChangedReceiver.onReceive(mContext, null);
+ }
+ }
+
+ /**
+ * Reload some of our resources when the configuration changes.
+ *
+ * We don't reload everything when the configuration changes -- we probably
+ * should, but getting that smooth is tough. Someday we'll fix that. In the
+ * meantime, just update the things that we know change.
+ */
+ void updateResources() {
+ // Update the quick setting tiles
+ if (mQSPanelController != null) {
+ mQSPanelController.updateResources();
+ }
+
+ if (mStatusBarWindowController != null) {
+ mStatusBarWindowController.refreshStatusBarHeight();
+ }
+
+ if (mNotificationPanelViewController != null) {
+ mNotificationPanelViewController.updateResources();
+ }
+ if (mBrightnessMirrorController != null) {
+ mBrightnessMirrorController.updateResources();
+ }
+ if (mStatusBarKeyguardViewManager != null) {
+ mStatusBarKeyguardViewManager.updateResources();
+ }
+
+ mPowerButtonReveal = new PowerButtonReveal(mContext.getResources().getDimensionPixelSize(
+ com.android.systemui.R.dimen.physical_power_button_center_screen_location_y));
+ }
+
+ // Visibility reporting
+ protected void handleVisibleToUserChanged(boolean visibleToUser) {
+ if (visibleToUser) {
+ handleVisibleToUserChangedImpl(visibleToUser);
+ mNotificationLogger.startNotificationLogging();
+ } else {
+ mNotificationLogger.stopNotificationLogging();
+ handleVisibleToUserChangedImpl(visibleToUser);
+ }
+ }
+
+ // Visibility reporting
+ void handleVisibleToUserChangedImpl(boolean visibleToUser) {
+ if (visibleToUser) {
+ /* The LEDs are turned off when the notification panel is shown, even just a little bit.
+ * See also CentralSurfaces.setPanelExpanded for another place where we attempt to do
+ * this.
+ */
+ boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
+ boolean clearNotificationEffects =
+ !mPresenter.isPresenterFullyCollapsed() &&
+ (mState == StatusBarState.SHADE
+ || mState == StatusBarState.SHADE_LOCKED);
+ int notificationLoad = mNotificationsController.getActiveNotificationsCount();
+ if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
+ notificationLoad = 1;
+ }
+ final int finalNotificationLoad = notificationLoad;
+ mUiBgExecutor.execute(() -> {
+ try {
+ mBarService.onPanelRevealed(clearNotificationEffects,
+ finalNotificationLoad);
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ }
+ });
+ } else {
+ mUiBgExecutor.execute(() -> {
+ try {
+ mBarService.onPanelHidden();
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ }
+ });
+ }
+
+ }
+
+ private void logStateToEventlog() {
+ boolean isShowing = mStatusBarKeyguardViewManager.isShowing();
+ boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded();
+ boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
+ boolean isSecure = mKeyguardStateController.isMethodSecure();
+ boolean unlocked = mKeyguardStateController.canDismissLockScreen();
+ int stateFingerprint = getLoggingFingerprint(mState,
+ isShowing,
+ isOccluded,
+ isBouncerShowing,
+ isSecure,
+ unlocked);
+ if (stateFingerprint != mLastLoggedStateFingerprint) {
+ if (mStatusBarStateLog == null) {
+ mStatusBarStateLog = new LogMaker(MetricsEvent.VIEW_UNKNOWN);
+ }
+ mMetricsLogger.write(mStatusBarStateLog
+ .setCategory(isBouncerShowing ? MetricsEvent.BOUNCER : MetricsEvent.LOCKSCREEN)
+ .setType(isShowing ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
+ .setSubtype(isSecure ? 1 : 0));
+ EventLogTags.writeSysuiStatusBarState(mState,
+ isShowing ? 1 : 0,
+ isOccluded ? 1 : 0,
+ isBouncerShowing ? 1 : 0,
+ isSecure ? 1 : 0,
+ unlocked ? 1 : 0);
+ mLastLoggedStateFingerprint = stateFingerprint;
+
+ StringBuilder uiEventValueBuilder = new StringBuilder();
+ uiEventValueBuilder.append(isBouncerShowing ? "BOUNCER" : "LOCKSCREEN");
+ uiEventValueBuilder.append(isShowing ? "_OPEN" : "_CLOSE");
+ uiEventValueBuilder.append(isSecure ? "_SECURE" : "_INSECURE");
+ sUiEventLogger.log(StatusBarUiEvent.valueOf(uiEventValueBuilder.toString()));
+ }
+ }
+
+ /**
+ * Returns a fingerprint of fields logged to eventlog
+ */
+ private static int getLoggingFingerprint(int statusBarState, boolean keyguardShowing,
+ boolean keyguardOccluded, boolean bouncerShowing, boolean secure,
+ boolean currentlyInsecure) {
+ // Reserve 8 bits for statusBarState. We'll never go higher than
+ // that, right? Riiiight.
+ return (statusBarState & 0xFF)
+ | ((keyguardShowing ? 1 : 0) << 8)
+ | ((keyguardOccluded ? 1 : 0) << 9)
+ | ((bouncerShowing ? 1 : 0) << 10)
+ | ((secure ? 1 : 0) << 11)
+ | ((currentlyInsecure ? 1 : 0) << 12);
+ }
+
+ @Override
+ public void postQSRunnableDismissingKeyguard(final Runnable runnable) {
+ mMainExecutor.execute(() -> {
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+ executeRunnableDismissingKeyguard(
+ () -> mMainExecutor.execute(runnable), null, false, false, false);
+ });
+ }
+
+ @Override
+ public void postStartActivityDismissingKeyguard(PendingIntent intent) {
+ postStartActivityDismissingKeyguard(intent, null /* animationController */);
+ }
+
+ @Override
+ public void postStartActivityDismissingKeyguard(final PendingIntent intent,
+ @Nullable ActivityLaunchAnimator.Controller animationController) {
+ mMainExecutor.execute(() -> startPendingIntentDismissingKeyguard(intent,
+ null /* intentSentUiThreadCallback */, animationController));
+ }
+
+ @Override
+ public void postStartActivityDismissingKeyguard(final Intent intent, int delay) {
+ postStartActivityDismissingKeyguard(intent, delay, null /* animationController */);
+ }
+
+ @Override
+ public void postStartActivityDismissingKeyguard(Intent intent, int delay,
+ @Nullable ActivityLaunchAnimator.Controller animationController) {
+ mMainExecutor.executeDelayed(
+ () ->
+ startActivityDismissingKeyguard(intent, true /* onlyProvisioned */,
+ true /* dismissShade */,
+ false /* disallowEnterPictureInPictureWhileLaunching */,
+ null /* callback */,
+ 0 /* flags */,
+ animationController,
+ getActivityUserHandle(intent)),
+ delay);
+ }
+
+ @Override
+ public void showKeyguard() {
+ mStatusBarStateController.setKeyguardRequested(true);
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
+ updateIsKeyguard();
+ mAssistManagerLazy.get().onLockscreenShown();
+ }
+
+ @Override
+ public boolean hideKeyguard() {
+ mStatusBarStateController.setKeyguardRequested(false);
+ return updateIsKeyguard();
+ }
+
+ @Override
+ public boolean updateIsKeyguard() {
+ return updateIsKeyguard(false /* forceStateChange */);
+ }
+
+ @Override
+ public boolean updateIsKeyguard(boolean forceStateChange) {
+ boolean wakeAndUnlocking = mBiometricUnlockController.isWakeAndUnlock();
+
+ // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise
+ // there's no surface we can show to the user. Note that the device goes fully interactive
+ // late in the transition, so we also allow the device to start dozing once the screen has
+ // turned off fully.
+ boolean keyguardForDozing = mDozeServiceHost.getDozingRequested()
+ && (!mDeviceInteractive || (isGoingToSleep()
+ && (isScreenFullyOff()
+ || (mKeyguardStateController.isShowing() && !isOccluded()))));
+ boolean isWakingAndOccluded = isOccluded() && isWakingOrAwake();
+ boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested()
+ || keyguardForDozing) && !wakeAndUnlocking && !isWakingAndOccluded;
+ if (keyguardForDozing) {
+ updatePanelExpansionForKeyguard();
+ }
+ if (shouldBeKeyguard) {
+ if (mScreenOffAnimationController.isKeyguardShowDelayed()
+ || (isGoingToSleep()
+ && mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_TURNING_OFF)) {
+ // Delay showing the keyguard until screen turned off.
+ } else {
+ showKeyguardImpl();
+ }
+ } else {
+ // During folding a foldable device this might be called as a result of
+ // 'onScreenTurnedOff' call for the inner display.
+ // In this case:
+ // * When phone is locked on folding: it doesn't make sense to hide keyguard as it
+ // will be immediately locked again
+ // * When phone is unlocked: we still don't want to execute hiding of the keyguard
+ // as the animation could prepare 'fake AOD' interface (without actually
+ // transitioning to keyguard state) and this might reset the view states
+ if (!mScreenOffAnimationController.isKeyguardHideDelayed()) {
+ return hideKeyguardImpl(forceStateChange);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void showKeyguardImpl() {
+ Trace.beginSection("CentralSurfaces#showKeyguard");
+ if (mKeyguardStateController.isLaunchTransitionFadingAway()) {
+ mNotificationPanelViewController.cancelAnimation();
+ onLaunchTransitionFadingEnded();
+ }
+ mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+ if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) {
+ mStatusBarStateController.setState(StatusBarState.KEYGUARD);
+ }
+ updatePanelExpansionForKeyguard();
+ Trace.endSection();
+ }
+
+ private void updatePanelExpansionForKeyguard() {
+ if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode()
+ != BiometricUnlockController.MODE_WAKE_AND_UNLOCK && !mBouncerShowing) {
+ mShadeController.instantExpandNotificationsPanel();
+ }
+ }
+
+ private void onLaunchTransitionFadingEnded() {
+ mNotificationPanelViewController.resetAlpha();
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
+ releaseGestureWakeLock();
+ runLaunchTransitionEndRunnable();
+ mKeyguardStateController.setLaunchTransitionFadingAway(false);
+ mPresenter.updateMediaMetaData(true /* metaDataChanged */, true);
+ }
+
+ @Override
+ public boolean isInLaunchTransition() {
+ return mNotificationPanelViewController.isLaunchTransitionRunning()
+ || mNotificationPanelViewController.isLaunchTransitionFinished();
+ }
+
+ /**
+ * Fades the content of the keyguard away after the launch transition is done.
+ *
+ * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
+ * starts
+ * @param endRunnable the runnable to be run when the transition is done. Will not run
+ * if the transition is cancelled, instead cancelRunnable will run
+ * @param cancelRunnable the runnable to be run if the transition is cancelled
+ */
+ @Override
+ public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
+ Runnable endRunnable, Runnable cancelRunnable) {
+ mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+ mLaunchTransitionEndRunnable = endRunnable;
+ mLaunchTransitionCancelRunnable = cancelRunnable;
+ Runnable hideRunnable = () -> {
+ mKeyguardStateController.setLaunchTransitionFadingAway(true);
+ if (beforeFading != null) {
+ beforeFading.run();
+ }
+ updateScrimController();
+ mPresenter.updateMediaMetaData(false, true);
+ mNotificationPanelViewController.resetAlpha();
+ mNotificationPanelViewController.fadeOut(
+ FADE_KEYGUARD_START_DELAY, FADE_KEYGUARD_DURATION,
+ this::onLaunchTransitionFadingEnded);
+ mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
+ LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
+ };
+ if (mNotificationPanelViewController.isLaunchTransitionRunning()) {
+ mNotificationPanelViewController.setLaunchTransitionEndRunnable(hideRunnable);
+ } else {
+ hideRunnable.run();
+ }
+ }
+
+ private void cancelAfterLaunchTransitionRunnables() {
+ if (mLaunchTransitionCancelRunnable != null) {
+ mLaunchTransitionCancelRunnable.run();
+ }
+ mLaunchTransitionEndRunnable = null;
+ mLaunchTransitionCancelRunnable = null;
+ mNotificationPanelViewController.setLaunchTransitionEndRunnable(null);
+ }
+
+ /**
+ * Fades the content of the Keyguard while we are dozing and makes it invisible when finished
+ * fading.
+ */
+ @Override
+ public void fadeKeyguardWhilePulsing() {
+ mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
+ ()-> {
+ hideKeyguard();
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ }).start();
+ }
+
+ /**
+ * Plays the animation when an activity that was occluding Keyguard goes away.
+ */
+ @Override
+ public void animateKeyguardUnoccluding() {
+ mNotificationPanelViewController.setExpandedFraction(0f);
+ mCommandQueueCallbacks.animateExpandNotificationsPanel();
+ mScrimController.setUnocclusionAnimationRunning(true);
+ }
+
+ /**
+ * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that
+ * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen
+ * because the launched app crashed or something else went wrong.
+ */
+ @Override
+ public void startLaunchTransitionTimeout() {
+ mMessageRouter.sendMessageDelayed(
+ MSG_LAUNCH_TRANSITION_TIMEOUT, LAUNCH_TRANSITION_TIMEOUT_MS);
+ }
+
+ private void onLaunchTransitionTimeout() {
+ Log.w(TAG, "Launch transition: Timeout!");
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
+ releaseGestureWakeLock();
+ mNotificationPanelViewController.resetViews(false /* animate */);
+ }
+
+ private void runLaunchTransitionEndRunnable() {
+ mLaunchTransitionCancelRunnable = null;
+ if (mLaunchTransitionEndRunnable != null) {
+ Runnable r = mLaunchTransitionEndRunnable;
+
+ // mLaunchTransitionEndRunnable might call showKeyguard, which would execute it again,
+ // which would lead to infinite recursion. Protect against it.
+ mLaunchTransitionEndRunnable = null;
+ r.run();
+ }
+ }
+
+ /**
+ * @return true if we would like to stay in the shade, false if it should go away entirely
+ */
+ @Override
+ public boolean hideKeyguardImpl(boolean forceStateChange) {
+ Trace.beginSection("CentralSurfaces#hideKeyguard");
+ boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
+ int previousState = mStatusBarStateController.getState();
+ if (!(mStatusBarStateController.setState(StatusBarState.SHADE, forceStateChange))) {
+ //TODO: StatusBarStateController should probably know about hiding the keyguard and
+ // notify listeners.
+
+ // If the state didn't change, we may still need to update public mode
+ mLockscreenUserManager.updatePublicMode();
+ }
+ if (mStatusBarStateController.leaveOpenOnKeyguardHide()) {
+ if (!mStatusBarStateController.isKeyguardRequested()) {
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
+ }
+ long delay = mKeyguardStateController.calculateGoingToFullShadeDelay();
+ mLockscreenShadeTransitionController.onHideKeyguard(delay, previousState);
+
+ // Disable layout transitions in navbar for this transition because the load is just
+ // too heavy for the CPU and GPU on any device.
+ mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay);
+ } else if (!mNotificationPanelViewController.isCollapsing()) {
+ instantCollapseNotificationPanel();
+ }
+
+ // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
+ // visibilities so next time we open the panel we know the correct height already.
+ if (mQSPanelController != null) {
+ mQSPanelController.refreshAllTiles();
+ }
+ mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+ releaseGestureWakeLock();
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
+ mNotificationPanelViewController.resetAlpha();
+ mNotificationPanelViewController.resetTranslation();
+ mNotificationPanelViewController.resetViewGroupFade();
+ updateDozingState();
+ updateScrimController();
+ Trace.endSection();
+ return staying;
+ }
+
+ private void releaseGestureWakeLock() {
+ if (mGestureWakeLock.isHeld()) {
+ mGestureWakeLock.release();
+ }
+ }
+
+ /**
+ * Notifies the status bar that Keyguard is going away very soon.
+ */
+ @Override
+ public void keyguardGoingAway() {
+ // Treat Keyguard exit animation as an app transition to achieve nice transition for status
+ // bar.
+ mKeyguardStateController.notifyKeyguardGoingAway(true);
+ mCommandQueue.appTransitionPending(mDisplayId, true /* forced */);
+ updateScrimController();
+ }
+
+ /**
+ * Notifies the status bar the Keyguard is fading away with the specified timings.
+ * @param startTime the start time of the animations in uptime millis
+ * @param delay the precalculated animation delay in milliseconds
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds
+ * @param isBypassFading is this a fading away animation while bypassing
+ */
+ @Override
+ public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration,
+ boolean isBypassFading) {
+ mCommandQueue.appTransitionStarting(mDisplayId, startTime + fadeoutDuration
+ - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
+ LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, fadeoutDuration > 0 /* animate */);
+ mCommandQueue.appTransitionStarting(mDisplayId,
+ startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
+ LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
+ mKeyguardStateController.notifyKeyguardFadingAway(delay, fadeoutDuration, isBypassFading);
+ }
+
+ /**
+ * Notifies that the Keyguard fading away animation is done.
+ */
+ @Override
+ public void finishKeyguardFadingAway() {
+ mKeyguardStateController.notifyKeyguardDoneFading();
+ mScrimController.setExpansionAffectsAlpha(true);
+
+ // If the device was re-locked while unlocking, we might have a pending lock that was
+ // delayed because the keyguard was in the middle of going away.
+ mKeyguardViewMediator.maybeHandlePendingLock();
+ }
+
+ /**
+ * Switches theme from light to dark and vice-versa.
+ */
+ protected void updateTheme() {
+ // Set additional scrim only if the lock and system wallpaper are different to prevent
+ // applying the dimming effect twice.
+ mUiBgExecutor.execute(() -> {
+ float dimAmount = 0f;
+ if (mWallpaperManager.lockScreenWallpaperExists()) {
+ dimAmount = mWallpaperManager.getWallpaperDimAmount();
+ }
+ final float scrimDimAmount = dimAmount;
+ mMainExecutor.execute(() -> {
+ mScrimController.setAdditionalScrimBehindAlphaKeyguard(scrimDimAmount);
+ mScrimController.applyCompositeAlphaOnScrimBehindKeyguard();
+ });
+ });
+
+ // Lock wallpaper defines the color of the majority of the views, hence we'll use it
+ // to set our default theme.
+ final boolean lockDarkText = mColorExtractor.getNeutralColors().supportsDarkText();
+ final int themeResId = lockDarkText ? R.style.Theme_SystemUI_LightWallpaper
+ : R.style.Theme_SystemUI;
+ if (mContext.getThemeResId() != themeResId) {
+ mContext.setTheme(themeResId);
+ mConfigurationController.notifyThemeChanged();
+ }
+ }
+
+ private void updateDozingState() {
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
+ Trace.beginSection("CentralSurfaces#updateDozingState");
+
+ boolean visibleNotOccluded = mStatusBarKeyguardViewManager.isShowing()
+ && !mStatusBarKeyguardViewManager.isOccluded();
+ // If we're dozing and we'll be animating the screen off, the keyguard isn't currently
+ // visible but will be shortly for the animation, so we should proceed as if it's visible.
+ boolean visibleNotOccludedOrWillBe =
+ visibleNotOccluded || (mDozing && mDozeParameters.shouldDelayKeyguardShow());
+
+ boolean wakeAndUnlock = mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+ boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock)
+ || (mDozing && mDozeParameters.shouldControlScreenOff()
+ && visibleNotOccludedOrWillBe);
+
+ mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation);
+ updateQsExpansionEnabled();
+ Trace.endSection();
+ }
+
+ @Override
+ public void userActivity() {
+ if (mState == StatusBarState.KEYGUARD) {
+ mKeyguardViewMediatorCallback.userActivity();
+ }
+ }
+
+ @Override
+ public boolean interceptMediaKey(KeyEvent event) {
+ return mState == StatusBarState.KEYGUARD
+ && mStatusBarKeyguardViewManager.interceptMediaKey(event);
+ }
+
+ /**
+ * While IME is active and a BACK event is detected, check with
+ * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event
+ * should be handled before routing to IME, in order to prevent the user having to hit back
+ * twice to exit bouncer.
+ */
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_BACK:
+ if (mState == StatusBarState.KEYGUARD
+ && mStatusBarKeyguardViewManager.dispatchBackKeyEventPreIme()) {
+ return onBackPressed();
+ }
+ }
+ return false;
+ }
+
+ protected boolean shouldUnlockOnMenuPressed() {
+ return mDeviceInteractive && mState != StatusBarState.SHADE
+ && mStatusBarKeyguardViewManager.shouldDismissOnMenuPressed();
+ }
+
+ @Override
+ public boolean onMenuPressed() {
+ if (shouldUnlockOnMenuPressed()) {
+ mShadeController.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void endAffordanceLaunch() {
+ releaseGestureWakeLock();
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
+ }
+
+ @Override
+ public boolean onBackPressed() {
+ boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
+ if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) {
+ if (isScrimmedBouncer) {
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
+ } else {
+ mNotificationPanelViewController.expandWithoutQs();
+ }
+ return true;
+ }
+ if (mNotificationPanelViewController.isQsCustomizing()) {
+ mNotificationPanelViewController.closeQsCustomizer();
+ return true;
+ }
+ if (mNotificationPanelViewController.isQsExpanded()) {
+ if (mNotificationPanelViewController.isQsDetailShowing()) {
+ mNotificationPanelViewController.closeQsDetail();
+ } else {
+ mNotificationPanelViewController.animateCloseQs(false /* animateAway */);
+ }
+ return true;
+ }
+ if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) {
+ return true;
+ }
+ if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
+ if (mNotificationPanelViewController.canPanelBeCollapsed()) {
+ mShadeController.animateCollapsePanels();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onSpacePressed() {
+ if (mDeviceInteractive && mState != StatusBarState.SHADE) {
+ mShadeController.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
+ return true;
+ }
+ return false;
+ }
+
+ private void showBouncerOrLockScreenIfKeyguard() {
+ // If the keyguard is animating away, we aren't really the keyguard anymore and should not
+ // show the bouncer/lockscreen.
+ if (!mKeyguardViewMediator.isHiding()
+ && !mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
+ if (mState == StatusBarState.SHADE_LOCKED
+ && mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
+ // shade is showing while locked on the keyguard, so go back to showing the
+ // lock screen where users can use the UDFPS affordance to enter the device
+ mStatusBarKeyguardViewManager.reset(true);
+ } else if ((mState == StatusBarState.KEYGUARD
+ && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing())
+ || mState == StatusBarState.SHADE_LOCKED) {
+ mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
+ }
+ }
+ }
+
+ /**
+ * Show the bouncer if we're currently on the keyguard or shade locked and aren't hiding.
+ * @param performAction the action to perform when the bouncer is dismissed.
+ * @param cancelAction the action to perform when unlock is aborted.
+ */
+ @Override
+ public void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction,
+ Runnable cancelAction) {
+ if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
+ && !mKeyguardViewMediator.isHiding()) {
+ mStatusBarKeyguardViewManager.dismissWithAction(performAction, cancelAction,
+ false /* afterKeyguardGone */);
+ } else if (cancelAction != null) {
+ cancelAction.run();
+ }
+ }
+
+ @Override
+ public void instantCollapseNotificationPanel() {
+ mNotificationPanelViewController.instantCollapse();
+ mShadeController.runPostCollapseRunnables();
+ }
+
+ /**
+ * Collapse the panel directly if we are on the main thread, post the collapsing on the main
+ * thread if we are not.
+ */
+ @Override
+ public void collapsePanelOnMainThread() {
+ if (Looper.getMainLooper().isCurrentThread()) {
+ mShadeController.collapsePanel();
+ } else {
+ mContext.getMainExecutor().execute(mShadeController::collapsePanel);
+ }
+ }
+
+ /** Collapse the panel. The collapsing will be animated for the given {@code duration}. */
+ @Override
+ public void collapsePanelWithDuration(int duration) {
+ mNotificationPanelViewController.collapseWithDuration(duration);
+ }
+
+ /**
+ * Updates the light reveal effect to reflect the reason we're waking or sleeping (for example,
+ * from the power button).
+ * @param wakingUp Whether we're updating because we're waking up (true) or going to sleep
+ * (false).
+ */
+ private void updateRevealEffect(boolean wakingUp) {
+ if (mLightRevealScrim == null) {
+ return;
+ }
+
+ final boolean wakingUpFromPowerButton = wakingUp
+ && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
+ && mWakefulnessLifecycle.getLastWakeReason()
+ == PowerManager.WAKE_REASON_POWER_BUTTON;
+ final boolean sleepingFromPowerButton = !wakingUp
+ && mWakefulnessLifecycle.getLastSleepReason()
+ == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON;
+
+ if (wakingUpFromPowerButton || sleepingFromPowerButton) {
+ mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
+ mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
+ } else if (!wakingUp || !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
+ // If we're going to sleep, but it's not from the power button, use the default reveal.
+ // If we're waking up, only use the default reveal if the biometric controller didn't
+ // already set it to the circular reveal because we're waking up from a fingerprint/face
+ // auth.
+ mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
+ mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
+ }
+ }
+
+ @Override
+ public LightRevealScrim getLightRevealScrim() {
+ return mLightRevealScrim;
+ }
+
+ @Override
+ public void onTrackingStarted() {
+ mShadeController.runPostCollapseRunnables();
+ }
+
+ @Override
+ public void onClosingFinished() {
+ mShadeController.runPostCollapseRunnables();
+ if (!mPresenter.isPresenterFullyCollapsed()) {
+ // if we set it not to be focusable when collapsing, we have to undo it when we aborted
+ // the closing
+ mNotificationShadeWindowController.setNotificationShadeFocusable(true);
+ }
+ }
+
+ @Override
+ public void onUnlockHintStarted() {
+ mFalsingCollector.onUnlockHintStarted();
+ mKeyguardIndicationController.showActionToUnlock();
+ }
+
+ @Override
+ public void onHintFinished() {
+ // Delay the reset a bit so the user can read the text.
+ mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
+ }
+
+ @Override
+ public void onCameraHintStarted() {
+ mFalsingCollector.onCameraHintStarted();
+ mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
+ }
+
+ @Override
+ public void onVoiceAssistHintStarted() {
+ mFalsingCollector.onLeftAffordanceHintStarted();
+ mKeyguardIndicationController.showTransientIndication(R.string.voice_hint);
+ }
+
+ @Override
+ public void onPhoneHintStarted() {
+ mFalsingCollector.onLeftAffordanceHintStarted();
+ mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
+ }
+
+ @Override
+ public void onTrackingStopped(boolean expand) {
+ if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+ if (!expand && !mKeyguardStateController.canDismissLockScreen()) {
+ mStatusBarKeyguardViewManager.showBouncer(false /* scrimmed */);
+ }
+ }
+ }
+
+ // TODO: Figure out way to remove these.
+ @Override
+ public NavigationBarView getNavigationBarView() {
+ return mNavigationBarController.getNavigationBarView(mDisplayId);
+ }
+
+ @Override
+ public boolean isOverviewEnabled() {
+ return mNavigationBarController.isOverviewEnabled(mDisplayId);
+ }
+
+ @Override
+ public void showPinningEnterExitToast(boolean entering) {
+ mNavigationBarController.showPinningEnterExitToast(mDisplayId, entering);
+ }
+
+ @Override
+ public void showPinningEscapeToast() {
+ mNavigationBarController.showPinningEscapeToast(mDisplayId);
+ }
+
+ /**
+ * TODO: Remove this method. Views should not be passed forward. Will cause theme issues.
+ * @return bottom area view
+ */
+ @Override
+ public KeyguardBottomAreaView getKeyguardBottomAreaView() {
+ return mNotificationPanelViewController.getKeyguardBottomAreaView();
+ }
+
+ /**
+ * Propagation of the bouncer state, indicating that it's fully visible.
+ */
+ @Override
+ public void setBouncerShowing(boolean bouncerShowing) {
+ mBouncerShowing = bouncerShowing;
+ mKeyguardBypassController.setBouncerShowing(bouncerShowing);
+ mPulseExpansionHandler.setBouncerShowing(bouncerShowing);
+ setBouncerShowingForStatusBarComponents(bouncerShowing);
+ mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
+ updateScrimController();
+ if (!mBouncerShowing) {
+ updatePanelExpansionForKeyguard();
+ }
+ }
+
+ /**
+ * Propagate the bouncer state to status bar components.
+ *
+ * Separate from {@link #setBouncerShowing} because we sometimes re-create the status bar and
+ * should update only the status bar components.
+ */
+ private void setBouncerShowingForStatusBarComponents(boolean bouncerShowing) {
+ int importance = bouncerShowing
+ ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+ if (mPhoneStatusBarViewController != null) {
+ mPhoneStatusBarViewController.setImportantForAccessibility(importance);
+ }
+ mNotificationPanelViewController.setImportantForAccessibility(importance);
+ mNotificationPanelViewController.setBouncerShowing(bouncerShowing);
+ }
+
+ /**
+ * Collapses the notification shade if it is tracking or expanded.
+ */
+ @Override
+ public void collapseShade() {
+ if (mNotificationPanelViewController.isTracking()) {
+ mNotificationShadeWindowViewController.cancelCurrentTouch();
+ }
+ if (mPanelExpanded && mState == StatusBarState.SHADE) {
+ mShadeController.animateCollapsePanels();
+ }
+ }
+
+ @VisibleForTesting
+ final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onFinishedGoingToSleep() {
+ mNotificationPanelViewController.onAffordanceLaunchEnded();
+ releaseGestureWakeLock();
+ mLaunchCameraWhenFinishedWaking = false;
+ mDeviceInteractive = false;
+ mWakeUpComingFromTouch = false;
+ mWakeUpTouchLocation = null;
+ updateVisibleToUser();
+
+ updateNotificationPanelTouchState();
+ mNotificationShadeWindowViewController.cancelCurrentTouch();
+ if (mLaunchCameraOnFinishedGoingToSleep) {
+ mLaunchCameraOnFinishedGoingToSleep = false;
+
+ // This gets executed before we will show Keyguard, so post it in order that the state
+ // is correct.
+ mMainExecutor.execute(() -> mCommandQueueCallbacks.onCameraLaunchGestureDetected(
+ mLastCameraLaunchSource));
+ }
+
+ if (mLaunchEmergencyActionOnFinishedGoingToSleep) {
+ mLaunchEmergencyActionOnFinishedGoingToSleep = false;
+
+ // This gets executed before we will show Keyguard, so post it in order that the
+ // state is correct.
+ mMainExecutor.execute(
+ () -> mCommandQueueCallbacks.onEmergencyActionLaunchGestureDetected());
+ }
+ updateIsKeyguard();
+ }
+
+ @Override
+ public void onStartedGoingToSleep() {
+ String tag = "CentralSurfaces#onStartedGoingToSleep";
+ DejankUtils.startDetectingBlockingIpcs(tag);
+
+ // cancel stale runnables that could put the device in the wrong state
+ cancelAfterLaunchTransitionRunnables();
+
+ updateRevealEffect(false /* wakingUp */);
+ updateNotificationPanelTouchState();
+ maybeEscalateHeadsUp();
+ dismissVolumeDialog();
+ mWakeUpCoordinator.setFullyAwake(false);
+ mKeyguardBypassController.onStartedGoingToSleep();
+
+ // The unlocked screen off and fold to aod animations might use our LightRevealScrim -
+ // we need to be expanded for it to be visible.
+ if (mDozeParameters.shouldShowLightRevealScrim()) {
+ makeExpandedVisible(true);
+ }
+
+ DejankUtils.stopDetectingBlockingIpcs(tag);
+ }
+
+ @Override
+ public void onStartedWakingUp() {
+ String tag = "CentralSurfaces#onStartedWakingUp";
+ DejankUtils.startDetectingBlockingIpcs(tag);
+ mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
+ mDeviceInteractive = true;
+ mWakeUpCoordinator.setWakingUp(true);
+ if (!mKeyguardBypassController.getBypassEnabled()) {
+ mHeadsUpManager.releaseAllImmediately();
+ }
+ updateVisibleToUser();
+ updateIsKeyguard();
+ mDozeServiceHost.stopDozing();
+ // This is intentionally below the stopDozing call above, since it avoids that we're
+ // unnecessarily animating the wakeUp transition. Animations should only be enabled
+ // once we fully woke up.
+ updateRevealEffect(true /* wakingUp */);
+ updateNotificationPanelTouchState();
+
+ // If we are waking up during the screen off animation, we should undo making the
+ // expanded visible (we did that so the LightRevealScrim would be visible).
+ if (mScreenOffAnimationController.shouldHideLightRevealScrimOnWakeUp()) {
+ makeExpandedInvisible();
+ }
+
+ });
+ DejankUtils.stopDetectingBlockingIpcs(tag);
+ }
+
+ @Override
+ public void onFinishedWakingUp() {
+ mWakeUpCoordinator.setFullyAwake(true);
+ mWakeUpCoordinator.setWakingUp(false);
+ if (mLaunchCameraWhenFinishedWaking) {
+ mNotificationPanelViewController.launchCamera(
+ false /* animate */, mLastCameraLaunchSource);
+ mLaunchCameraWhenFinishedWaking = false;
+ }
+ if (mLaunchEmergencyActionWhenFinishedWaking) {
+ mLaunchEmergencyActionWhenFinishedWaking = false;
+ Intent emergencyIntent = getEmergencyActionIntent();
+ if (emergencyIntent != null) {
+ mContext.startActivityAsUser(emergencyIntent,
+ getActivityUserHandle(emergencyIntent));
+ }
+ }
+ updateScrimController();
+ }
+ };
+
+ /**
+ * We need to disable touch events because these might
+ * collapse the panel after we expanded it, and thus we would end up with a blank
+ * Keyguard.
+ */
+ @Override
+ public void updateNotificationPanelTouchState() {
+ boolean goingToSleepWithoutAnimation = isGoingToSleep()
+ && !mDozeParameters.shouldControlScreenOff();
+ boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
+ || goingToSleepWithoutAnimation;
+ mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
+ mNotificationIconAreaController.setAnimationsEnabled(!disabled);
+ }
+
+ final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
+ @Override
+ public void onScreenTurningOn(Runnable onDrawn) {
+ mFalsingCollector.onScreenTurningOn();
+ mNotificationPanelViewController.onScreenTurningOn();
+ }
+
+ @Override
+ public void onScreenTurnedOn() {
+ mScrimController.onScreenTurnedOn();
+ }
+
+ @Override
+ public void onScreenTurnedOff() {
+ Trace.beginSection("CentralSurfaces#onScreenTurnedOff");
+ mFalsingCollector.onScreenOff();
+ mScrimController.onScreenTurnedOff();
+ if (mCloseQsBeforeScreenOff) {
+ mNotificationPanelViewController.closeQs();
+ mCloseQsBeforeScreenOff = false;
+ }
+ updateIsKeyguard();
+ Trace.endSection();
+ }
+ };
+
+ @Override
+ public int getWakefulnessState() {
+ return mWakefulnessLifecycle.getWakefulness();
+ }
+
+ /**
+ * @return true if the screen is currently fully off, i.e. has finished turning off and has
+ * since not started turning on.
+ */
+ @Override
+ public boolean isScreenFullyOff() {
+ return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF;
+ }
+
+ @Override
+ public void showScreenPinningRequest(int taskId, boolean allowCancel) {
+ mScreenPinningRequest.showPrompt(taskId, allowCancel);
+ }
+
+ @Nullable
+ @Override
+ public Intent getEmergencyActionIntent() {
+ Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
+ PackageManager pm = mContext.getPackageManager();
+ List<ResolveInfo> emergencyActivities = pm.queryIntentActivities(emergencyIntent,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ ResolveInfo resolveInfo = getTopEmergencySosInfo(emergencyActivities);
+ if (resolveInfo == null) {
+ Log.wtf(TAG, "Couldn't find an app to process the emergency intent.");
+ return null;
+ }
+ emergencyIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name));
+ emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return emergencyIntent;
+ }
+
+ /**
+ * Select and return the "best" ResolveInfo for Emergency SOS Activity.
+ */
+ private @Nullable ResolveInfo getTopEmergencySosInfo(List<ResolveInfo> emergencyActivities) {
+ // No matched activity.
+ if (emergencyActivities == null || emergencyActivities.isEmpty()) {
+ return null;
+ }
+
+ // Of multiple matched Activities, give preference to the pre-set package name.
+ String preferredAppPackageName =
+ mContext.getString(R.string.config_preferredEmergencySosPackage);
+
+ // If there is no preferred app, then return first match.
+ if (TextUtils.isEmpty(preferredAppPackageName)) {
+ return emergencyActivities.get(0);
+ }
+
+ for (ResolveInfo emergencyInfo: emergencyActivities) {
+ // If activity is from the preferred app, use it.
+ if (TextUtils.equals(emergencyInfo.activityInfo.packageName, preferredAppPackageName)) {
+ return emergencyInfo;
+ }
+ }
+ // No matching activity: return first match
+ return emergencyActivities.get(0);
+ }
+
+ @Override
+ public boolean isCameraAllowedByAdmin() {
+ if (mDevicePolicyManager.getCameraDisabled(null,
+ mLockscreenUserManager.getCurrentUserId())) {
+ return false;
+ } else if (mStatusBarKeyguardViewManager == null
+ || (isKeyguardShowing() && isKeyguardSecure())) {
+ // Check if the admin has disabled the camera specifically for the keyguard
+ return (mDevicePolicyManager.getKeyguardDisabledFeatures(null,
+ mLockscreenUserManager.getCurrentUserId())
+ & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isGoingToSleep() {
+ return mWakefulnessLifecycle.getWakefulness()
+ == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
+ }
+
+ boolean isWakingOrAwake() {
+ return mWakefulnessLifecycle.getWakefulness() == WakefulnessLifecycle.WAKEFULNESS_WAKING
+ || mWakefulnessLifecycle.getWakefulness() == WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+ }
+
+ @Override
+ public void notifyBiometricAuthModeChanged() {
+ mDozeServiceHost.updateDozing();
+ updateScrimController();
+ }
+
+ /**
+ * Set the amount of progress we are currently in if we're transitioning to the full shade.
+ * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+ * shade.
+ */
+ @Override
+ public void setTransitionToFullShadeProgress(float transitionToFullShadeProgress) {
+ mTransitionToFullShadeProgress = transitionToFullShadeProgress;
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public void setBouncerHiddenFraction(float expansion) {
+ mScrimController.setBouncerHiddenFraction(expansion);
+ }
+
+ @Override
+ @VisibleForTesting
+ public void updateScrimController() {
+ Trace.beginSection("CentralSurfaces#updateScrimController");
+
+ boolean unlocking = mKeyguardStateController.isShowing() && (
+ mBiometricUnlockController.isWakeAndUnlock()
+ || mKeyguardStateController.isKeyguardFadingAway()
+ || mKeyguardStateController.isKeyguardGoingAway()
+ || mKeyguardViewMediator.requestedShowSurfaceBehindKeyguard()
+ || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind());
+
+ mScrimController.setExpansionAffectsAlpha(!unlocking);
+
+ boolean launchingAffordanceWithPreview =
+ mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
+ mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
+
+ if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
+ if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+ || mTransitionToFullShadeProgress > 0f) {
+ mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
+ } else {
+ mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+ }
+ } else if (mBouncerShowing && !unlocking) {
+ // 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.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
+ mScrimController.transitionTo(state);
+ } else if (launchingAffordanceWithPreview) {
+ // We want to avoid animating when launching with a preview.
+ mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
+ } else if (mBrightnessMirrorVisible) {
+ mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
+ } else if (mState == StatusBarState.SHADE_LOCKED) {
+ mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ } else if (mDozeServiceHost.isPulsing()) {
+ mScrimController.transitionTo(ScrimState.PULSING,
+ mDozeScrimController.getScrimCallback());
+ } else if (mDozeServiceHost.hasPendingScreenOffCallback()) {
+ mScrimController.transitionTo(ScrimState.OFF, new ScrimController.Callback() {
+ @Override
+ public void onFinished() {
+ mDozeServiceHost.executePendingScreenOffCallback();
+ }
+ });
+ } else if (mDozing && !unlocking) {
+ mScrimController.transitionTo(ScrimState.AOD);
+ } else if (mKeyguardStateController.isShowing() && !isOccluded() && !unlocking) {
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ } else if (mKeyguardStateController.isShowing() && mKeyguardUpdateMonitor.isDreaming()) {
+ mScrimController.transitionTo(ScrimState.DREAMING);
+ } else {
+ mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
+ }
+ updateLightRevealScrimVisibility();
+
+ Trace.endSection();
+ }
+
+ @Override
+ public boolean isKeyguardShowing() {
+ if (mStatusBarKeyguardViewManager == null) {
+ Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true");
+ return true;
+ }
+ return mStatusBarKeyguardViewManager.isShowing();
+ }
+
+ @Override
+ public boolean shouldIgnoreTouch() {
+ return (mStatusBarStateController.isDozing()
+ && mDozeServiceHost.getIgnoreTouchWhilePulsing())
+ || mScreenOffAnimationController.shouldIgnoreKeyguardTouches();
+ }
+
+ // Begin Extra BaseStatusBar methods.
+
+ protected final CommandQueue mCommandQueue;
+ protected IStatusBarService mBarService;
+
+ // all notifications
+ protected NotificationStackScrollLayout mStackScroller;
+
+ // handling reordering
+ private final VisualStabilityManager mVisualStabilityManager;
+
+ protected AccessibilityManager mAccessibilityManager;
+
+ protected boolean mDeviceInteractive;
+
+ protected boolean mVisible;
+
+ // mScreenOnFromKeyguard && mVisible.
+ private boolean mVisibleToUser;
+
+ protected DevicePolicyManager mDevicePolicyManager;
+ private final PowerManager mPowerManager;
+ protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
+ protected KeyguardManager mKeyguardManager;
+ private final DeviceProvisionedController mDeviceProvisionedController;
+
+ private final NavigationBarController mNavigationBarController;
+ private final AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
+
+ // UI-specific methods
+
+ protected WindowManager mWindowManager;
+ protected IWindowManager mWindowManagerService;
+ private IDreamManager mDreamManager;
+
+ protected Display mDisplay;
+ private int mDisplayId;
+
+ protected NotificationShelfController mNotificationShelfController;
+
+ private final Lazy<AssistManager> mAssistManagerLazy;
+
+ @Override
+ public boolean isDeviceInteractive() {
+ return mDeviceInteractive;
+ }
+
+ private final BroadcastReceiver mBannerActionBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
+ NotificationManager noMan = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ noMan.cancel(com.android.internal.messages.nano.SystemMessageProto.SystemMessage.
+ NOTE_HIDDEN_NOTIFICATIONS);
+
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
+ if (BANNER_ACTION_SETUP.equals(action)) {
+ mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+ true /* force */);
+ mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ );
+ }
+ }
+ }
+ };
+
+ @Override
+ public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
+ mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
+ }
+
+
+ @Override
+ public void awakenDreams() {
+ mUiBgExecutor.execute(() -> {
+ try {
+ mDreamManager.awaken();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ protected void toggleKeyboardShortcuts(int deviceId) {
+ KeyboardShortcuts.toggle(mContext, deviceId);
+ }
+
+ protected void dismissKeyboardShortcuts() {
+ KeyboardShortcuts.dismiss();
+ }
+
+ /**
+ * Dismiss the keyguard then execute an action.
+ *
+ * @param action The action to execute after dismissing the keyguard.
+ * @param collapsePanel Whether we should collapse the panel after dismissing the keyguard.
+ * @param willAnimateOnKeyguard Whether {@param action} will run an animation on the keyguard if
+ * we are locked.
+ */
+ private void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone,
+ boolean collapsePanel, boolean willAnimateOnKeyguard) {
+ if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
+
+ OnDismissAction onDismissAction = new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ new Thread(() -> {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ action.run();
+ }).start();
+
+ return collapsePanel ? mShadeController.collapsePanel() : willAnimateOnKeyguard;
+ }
+
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return willAnimateOnKeyguard;
+ }
+ };
+ dismissKeyguardThenExecute(onDismissAction, afterKeyguardGone);
+ }
+
+ @Override
+ public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
+ startPendingIntentDismissingKeyguard(intent, null);
+ }
+
+ @Override
+ public void startPendingIntentDismissingKeyguard(
+ final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) {
+ startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback,
+ (ActivityLaunchAnimator.Controller) null);
+ }
+
+ @Override
+ public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+ Runnable intentSentUiThreadCallback, View associatedView) {
+ ActivityLaunchAnimator.Controller animationController = null;
+ if (associatedView instanceof ExpandableNotificationRow) {
+ animationController = mNotificationAnimationProvider.getAnimatorController(
+ ((ExpandableNotificationRow) associatedView));
+ }
+
+ startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback,
+ animationController);
+ }
+
+ @Override
+ public void startPendingIntentDismissingKeyguard(
+ final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback,
+ @Nullable ActivityLaunchAnimator.Controller animationController) {
+ final boolean willLaunchResolverActivity = intent.isActivity()
+ && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
+ mLockscreenUserManager.getCurrentUserId());
+
+ boolean animate = !willLaunchResolverActivity
+ && animationController != null
+ && shouldAnimateLaunch(intent.isActivity());
+
+ // If we animate, don't collapse the shade and defer the keyguard dismiss (in case we run
+ // the animation on the keyguard). The animation will take care of (instantly) collapsing
+ // the shade and hiding the keyguard once it is done.
+ boolean collapse = !animate;
+ executeActionDismissingKeyguard(() -> {
+ try {
+ // We wrap animationCallback with a StatusBarLaunchAnimatorController so that the
+ // shade is collapsed after the animation (or when it is cancelled, aborted, etc).
+ ActivityLaunchAnimator.Controller controller =
+ animationController != null ? new StatusBarLaunchAnimatorController(
+ animationController, this, intent.isActivity()) : null;
+
+ mActivityLaunchAnimator.startPendingIntentWithAnimation(
+ controller, animate, intent.getCreatorPackage(),
+ (animationAdapter) -> {
+ ActivityOptions options = new ActivityOptions(
+ CentralSurfaces.getActivityOptions(
+ mDisplayId, animationAdapter));
+ // TODO b/221255671: restrict this to only be set for notifications
+ options.setEligibleForLegacyPermissionPrompt(true);
+ return intent.sendAndReturnResult(null, 0, null, null, null,
+ null, options.toBundle());
+ });
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here.
+ // Just log the exception message.
+ Log.w(TAG, "Sending intent failed: " + e);
+ if (!collapse) {
+ // executeActionDismissingKeyguard did not collapse for us already.
+ collapsePanelOnMainThread();
+ }
+ // TODO: Dismiss Keyguard.
+ }
+ if (intent.isActivity()) {
+ mAssistManagerLazy.get().hideAssist();
+ }
+ if (intentSentUiThreadCallback != null) {
+ postOnUiThread(intentSentUiThreadCallback);
+ }
+ }, willLaunchResolverActivity, collapse, animate);
+ }
+
+ private void postOnUiThread(Runnable runnable) {
+ mMainExecutor.execute(runnable);
+ }
+
+ @Override
+ public void visibilityChanged(boolean visible) {
+ if (mVisible != visible) {
+ mVisible = visible;
+ if (!visible) {
+ mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
+ }
+ }
+ updateVisibleToUser();
+ }
+
+ protected void updateVisibleToUser() {
+ boolean oldVisibleToUser = mVisibleToUser;
+ mVisibleToUser = mVisible && mDeviceInteractive;
+
+ if (oldVisibleToUser != mVisibleToUser) {
+ handleVisibleToUserChanged(mVisibleToUser);
+ }
+ }
+
+ /**
+ * Clear Buzz/Beep/Blink.
+ */
+ @Override
+ public void clearNotificationEffects() {
+ try {
+ mBarService.clearNotificationEffects();
+ } catch (RemoteException e) {
+ // Won't fail unless the world has ended.
+ }
+ }
+
+ /**
+ * @return Whether the security bouncer from Keyguard is showing.
+ */
+ @Override
+ public boolean isBouncerShowing() {
+ return mBouncerShowing;
+ }
+
+ /**
+ * @return Whether the security bouncer from Keyguard is showing.
+ */
+ @Override
+ public boolean isBouncerShowingScrimmed() {
+ return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming();
+ }
+
+ @Override
+ public boolean isBouncerShowingOverDream() {
+ return isBouncerShowing() && mDreamOverlayStateController.isOverlayActive();
+ }
+
+ /**
+ * When {@link KeyguardBouncer} starts to be dismissed, playing its animation.
+ */
+ @Override
+ public void onBouncerPreHideAnimation() {
+ mNotificationPanelViewController.onBouncerPreHideAnimation();
+
+ }
+
+ @Override
+ public boolean isKeyguardSecure() {
+ if (mStatusBarKeyguardViewManager == null) {
+ // startKeyguard() hasn't been called yet, so we don't know.
+ // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
+ // value onVisibilityChanged().
+ Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
+ new Throwable());
+ return false;
+ }
+ return mStatusBarKeyguardViewManager.isSecure();
+ }
+ @Override
+ public NotificationPanelViewController getPanelController() {
+ return mNotificationPanelViewController;
+ }
+ // End Extra BaseStatusBarMethods.
+
+ @Override
+ public NotificationGutsManager getGutsManager() {
+ return mGutsManager;
+ }
+
+ boolean isTransientShown() {
+ return mTransientShown;
+ }
+
+ private void updateLightRevealScrimVisibility() {
+ if (mLightRevealScrim == null) {
+ // status bar may not be inflated yet
+ return;
+ }
+
+ mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
+ }
+
+ @Override
+ public void extendDozePulse(){
+ mDozeScrimController.extendPulse();
+ }
+
+ private final KeyguardUpdateMonitorCallback mUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onDreamingStateChanged(boolean dreaming) {
+ updateScrimController();
+ if (dreaming) {
+ maybeEscalateHeadsUp();
+ }
+ }
+
+ // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+ // KeyguardCoordinator
+ @Override
+ public void onStrongAuthStateChanged(int userId) {
+ super.onStrongAuthStateChanged(userId);
+ mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged");
+ }
+ };
+
+
+ private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener =
+ new FalsingManager.FalsingBeliefListener() {
+ @Override
+ public void onFalse() {
+ // Hides quick settings, bouncer, and quick-quick settings.
+ mStatusBarKeyguardViewManager.reset(true);
+ }
+ };
+
+ // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over,
+ // this animation is tied to the scrim for historic reasons.
+ // TODO: notify when keyguard has faded away instead of the scrim.
+ private final ScrimController.Callback mUnlockScrimCallback = new ScrimController
+ .Callback() {
+ @Override
+ public void onFinished() {
+ if (mStatusBarKeyguardViewManager == null) {
+ Log.w(TAG, "Tried to notify keyguard visibility when "
+ + "mStatusBarKeyguardViewManager was null");
+ return;
+ }
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ }
+ }
+
+ @Override
+ public void onCancelled() {
+ onFinished();
+ }
+ };
+
+ private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
+ @Override
+ public void onUserSetupChanged() {
+ final boolean userSetup = mDeviceProvisionedController.isCurrentUserSetup();
+ Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for "
+ + "current user");
+ if (MULTIUSER_DEBUG) {
+ Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s",
+ userSetup, mUserSetup));
+ }
+
+ if (userSetup != mUserSetup) {
+ mUserSetup = userSetup;
+ if (!mUserSetup) {
+ animateCollapseQuickSettings();
+ }
+ if (mNotificationPanelViewController != null) {
+ mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
+ }
+ updateQsExpansionEnabled();
+ }
+ }
+ };
+
+ private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!mWallpaperSupported) {
+ // Receiver should not have been registered at all...
+ Log.wtf(TAG, "WallpaperManager not supported");
+ return;
+ }
+ WallpaperInfo info = mWallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
+ mWallpaperController.onWallpaperInfoUpdated(info);
+
+ final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
+ // If WallpaperInfo is null, it must be ImageWallpaper.
+ final boolean supportsAmbientMode = deviceSupportsAodWallpaper
+ && (info != null && info.supportsAmbientMode());
+
+ mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+ mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+ mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+ }
+ };
+
+ private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ updateResources();
+ updateDisplaySize(); // populates mDisplayMetrics
+
+ if (DEBUG) {
+ Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
+ }
+
+ if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
+ mViewHierarchyManager.updateRowStates();
+ }
+ mScreenPinningRequest.onConfigurationChanged();
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ // TODO: Remove this.
+ if (mBrightnessMirrorController != null) {
+ mBrightnessMirrorController.onDensityOrFontScaleChanged();
+ }
+ // TODO: Bring these out of CentralSurfaces.
+ mUserInfoControllerImpl.onDensityOrFontScaleChanged();
+ mUserSwitcherController.onDensityOrFontScaleChanged();
+ mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
+ mHeadsUpManager.onDensityOrFontScaleChanged();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ if (mBrightnessMirrorController != null) {
+ mBrightnessMirrorController.onOverlayChanged();
+ }
+ // We need the new R.id.keyguard_indication_area before recreating
+ // mKeyguardIndicationController
+ mNotificationPanelViewController.onThemeChanged();
+
+ if (mStatusBarKeyguardViewManager != null) {
+ mStatusBarKeyguardViewManager.onThemeChanged();
+ }
+ if (mAmbientIndicationContainer instanceof AutoReinflateContainer) {
+ ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout();
+ }
+ mNotificationIconAreaController.onThemeChanged();
+ }
+
+ @Override
+ public void onUiModeChanged() {
+ if (mBrightnessMirrorController != null) {
+ mBrightnessMirrorController.onUiModeChanged();
+ }
+ }
+ };
+
+ private StatusBarStateController.StateListener mStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStatePreChange(int oldState, int newState) {
+ // If we're visible and switched to SHADE_LOCKED (the user dragged
+ // down on the lockscreen), clear notification LED, vibration,
+ // ringing.
+ // Other transitions are covered in handleVisibleToUserChanged().
+ if (mVisible && (newState == StatusBarState.SHADE_LOCKED
+ || mStatusBarStateController.goingToFullShade())) {
+ clearNotificationEffects();
+ }
+ if (newState == StatusBarState.KEYGUARD) {
+ mRemoteInputManager.onPanelCollapsed();
+ maybeEscalateHeadsUp();
+ }
+ }
+
+ @Override
+ public void onStateChanged(int newState) {
+ mState = newState;
+ updateReportRejectedTouchVisibility();
+ mDozeServiceHost.updateDozing();
+ updateTheme();
+ mNavigationBarController.touchAutoDim(mDisplayId);
+ Trace.beginSection("CentralSurfaces#updateKeyguardState");
+ if (mState == StatusBarState.KEYGUARD) {
+ mNotificationPanelViewController.cancelPendingPanelCollapse();
+ }
+ updateDozingState();
+ checkBarModes();
+ updateScrimController();
+ mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
+ Trace.endSection();
+ }
+
+ @Override
+ public void onDozeAmountChanged(float linear, float eased) {
+ if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
+ && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
+ mLightRevealScrim.setRevealAmount(1f - linear);
+ }
+ }
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ Trace.beginSection("CentralSurfaces#updateDozing");
+ mDozing = isDozing;
+
+ // Collapse the notification panel if open
+ boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
+ && mDozeParameters.shouldControlScreenOff();
+ mNotificationPanelViewController.resetViews(dozingAnimated);
+
+ updateQsExpansionEnabled();
+ mKeyguardViewMediator.setDozing(mDozing);
+
+ mNotificationsController.requestNotificationUpdate("onDozingChanged");
+ updateDozingState();
+ mDozeServiceHost.updateDozing();
+ updateScrimController();
+ updateReportRejectedTouchVisibility();
+ Trace.endSection();
+ }
+
+ @Override
+ public void onFullscreenStateChanged(boolean isFullscreen) {
+ mIsFullscreen = isFullscreen;
+ maybeUpdateBarMode();
+ }
+ };
+
+ private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
+ new BatteryController.BatteryStateChangeCallback() {
+ @Override
+ public void onPowerSaveChanged(boolean isPowerSave) {
+ mMainExecutor.execute(mCheckBarModes);
+ if (mDozeServiceHost != null) {
+ mDozeServiceHost.firePowerSaveChanged(isPowerSave);
+ }
+ }
+ };
+
+ private final ActivityLaunchAnimator.Callback mActivityLaunchAnimatorCallback =
+ new ActivityLaunchAnimator.Callback() {
+ @Override
+ public boolean isOnKeyguard() {
+ return mKeyguardStateController.isShowing();
+ }
+
+ @Override
+ public void hideKeyguardWithAnimation(IRemoteAnimationRunner runner) {
+ // We post to the main thread for 2 reasons:
+ // 1. KeyguardViewMediator is not thread-safe.
+ // 2. To ensure that ViewMediatorCallback#keyguardDonePending is called before
+ // ViewMediatorCallback#readyForKeyguardDone. The wrong order could occur
+ // when doing
+ // dismissKeyguardThenExecute { hideKeyguardWithAnimation(runner) }.
+ mMainExecutor.execute(() -> mKeyguardViewMediator.hideWithAnimation(runner));
+ }
+
+ @Override
+ public int getBackgroundColor(TaskInfo task) {
+ if (!mStartingSurfaceOptional.isPresent()) {
+ Log.w(TAG, "No starting surface, defaulting to SystemBGColor");
+ return SplashscreenContentDrawer.getSystemBGColor();
+ }
+
+ return mStartingSurfaceOptional.get().getBackgroundColor(task);
+ }
+ };
+
+ private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener =
+ new ActivityLaunchAnimator.Listener() {
+ @Override
+ public void onLaunchAnimationStart() {
+ mKeyguardViewMediator.setBlursDisabledForAppLaunch(true);
+ }
+
+ @Override
+ public void onLaunchAnimationEnd() {
+ mKeyguardViewMediator.setBlursDisabledForAppLaunch(false);
+ }
+ };
+
+ private final DemoMode mDemoModeCallback = new DemoMode() {
+ @Override
+ public void onDemoModeFinished() {
+ checkBarModes();
+ }
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) { }
+ };
+
+ /**
+ * Determines what UserHandle to use when launching an activity.
+ *
+ * We want to ensure that activities that are launched within the systemui process should be
+ * launched as user of the current process.
+ * @param intent
+ * @return UserHandle
+ */
+ private UserHandle getActivityUserHandle(Intent intent) {
+ String[] packages = mContext.getResources().getStringArray(R.array.system_ui_packages);
+ for (String pkg : packages) {
+ if (intent.getComponent() == null) break;
+ if (pkg.equals(intent.getComponent().getPackageName())) {
+ return new UserHandle(UserHandle.myUserId());
+ }
+ }
+ return UserHandle.CURRENT;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 034b751d1e61..2dc3261eb886 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -27,11 +27,13 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.util.AttributeSet;
+import android.util.MathUtils;
import android.util.Property;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.animation.Interpolator;
+import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap;
import com.android.internal.statusbar.StatusBarIcon;
@@ -136,6 +138,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}.setDuration(CONTENT_FADE_DURATION);
private static final int MAX_ICONS_ON_AOD = 3;
+
+ /* Maximum number of icons in short shelf on lockscreen when also showing overflow dot. */
public static final int MAX_ICONS_ON_LOCKSCREEN = 3;
public static final int MAX_STATIC_ICONS = 4;
private static final int MAX_DOTS = 1;
@@ -145,7 +149,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
private int mDotPadding;
private int mStaticDotRadius;
private int mStaticDotDiameter;
- private int mOverflowWidth;
private int mActualLayoutWidth = NO_VALUE;
private float mActualPaddingEnd = NO_VALUE;
private float mActualPaddingStart = NO_VALUE;
@@ -219,10 +222,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
paint.setColor(Color.RED);
canvas.drawLine(mVisualOverflowStart, 0, mVisualOverflowStart, height, paint);
-
- paint.setColor(Color.YELLOW);
- float overflow = getMaxOverflowStart();
- canvas.drawLine(overflow, 0, overflow, height, paint);
}
}
@@ -255,14 +254,14 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
}
- private void setIconSize(int size) {
+ @VisibleForTesting
+ public void setIconSize(int size) {
mIconSize = size;
- mOverflowWidth = mIconSize + (MAX_DOTS - 1) * (mStaticDotDiameter + mDotPadding);
}
private void updateState() {
resetViewStates();
- calculateIconTranslations();
+ calculateIconXTranslations();
applyIconStates();
}
@@ -390,12 +389,11 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
* @return Width of shelf for the given number of icons
*/
public float calculateWidthFor(float numIcons) {
- if (getChildCount() == 0) {
+ if (numIcons == 0) {
return 0f;
}
- final float contentWidth = numIcons <= MAX_ICONS_ON_LOCKSCREEN + 1
- ? numIcons * mIconSize
- : MAX_ICONS_ON_LOCKSCREEN * mIconSize + (float) mOverflowWidth;
+ final float contentWidth =
+ mIconSize * MathUtils.min(numIcons, MAX_ICONS_ON_LOCKSCREEN + 1);
return getActualPaddingStart()
+ contentWidth
+ getActualPaddingEnd();
@@ -406,14 +404,13 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
* are inserted into the notification container.
* If this is not a whole number, the fraction means by how much the icon is appearing.
*/
- public void calculateIconTranslations() {
+ public void calculateIconXTranslations() {
float translationX = getActualPaddingStart();
int firstOverflowIndex = -1;
int childCount = getChildCount();
int maxVisibleIcons = mOnLockScreen ? MAX_ICONS_ON_AOD :
mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
float layoutEnd = getLayoutEnd();
- float overflowStart = getMaxOverflowStart();
mVisualOverflowStart = 0;
mFirstVisibleIconState = null;
for (int i = 0; i < childCount; i++) {
@@ -438,12 +435,12 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
? StatusBarIconView.STATE_HIDDEN
: StatusBarIconView.STATE_ICON;
- boolean isOverflowing =
- (translationX > (isLastChild ? layoutEnd - mIconSize
- : overflowStart - mIconSize));
+ final float overflowDotX = layoutEnd - mIconSize;
+ boolean isOverflowing = translationX > overflowDotX;
+
if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
firstOverflowIndex = isLastChild && !forceOverflow ? i - 1 : i;
- mVisualOverflowStart = layoutEnd - mOverflowWidth;
+ mVisualOverflowStart = layoutEnd - mIconSize;
if (forceOverflow || mIsStaticLayout) {
mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
}
@@ -477,7 +474,6 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
mLastVisibleIconState = mIconStates.get(lastChild);
mFirstVisibleIconState = mIconStates.get(getChildAt(0));
}
-
if (isLayoutRtl()) {
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
@@ -568,7 +564,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
private float getMaxOverflowStart() {
- return getLayoutEnd() - mOverflowWidth;
+ return getLayoutEnd() - mIconSize;
}
public void setChangingViewPositions(boolean changingViewPositions) {
@@ -635,7 +631,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
return 0;
}
- int collapsedPadding = mOverflowWidth;
+ int collapsedPadding = mIconSize;
if (collapsedPadding + getFinalTranslationX() > getWidth()) {
collapsedPadding = getWidth() - getFinalTranslationX();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index e4f42b10dab0..98a711d122fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -179,6 +179,7 @@ import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.phone.panelstate.PanelState;
+import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -310,6 +311,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final NotificationRemoteInputManager mRemoteInputManager;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final ShadeTransitionController mShadeTransitionController;
private final TapAgainViewController mTapAgainViewController;
private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
private final RecordingController mRecordingController;
@@ -389,6 +391,12 @@ public class NotificationPanelViewController extends PanelViewController {
private int mLargeScreenShadeHeaderHeight;
private int mSplitShadeNotificationsScrimMarginBottom;
+ /**
+ * Vertical overlap allowed between the bottom of the notification shelf and
+ * the top of the lock icon or the under-display fingerprint sensor background.
+ */
+ private int mShelfAndLockIconOverlap;
+
private final KeyguardClockPositionAlgorithm
mClockPositionAlgorithm =
new KeyguardClockPositionAlgorithm();
@@ -745,7 +753,8 @@ public class NotificationPanelViewController extends PanelViewController {
NotificationListContainer notificationListContainer,
PanelEventsEmitter panelEventsEmitter,
NotificationStackSizeCalculator notificationStackSizeCalculator,
- UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ ShadeTransitionController shadeTransitionController) {
super(view,
falsingManager,
dozeLog,
@@ -826,7 +835,9 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardBypassController = bypassController;
mUpdateMonitor = keyguardUpdateMonitor;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
+ mShadeTransitionController = shadeTransitionController;
lockscreenShadeTransitionController.setNotificationPanelController(this);
+ shadeTransitionController.setNotificationPanelViewController(this);
DynamicPrivacyControlListener
dynamicPrivacyControlListener =
new DynamicPrivacyControlListener();
@@ -885,7 +896,10 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void onUnlockAnimationStarted(
- boolean playingCannedAnimation, boolean isWakeAndUnlock) {
+ boolean playingCannedAnimation,
+ boolean isWakeAndUnlock,
+ long unlockAnimationStartDelay,
+ long unlockAnimationDuration) {
// Disable blurs while we're unlocking so that panel expansion does not
// cause blurring. This will eventually be re-enabled by the panel view on
// ACTION_UP, since the user's finger might still be down after a swipe to
@@ -902,7 +916,22 @@ public class NotificationPanelViewController extends PanelViewController {
onTrackingStopped(false);
instantCollapse();
} else {
- fling(0f, false, 1f, false);
+ mView.animate()
+ .alpha(0f)
+ .setStartDelay(0)
+ // Translate up by 4%.
+ .translationY(mView.getHeight() * -0.04f)
+ // This start delay is to give us time to animate out before
+ // the launcher icons animation starts, so use that as our
+ // duration.
+ .setDuration(unlockAnimationStartDelay)
+ .setInterpolator(EMPHASIZED_DECELERATE)
+ .withEndAction(() -> {
+ instantCollapse();
+ mView.setAlpha(1f);
+ mView.setTranslationY(0f);
+ })
+ .start();
}
}
}
@@ -1081,6 +1110,9 @@ public class NotificationPanelViewController extends PanelViewController {
mResources.getDimensionPixelSize(
R.dimen.split_shade_notifications_scrim_margin_bottom);
+ mShelfAndLockIconOverlap =
+ mResources.getDimensionPixelSize(R.dimen.shelf_and_lock_icon_overlap);
+
final boolean newShouldUseSplitNotificationShade =
LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
final boolean splitNotificationShadeChanged =
@@ -1466,27 +1498,18 @@ public class NotificationPanelViewController extends PanelViewController {
}
/**
- * @return the maximum keyguard notifications that can fit on the screen
+ * @return Space available to show notifications on lockscreen.
*/
@VisibleForTesting
- int computeMaxKeyguardNotifications() {
- if (mAmbientState.getFractionToShade() > 0 || mAmbientState.getDozeAmount() > 0) {
- return mMaxAllowedKeyguardNotifications;
- }
+ float getSpaceForLockscreenNotifications() {
float topPadding = mNotificationStackScrollLayoutController.getTopPadding();
- float shelfIntrinsicHeight =
- mNotificationShelfController.getVisibility() == View.GONE
- ? 0
- : mNotificationShelfController.getIntrinsicHeight();
- // Padding to add to the bottom of the stack to keep a minimum distance from the top of
- // the lock icon.
- float lockIconPadding = 0;
+ // Space between bottom of notifications and top of lock icon or udfps background.
+ float lockIconPadding = mLockIconViewController.getTop();
if (mLockIconViewController.getTop() != 0) {
- final float lockIconTopWithPadding = mLockIconViewController.getTop()
- - mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding);
lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
- - lockIconTopWithPadding;
+ - mLockIconViewController.getTop()
+ - mShelfAndLockIconOverlap;
}
float bottomPadding = Math.max(lockIconPadding,
@@ -1497,9 +1520,26 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.getHeight()
- topPadding
- bottomPadding;
+ return availableSpace;
+ }
+
+ /**
+ * @return Maximum number of notifications that can fit on keyguard.
+ */
+ @VisibleForTesting
+ int computeMaxKeyguardNotifications() {
+ if (mAmbientState.getFractionToShade() > 0 || mAmbientState.getDozeAmount() > 0) {
+ return mMaxAllowedKeyguardNotifications;
+ }
+
+ final float shelfIntrinsicHeight =
+ mNotificationShelfController.getVisibility() == View.GONE
+ ? 0
+ : mNotificationShelfController.getIntrinsicHeight();
return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications(
- mNotificationStackScrollLayoutController.getView(), availableSpace,
+ mNotificationStackScrollLayoutController.getView(),
+ getSpaceForLockscreenNotifications(),
shelfIntrinsicHeight);
}
@@ -3607,6 +3647,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
});
mLockscreenShadeTransitionController.setQS(mQs);
+ mShadeTransitionController.setQs(mQs);
mNotificationStackScrollLayoutController.setQsHeader((ViewGroup) mQs.getHeader());
mQs.setScrollListener(mScrollListener);
updateQsExpansion();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 2f11b16f9383..be5b33eb0da0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -252,7 +252,7 @@ public class NotificationShadeWindowViewController {
}
if (mStatusBarStateController.isDozing()) {
- mService.mDozeScrimController.extendPulse();
+ mService.extendDozePulse();
}
mLockIconViewController.onTouchEvent(
ev,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 9e707644782c..6637394e2b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -108,6 +108,8 @@ public abstract class PanelViewController {
*/
private boolean mIsSpringBackAnimation;
+ private boolean mInSplitShade;
+
private void logf(String fmt, Object... args) {
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
@@ -303,8 +305,9 @@ public abstract class PanelViewController {
mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
mHintDistance = mResources.getDimension(R.dimen.hint_move_distance);
mPanelFlingOvershootAmount = mResources.getDimension(R.dimen.panel_overshoot_amount);
- mUnlockFalsingThreshold = mResources.getDimensionPixelSize(
- R.dimen.unlock_falsing_threshold);
+ mUnlockFalsingThreshold =
+ mResources.getDimensionPixelSize(R.dimen.unlock_falsing_threshold);
+ mInSplitShade = mResources.getBoolean(R.bool.config_use_split_notification_shade);
}
protected float getTouchSlop(MotionEvent event) {
@@ -600,10 +603,12 @@ public abstract class PanelViewController {
}
mIsFlinging = true;
// we want to perform an overshoot animation when flinging open
- final boolean addOverscroll = expand
- && mStatusBarStateController.getState() != StatusBarState.KEYGUARD
- && mOverExpansion == 0.0f
- && vel >= 0;
+ final boolean addOverscroll =
+ expand
+ && !mInSplitShade // Split shade has its own overscroll logic
+ && mStatusBarStateController.getState() != StatusBarState.KEYGUARD
+ && mOverExpansion == 0.0f
+ && vel >= 0;
final boolean shouldSpringBack = addOverscroll || (mOverExpansion != 0.0f && expand);
float overshootAmount = 0.0f;
if (addOverscroll) {
@@ -777,7 +782,8 @@ public abstract class PanelViewController {
}
float maxPanelHeight = getMaxPanelHeight();
if (mHeightAnimator == null) {
- if (mTracking) {
+ // Split shade has its own overscroll logic
+ if (mTracking && !mInSplitShade) {
float overExpansionPixels = Math.max(0, h - maxPanelHeight);
setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index 5d38eea15723..c5e5297ae6ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -31,6 +31,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule;
import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks;
+import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
import com.android.systemui.statusbar.phone.LargeScreenShadeHeaderController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
@@ -51,7 +52,7 @@ import dagger.Subcomponent;
/**
* Dagger subcomponent for classes (semi-)related to the status bar. The component is created once
- * inside {@link com.android.systemui.statusbar.phone.CentralSurfaces} and never re-created.
+ * inside {@link CentralSurfacesImpl} and never re-created.
*
* TODO(b/197137564): This should likely be re-factored a bit. It includes classes that aren't
* directly related to status bar functionality, like multiple notification classes. And, the fact
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index c024c7245c45..942d186e7005 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -16,317 +16,22 @@
package com.android.systemui.statusbar.phone.dagger;
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.hardware.devicestate.DeviceStateManager;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-
-import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.InitController;
-import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.navigationbar.NavigationBarController;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.settings.brightness.BrightnessSliderController;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.charging.WiredChargingRippleController;
-import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
-import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.DozeScrimController;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
-import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-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.util.WallpaperController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.concurrency.MessageRouter;
-import com.android.systemui.volume.VolumeComponent;
-import com.android.systemui.wmshell.BubblesManager;
-import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.startingsurface.StartingSurface;
-
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
+import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
-import dagger.Lazy;
+import dagger.Binds;
import dagger.Module;
-import dagger.Provides;
/**
- * Dagger Module providing {@link CentralSurfaces}.
+ * Dagger Module providing {@link CentralSurfacesImpl}.
*/
@Module
public interface StatusBarPhoneModule {
/**
* Provides our instance of CentralSurfaces which is considered optional.
*/
- @Provides
+ @Binds
@SysUISingleton
- static CentralSurfaces provideCentralSurfaces(
- Context context,
- NotificationsController notificationsController,
- FragmentService fragmentService,
- LightBarController lightBarController,
- AutoHideController autoHideController,
- StatusBarWindowController statusBarWindowController,
- StatusBarWindowStateController statusBarWindowStateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarSignalPolicy statusBarSignalPolicy,
- PulseExpansionHandler pulseExpansionHandler,
- NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- KeyguardBypassController keyguardBypassController,
- KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManagerPhone,
- DynamicPrivacyController dynamicPrivacyController,
- FalsingManager falsingManager,
- FalsingCollector falsingCollector,
- BroadcastDispatcher broadcastDispatcher,
- NotifShadeEventSource notifShadeEventSource,
- NotificationEntryManager notificationEntryManager,
- NotificationGutsManager notificationGutsManager,
- NotificationLogger notificationLogger,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
- NotificationViewHierarchyManager notificationViewHierarchyManager,
- PanelExpansionStateManager panelExpansionStateManager,
- KeyguardViewMediator keyguardViewMediator,
- DisplayMetrics displayMetrics,
- MetricsLogger metricsLogger,
- @UiBackground Executor uiBgExecutor,
- NotificationMediaManager notificationMediaManager,
- NotificationLockscreenUserManager lockScreenUserManager,
- NotificationRemoteInputManager remoteInputManager,
- UserSwitcherController userSwitcherController,
- NetworkController networkController,
- BatteryController batteryController,
- SysuiColorExtractor colorExtractor,
- ScreenLifecycle screenLifecycle,
- WakefulnessLifecycle wakefulnessLifecycle,
- SysuiStatusBarStateController statusBarStateController,
- Optional<BubblesManager> bubblesManagerOptional,
- Optional<Bubbles> bubblesOptional,
- VisualStabilityManager visualStabilityManager,
- DeviceProvisionedController deviceProvisionedController,
- NavigationBarController navigationBarController,
- AccessibilityFloatingMenuController accessibilityFloatingMenuController,
- Lazy<AssistManager> assistManagerLazy,
- ConfigurationController configurationController,
- NotificationShadeWindowController notificationShadeWindowController,
- DozeParameters dozeParameters,
- ScrimController scrimController,
- Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
- LockscreenGestureLogger lockscreenGestureLogger,
- Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
- DozeServiceHost dozeServiceHost,
- PowerManager powerManager,
- ScreenPinningRequest screenPinningRequest,
- DozeScrimController dozeScrimController,
- VolumeComponent volumeComponent,
- CommandQueue commandQueue,
- CentralSurfacesComponent.Factory statusBarComponentFactory,
- PluginManager pluginManager,
- ShadeController shadeController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- ViewMediatorCallback viewMediatorCallback,
- InitController initController,
- @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
- PluginDependencyProvider pluginDependencyProvider,
- KeyguardDismissUtil keyguardDismissUtil,
- ExtensionController extensionController,
- UserInfoControllerImpl userInfoControllerImpl,
- PhoneStatusBarPolicy phoneStatusBarPolicy,
- KeyguardIndicationController keyguardIndicationController,
- DemoModeController demoModeController,
- Lazy<NotificationShadeDepthController> notificationShadeDepthController,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager,
- NotificationIconAreaController notificationIconAreaController,
- BrightnessSliderController.Factory brightnessSliderFactory,
- ScreenOffAnimationController screenOffAnimationController,
- WallpaperController wallpaperController,
- OngoingCallController ongoingCallController,
- StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
- LockscreenShadeTransitionController transitionController,
- FeatureFlags featureFlags,
- KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- @Main Handler mainHandler,
- @Main DelayableExecutor delayableExecutor,
- @Main MessageRouter messageRouter,
- WallpaperManager wallpaperManager,
- Optional<StartingSurface> startingSurfaceOptional,
- ActivityLaunchAnimator activityLaunchAnimator,
- NotifPipelineFlags notifPipelineFlags,
- InteractionJankMonitor jankMonitor,
- DeviceStateManager deviceStateManager,
- DreamOverlayStateController dreamOverlayStateController,
- WiredChargingRippleController wiredChargingRippleController) {
- return new CentralSurfaces(
- context,
- notificationsController,
- fragmentService,
- lightBarController,
- autoHideController,
- statusBarWindowController,
- statusBarWindowStateController,
- keyguardUpdateMonitor,
- statusBarSignalPolicy,
- pulseExpansionHandler,
- notificationWakeUpCoordinator,
- keyguardBypassController,
- keyguardStateController,
- headsUpManagerPhone,
- dynamicPrivacyController,
- falsingManager,
- falsingCollector,
- broadcastDispatcher,
- notifShadeEventSource,
- notificationEntryManager,
- notificationGutsManager,
- notificationLogger,
- notificationInterruptStateProvider,
- notificationViewHierarchyManager,
- panelExpansionStateManager,
- keyguardViewMediator,
- displayMetrics,
- metricsLogger,
- uiBgExecutor,
- notificationMediaManager,
- lockScreenUserManager,
- remoteInputManager,
- userSwitcherController,
- networkController,
- batteryController,
- colorExtractor,
- screenLifecycle,
- wakefulnessLifecycle,
- statusBarStateController,
- bubblesManagerOptional,
- bubblesOptional,
- visualStabilityManager,
- deviceProvisionedController,
- navigationBarController,
- accessibilityFloatingMenuController,
- assistManagerLazy,
- configurationController,
- notificationShadeWindowController,
- dozeParameters,
- scrimController,
- lockscreenWallpaperLazy,
- lockscreenGestureLogger,
- biometricUnlockControllerLazy,
- dozeServiceHost,
- powerManager,
- screenPinningRequest,
- dozeScrimController,
- volumeComponent,
- commandQueue,
- statusBarComponentFactory,
- pluginManager,
- shadeController,
- statusBarKeyguardViewManager,
- viewMediatorCallback,
- initController,
- timeTickHandler,
- pluginDependencyProvider,
- keyguardDismissUtil,
- extensionController,
- userInfoControllerImpl,
- phoneStatusBarPolicy,
- keyguardIndicationController,
- demoModeController,
- notificationShadeDepthController,
- statusBarTouchableRegionManager,
- notificationIconAreaController,
- brightnessSliderFactory,
- screenOffAnimationController,
- wallpaperController,
- ongoingCallController,
- statusBarHideIconsForBouncerManager,
- transitionController,
- featureFlags,
- keyguardUnlockAnimationController,
- mainHandler,
- delayableExecutor,
- messageRouter,
- wallpaperManager,
- startingSurfaceOptional,
- activityLaunchAnimator,
- notifPipelineFlags,
- jankMonitor,
- deviceStateManager,
- dreamOverlayStateController,
- wiredChargingRippleController
- );
- }
+ CentralSurfaces bindsCentralSurfaces(CentralSurfacesImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index 2eba325ff63d..6717bc768fbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -18,14 +18,12 @@ package com.android.systemui.statusbar.phone.fragment.dagger;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.LightsOutNotifController;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarDemoMode;
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import dagger.BindsInstance;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt
index e29959290355..ca667dddbe8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.phone.panelstate
/** A listener interface to be notified of state change events for the notification panel. */
-interface PanelStateListener {
+fun interface PanelStateListener {
/** Called when the panel's expansion state has changed. */
fun onPanelStateChanged(@PanelState state: Int)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt
new file mode 100644
index 000000000000..2789db874249
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt
@@ -0,0 +1,14 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import javax.inject.Inject
+
+/**
+ * An implementation on [ShadeOverScroller] that does nothing.
+ *
+ * At the moment there is only a concrete implementation [ShadeOverScroller] for split-shade, so
+ * this one is used when we are not in split-shade.
+ */
+class NoOpOverScroller @Inject constructor() : ShadeOverScroller {
+ override fun onPanelStateChanged(newPanelState: Int) {}
+ override fun onDragDownAmountChanged(newDragDownAmount: Float) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt
new file mode 100644
index 000000000000..f1cedeb21e0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt
@@ -0,0 +1,11 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import com.android.systemui.statusbar.phone.panelstate.PanelState
+
+/** Represents an over scroller for the non-lockscreen shade. */
+interface ShadeOverScroller {
+
+ fun onPanelStateChanged(@PanelState newPanelState: Int)
+
+ fun onDragDownAmountChanged(newDragDownAmount: Float)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt
new file mode 100644
index 000000000000..2762b9a38e92
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt
@@ -0,0 +1,73 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import android.content.Context
+import android.content.res.Configuration
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
+import com.android.systemui.statusbar.phone.panelstate.PanelState
+import com.android.systemui.statusbar.policy.ConfigurationController
+import javax.inject.Inject
+
+/** Controls the shade expansion transition on non-lockscreen. */
+@SysUISingleton
+class ShadeTransitionController
+@Inject
+constructor(
+ configurationController: ConfigurationController,
+ panelExpansionStateManager: PanelExpansionStateManager,
+ private val context: Context,
+ private val splitShadeOverScrollerFactory: SplitShadeOverScroller.Factory,
+ private val noOpOverScroller: NoOpOverScroller
+) {
+
+ lateinit var notificationPanelViewController: NotificationPanelViewController
+ lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController
+ lateinit var qs: QS
+
+ private var inSplitShade = false
+
+ private val splitShadeOverScroller by lazy {
+ splitShadeOverScrollerFactory.create(qs, notificationStackScrollLayoutController)
+ }
+ private val shadeOverScroller: ShadeOverScroller
+ get() =
+ if (inSplitShade && propertiesInitialized()) {
+ splitShadeOverScroller
+ } else {
+ noOpOverScroller
+ }
+
+ init {
+ updateResources()
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ }
+ })
+ panelExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged)
+ panelExpansionStateManager.addStateListener(this::onPanelStateChanged)
+ }
+
+ private fun updateResources() {
+ inSplitShade = context.resources.getBoolean(R.bool.config_use_split_notification_shade)
+ }
+
+ private fun onPanelStateChanged(@PanelState state: Int) {
+ shadeOverScroller.onPanelStateChanged(state)
+ }
+
+ private fun onPanelExpansionChanged(event: PanelExpansionChangeEvent) {
+ shadeOverScroller.onDragDownAmountChanged(event.dragDownPxAmount)
+ }
+
+ private fun propertiesInitialized() =
+ this::qs.isInitialized &&
+ this::notificationPanelViewController.isInitialized &&
+ this::notificationStackScrollLayoutController.isInitialized
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt
new file mode 100644
index 000000000000..71050f2e7c67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt
@@ -0,0 +1,142 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import android.animation.Animator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.Configuration
+import android.util.MathUtils
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.panelstate.PanelState
+import com.android.systemui.statusbar.phone.panelstate.STATE_CLOSED
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
+import com.android.systemui.statusbar.policy.ConfigurationController
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.io.PrintWriter
+
+class SplitShadeOverScroller
+@AssistedInject
+constructor(
+ configurationController: ConfigurationController,
+ dumpManager: DumpManager,
+ private val context: Context,
+ private val scrimController: ScrimController,
+ @Assisted private val qS: QS,
+ @Assisted private val nsslController: NotificationStackScrollLayoutController
+) : ShadeOverScroller {
+
+ private var releaseOverScrollDuration = 0L
+ private var maxOverScrollAmount = 0
+ private var previousOverscrollAmount = 0
+ private var dragDownAmount: Float = 0f
+ @PanelState private var panelState: Int = STATE_CLOSED
+ private var releaseOverScrollAnimator: Animator? = null
+
+ init {
+ updateResources()
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ }
+ })
+ dumpManager.registerDumpable(this::dump)
+ }
+
+ private fun updateResources() {
+ val resources = context.resources
+ maxOverScrollAmount = resources.getDimensionPixelSize(R.dimen.shade_max_over_scroll_amount)
+ releaseOverScrollDuration =
+ resources.getInteger(R.integer.lockscreen_shade_over_scroll_release_duration).toLong()
+ }
+
+ override fun onPanelStateChanged(@PanelState newPanelState: Int) {
+ if (shouldReleaseOverscroll(previousState = panelState, newState = newPanelState)) {
+ releaseOverScroll()
+ }
+ panelState = newPanelState
+ }
+
+ override fun onDragDownAmountChanged(newDragDownAmount: Float) {
+ if (dragDownAmount == newDragDownAmount) {
+ return
+ }
+ dragDownAmount = newDragDownAmount
+ if (shouldOverscroll()) {
+ overScroll(newDragDownAmount)
+ }
+ }
+
+ private fun shouldOverscroll() = panelState == STATE_OPENING
+
+ private fun shouldReleaseOverscroll(@PanelState previousState: Int, @PanelState newState: Int) =
+ previousState == STATE_OPENING && newState != STATE_OPENING
+
+ private fun overScroll(dragDownAmount: Float) {
+ val overscrollAmount: Int = calculateOverscrollAmount(dragDownAmount)
+ applyOverscroll(overscrollAmount)
+ previousOverscrollAmount = overscrollAmount
+ }
+
+ private fun calculateOverscrollAmount(dragDownAmount: Float): Int {
+ val fullHeight: Int = nsslController.height
+ val fullHeightProgress: Float = MathUtils.saturate(dragDownAmount / fullHeight)
+ return (fullHeightProgress * maxOverScrollAmount).toInt()
+ }
+
+ private fun applyOverscroll(overscrollAmount: Int) {
+ qS.setOverScrollAmount(overscrollAmount)
+ scrimController.setNotificationsOverScrollAmount(overscrollAmount)
+ nsslController.setOverScrollAmount(overscrollAmount)
+ }
+
+ private fun releaseOverScroll() {
+ val animator = ValueAnimator.ofInt(previousOverscrollAmount, 0)
+ animator.addUpdateListener {
+ val overScrollAmount = it.animatedValue as Int
+ qS.setOverScrollAmount(overScrollAmount)
+ scrimController.setNotificationsOverScrollAmount(overScrollAmount)
+ nsslController.setOverScrollAmount(overScrollAmount)
+ }
+ animator.interpolator = Interpolators.STANDARD
+ animator.duration = releaseOverScrollDuration
+ animator.start()
+ releaseOverScrollAnimator = animator
+ previousOverscrollAmount = 0
+ }
+
+ @VisibleForTesting
+ internal fun finishAnimations() {
+ releaseOverScrollAnimator?.end()
+ releaseOverScrollAnimator = null
+ }
+
+ private fun dump(pw: PrintWriter, strings: Array<String>) {
+ pw.println(
+ """
+ SplitShadeOverScroller:
+ Resources:
+ releaseOverScrollDuration: $releaseOverScrollDuration
+ maxOverScrollAmount: $maxOverScrollAmount
+ State:
+ previousOverscrollAmount: $previousOverscrollAmount
+ dragDownAmount: $dragDownAmount
+ panelState: $panelState
+ """.trimIndent())
+ }
+
+ @AssistedFactory
+ fun interface Factory {
+ fun create(
+ qS: QS,
+ nsslController: NotificationStackScrollLayoutController
+ ): SplitShadeOverScroller
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 7920d388c670..a50d3d607aec 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -474,7 +474,7 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
mThemeStyle = fetchThemeStyleFromSetting();
mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle);
mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle);
- if (colorSchemeIsApplied()) {
+ if (colorSchemeIsApplied() && !forceReload) {
Log.d(TAG, "Skipping overlay creation. Theme was already: " + mColorScheme);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/ColorUtil.kt b/packages/SystemUI/src/com/android/systemui/util/ColorUtil.kt
new file mode 100644
index 000000000000..27a53bf2ceda
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/ColorUtil.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.content.res.TypedArray
+import android.graphics.Color
+import android.view.ContextThemeWrapper
+
+/** Returns an ARGB color version of [color] at the given [alpha]. */
+fun getColorWithAlpha(color: Int, alpha: Float): Int =
+ Color.argb(
+ (alpha * 255).toInt(),
+ Color.red(color),
+ Color.green(color),
+ Color.blue(color)
+ )
+
+
+/**
+ * Returns the color provided at the specified {@param attrIndex} in {@param a} if it exists,
+ * otherwise, returns the color from the private attribute {@param privAttrId}.
+ */
+fun getPrivateAttrColorIfUnset(
+ ctw: ContextThemeWrapper, attrArray: TypedArray,
+ attrIndex: Int, defColor: Int, privAttrId: Int
+): Int {
+ // If the index is specified, use that value
+ var a = attrArray
+ if (a.hasValue(attrIndex)) {
+ return a.getColor(attrIndex, defColor)
+ }
+
+ // Otherwise fallback to the value of the private attribute
+ val customAttrs = intArrayOf(privAttrId)
+ a = ctw.obtainStyledAttributes(customAttrs)
+ val color = a.getColor(0, defColor)
+ a.recycle()
+ return color
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 8e5e1d2e1b87..5b5dca30620a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -105,25 +105,6 @@ public class Utils {
}
/**
- * Returns the color provided at the specified {@param attrIndex} in {@param a} if it exists,
- * otherwise, returns the color from the private attribute {@param privAttrId}.
- */
- public static int getPrivateAttrColorIfUnset(ContextThemeWrapper ctw, TypedArray a,
- int attrIndex, int defColor, int privAttrId) {
- // If the index is specified, use that value
- if (a.hasValue(attrIndex)) {
- return a.getColor(attrIndex, defColor);
- }
-
- // Otherwise fallback to the value of the private attribute
- int[] customAttrs = { privAttrId };
- a = ctw.obtainStyledAttributes(customAttrs);
- int color = a.getColor(0, defColor);
- a.recycle();
- return color;
- }
-
- /**
* Gets the {@link R.dimen#status_bar_header_height_keyguard}.
*/
public static int getStatusBarHeaderHeightKeyguard(Context context) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
index 4beec574cd2a..01365b43b4b8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -70,7 +70,7 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
private Display mSecondaryDisplay;
// This display is in a different group from the default and secondary displays.
- private Display mDifferentGroupDisplay;
+ private Display mAlwaysUnlockedDisplay;
@Before
public void setUp() {
@@ -86,12 +86,12 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
Display.DEFAULT_DISPLAY + 1,
new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
- DisplayInfo differentGroupInfo = new DisplayInfo();
- differentGroupInfo.displayId = Display.DEFAULT_DISPLAY + 2;
- differentGroupInfo.displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
- mDifferentGroupDisplay = new Display(DisplayManagerGlobal.getInstance(),
+ DisplayInfo alwaysUnlockedDisplayInfo = new DisplayInfo();
+ alwaysUnlockedDisplayInfo.displayId = Display.DEFAULT_DISPLAY + 2;
+ alwaysUnlockedDisplayInfo.flags = Display.FLAG_ALWAYS_UNLOCKED;
+ mAlwaysUnlockedDisplay = new Display(DisplayManagerGlobal.getInstance(),
Display.DEFAULT_DISPLAY,
- differentGroupInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
+ alwaysUnlockedDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
}
@Test
@@ -110,18 +110,18 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
}
@Test
- public void testShow_includeNonDefaultGroupDisplay() {
+ public void testShow_includeAlwaysUnlockedDisplay() {
when(mDisplayManager.getDisplays()).thenReturn(
- new Display[]{mDefaultDisplay, mDifferentGroupDisplay});
+ new Display[]{mDefaultDisplay, mAlwaysUnlockedDisplay});
mManager.show();
verify(mManager, never()).createPresentation(any());
}
@Test
- public void testShow_includeSecondaryAndNonDefaultGroupDisplays() {
+ public void testShow_includeSecondaryAndAlwaysUnlockedDisplays() {
when(mDisplayManager.getDisplays()).thenReturn(
- new Display[]{mDefaultDisplay, mSecondaryDisplay, mDifferentGroupDisplay});
+ new Display[]{mDefaultDisplay, mSecondaryDisplay, mAlwaysUnlockedDisplay});
mManager.show();
verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
new file mode 100644
index 000000000000..aff94eb7aef5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.inputmethod.InputMethodManager
+import androidx.test.filters.SmallTest
+import com.android.internal.util.LatencyTracker
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.util.concurrency.DelayableExecutor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardPasswordViewControllerTest : SysuiTestCase() {
+ @Mock
+ private lateinit var keyguardPasswordView: KeyguardPasswordView
+ @Mock
+ lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock
+ lateinit var securityMode: KeyguardSecurityModel.SecurityMode
+ @Mock
+ lateinit var lockPatternUtils: LockPatternUtils
+ @Mock
+ lateinit var keyguardSecurityCallback: KeyguardSecurityCallback
+ @Mock
+ lateinit var messageAreaControllerFactory: KeyguardMessageAreaController.Factory
+ @Mock
+ lateinit var latencyTracker: LatencyTracker
+ @Mock
+ lateinit var inputMethodManager: InputMethodManager
+ @Mock
+ lateinit var emergencyButtonController: EmergencyButtonController
+ @Mock
+ lateinit var mainExecutor: DelayableExecutor
+ @Mock
+ lateinit var falsingCollector: FalsingCollector
+ @Mock
+ lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock
+ private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+ @Mock
+ private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+
+ private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ Mockito.`when`(keyguardPasswordView
+ .findViewById<KeyguardMessageArea>(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea)
+ Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea))
+ .thenReturn(mKeyguardMessageAreaController)
+ keyguardPasswordViewController = KeyguardPasswordViewController(
+ keyguardPasswordView,
+ keyguardUpdateMonitor,
+ securityMode,
+ lockPatternUtils,
+ keyguardSecurityCallback,
+ messageAreaControllerFactory,
+ latencyTracker,
+ inputMethodManager,
+ emergencyButtonController,
+ mainExecutor,
+ mContext.resources,
+ falsingCollector,
+ statusBarKeyguardViewManager
+ )
+ }
+
+ @Test
+ fun testFocusWhenBouncerIsShown() {
+ Mockito.`when`(statusBarKeyguardViewManager.isBouncerShowing).thenReturn(true)
+ Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true)
+ keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED)
+ keyguardPasswordView.post { verify(keyguardPasswordView).requestFocus() }
+ }
+
+ @Test
+ fun testDoNotFocusWhenBouncerIsHidden() {
+ Mockito.`when`(statusBarKeyguardViewManager.isBouncerShowing).thenReturn(false)
+ Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true)
+ keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED)
+ verify(keyguardPasswordView, never()).requestFocus()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index a819a7a0f815..7b7dfdce90b2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -51,6 +51,7 @@ import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -123,6 +124,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
private UserSwitcherController mUserSwitcherController;
@Mock
private SessionTracker mSessionTracker;
+ @Mock
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -150,7 +153,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
(KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
SecurityMode.Password, mLockPatternUtils, null,
mKeyguardMessageAreaControllerFactory, null, null, mEmergencyButtonController,
- null, mock(Resources.class), null);
+ null, mock(Resources.class), null, mStatusBarKeyguardViewManager);
mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory(
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
@@ -287,4 +290,14 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
}
+
+ @Test
+ public void addUserSwitchCallback() {
+ mKeyguardSecurityContainerController.onViewAttached();
+ verify(mUserSwitcherController)
+ .addUserSwitchCallback(any(UserSwitcherController.UserSwitchCallback.class));
+ mKeyguardSecurityContainerController.onViewDetached();
+ verify(mUserSwitcherController)
+ .removeUserSwitchCallback(any(UserSwitcherController.UserSwitchCallback.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 1753157c631d..650a5d0a8712 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -22,7 +22,6 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -55,8 +54,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
@Mock
DozeParameters mDozeParameters;
@Mock
- KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
- @Mock
ScreenOffAnimationController mScreenOffAnimationController;
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
@@ -75,7 +72,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
mKeyguardUpdateMonitor,
mConfigurationController,
mDozeParameters,
- mKeyguardUnlockAnimationController,
mScreenOffAnimationController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
index e62b4e63e3d5..55f0591b6ce0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
@@ -142,6 +142,25 @@ class DisplayCutoutBaseViewTest : SysuiTestCase() {
assertThat(cutoutBaseView.protectionRect).isEqualTo(pathBounds)
}
+ @Test
+ fun testCutoutProtection_withDisplayRatio() {
+ setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */)
+ whenever(cutoutBaseView.getPhysicalPixelDisplaySizeRatio()).thenReturn(0.5f)
+ val bounds = Rect(0, 0, 10, 10)
+ val path = Path()
+ val pathBounds = RectF(bounds)
+ path.addRect(pathBounds, Path.Direction.CCW)
+
+ context.mainExecutor.execute {
+ cutoutBaseView.setProtection(path, bounds)
+ cutoutBaseView.enableShowProtection(true)
+ }
+ waitForIdleSync()
+
+ assertThat(cutoutBaseView.protectionPath.isRect(pathBounds)).isTrue()
+ assertThat(cutoutBaseView.protectionRect).isEqualTo(RectF(0f, 0f, 5f, 5f))
+ }
+
private fun setupDisplayCutoutBaseView(fillCutout: Boolean, hasCutout: Boolean) {
mContext.orCreateTestableResources.addOverride(
R.array.config_displayUniqueIdArray, arrayOf<String>())
@@ -151,6 +170,7 @@ class DisplayCutoutBaseViewTest : SysuiTestCase() {
cutoutBaseView = spy(DisplayCutoutBaseView(mContext))
whenever(cutoutBaseView.display).thenReturn(mockDisplay)
whenever(cutoutBaseView.rootView).thenReturn(mockRootView)
+ whenever(cutoutBaseView.getPhysicalPixelDisplaySizeRatio()).thenReturn(1f)
whenever(mockDisplay.getDisplayInfo(eq(cutoutBaseView.displayInfo))
).then {
val info = it.getArgument<DisplayInfo>(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index fb1a968acceb..2d8c4d5dceb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -17,6 +17,7 @@ import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -125,4 +126,69 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished(
false /* cancelled */)
}
+
+ /**
+ * If we requested that the surface behind be made visible, and we're not flinging away the
+ * keyguard, it means that we're swiping to unlock and want the surface visible so it can follow
+ * the user's touch event as they swipe to unlock.
+ *
+ * In this case, we should verify that the surface was made visible via the alpha fade in
+ * animator, and verify that we did not start the canned animation to animate the surface in
+ * (since it's supposed to be following the touch events).
+ */
+ @Test
+ fun fadeInSurfaceBehind_ifRequestedShowSurface_butNotFlinging() {
+ `when`(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(false)
+
+ keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ true /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ assertTrue(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
+ assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
+ }
+
+ /**
+ * We requested the surface behind to be made visible, but we're now flinging to dismiss the
+ * keyguard. This means this was a swipe to dismiss gesture but the user flung the keyguard and
+ * lifted their finger while we were requesting the surface be made visible.
+ *
+ * In this case, we should verify that we are playing the canned unlock animation and not
+ * simply fading in the surface.
+ */
+ @Test
+ fun playCannedUnlockAnimation_ifRequestedShowSurface_andFlinging() {
+ `when`(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(true)
+
+ keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ true /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
+ assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
+ }
+
+ /**
+ * We never requested the surface behind to be made visible, which means no swiping to unlock
+ * ever happened and we're just playing the simple canned animation (happens via UDFPS unlock,
+ * long press on the lock icon, etc).
+ *
+ * In this case, we should verify that we are playing the canned unlock animation and not
+ * simply fading in the surface.
+ */
+ @Test
+ fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() {
+ keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
+ assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt
index 8f967ab5294f..65d501442d87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt
@@ -19,9 +19,9 @@ package com.android.systemui.media
import org.mockito.Mockito.`when` as whenever
import android.animation.ValueAnimator
import android.graphics.Color
-import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.monet.ColorScheme
import junit.framework.Assert.assertEquals
@@ -46,28 +46,35 @@ class ColorSchemeTransitionTest : SysuiTestCase() {
private interface ExtractCB : (ColorScheme) -> Int
private interface ApplyCB : (Int) -> Unit
- private lateinit var colorTransition: ColorTransition
+ private lateinit var colorTransition: AnimatingColorTransition
private lateinit var colorSchemeTransition: ColorSchemeTransition
- @Mock private lateinit var mockTransition: ColorTransition
+ @Mock private lateinit var mockAnimatingTransition: AnimatingColorTransition
+ @Mock private lateinit var mockGenericTransition: GenericColorTransition
@Mock private lateinit var valueAnimator: ValueAnimator
@Mock private lateinit var colorScheme: ColorScheme
@Mock private lateinit var extractColor: ExtractCB
@Mock private lateinit var applyColor: ApplyCB
- private lateinit var transitionFactory: ColorTransitionFactory
+ private lateinit var animatingColorTransitionFactory: AnimatingColorTransitionFactory
+ private lateinit var genericColorTransitionFactory: GenericColorTransitionFactory
@Mock private lateinit var mediaViewHolder: MediaViewHolder
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
@Before
fun setUp() {
- transitionFactory = { default, extractColor, applyColor -> mockTransition }
+ animatingColorTransitionFactory = { _, _, _ -> mockAnimatingTransition }
+ genericColorTransitionFactory = { _ -> mockGenericTransition }
whenever(extractColor.invoke(colorScheme)).thenReturn(TARGET_COLOR)
- colorSchemeTransition = ColorSchemeTransition(context, mediaViewHolder, transitionFactory)
+ colorSchemeTransition = ColorSchemeTransition(
+ context, mediaViewHolder, animatingColorTransitionFactory, genericColorTransitionFactory
+ )
- colorTransition = object : ColorTransition(DEFAULT_COLOR, extractColor, applyColor) {
+ colorTransition = object : AnimatingColorTransition(
+ DEFAULT_COLOR, extractColor, applyColor
+ ) {
override fun buildAnimator(): ValueAnimator {
return valueAnimator
}
@@ -142,6 +149,7 @@ class ColorSchemeTransitionTest : SysuiTestCase() {
@Test
fun testColorSchemeTransition_update() {
colorSchemeTransition.updateColorScheme(colorScheme)
- verify(mockTransition, times(6)).updateColorScheme(colorScheme)
+ verify(mockAnimatingTransition, times(6)).updateColorScheme(colorScheme)
+ verify(mockGenericTransition).updateColorScheme(colorScheme)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index b8c85bb41726..76171b2c4054 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -124,7 +124,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var collapsedSet: ConstraintSet
@Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
@Mock private lateinit var mediaCarouselController: MediaCarouselController
- @Mock private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
@Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var transitionParent: ViewGroup
private lateinit var appIcon: ImageView
@@ -270,7 +269,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
smartspaceData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
packageName = PACKAGE,
instanceId = instanceId,
- recommendations = listOf(smartspaceAction),
+ recommendations = listOf(smartspaceAction, smartspaceAction, smartspaceAction),
cardAction = smartspaceAction
)
}
@@ -294,9 +293,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
*/
private fun initMediaViewHolderMocks() {
whenever(seekBarViewModel.progress).thenReturn(seekBarData)
- whenever(mediaCarouselController.mediaCarouselScrollHandler)
- .thenReturn(mediaCarouselScrollHandler)
- whenever(mediaCarouselScrollHandler.qsExpanded).thenReturn(false)
// Set up mock views for the players
appIcon = ImageView(context)
@@ -1448,6 +1444,66 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
+ fun bindRecommendation_listHasTooFewRecs_notDisplayed() {
+ player.attachRecommendation(recommendationViewHolder)
+ val icon = Icon.createWithResource(context, R.drawable.ic_1x_mobiledata)
+ val data = smartspaceData.copy(
+ recommendations = listOf(
+ SmartspaceAction.Builder("id1", "title1")
+ .setSubtitle("subtitle1")
+ .setIcon(icon)
+ .setExtras(Bundle.EMPTY)
+ .build(),
+ SmartspaceAction.Builder("id2", "title2")
+ .setSubtitle("subtitle2")
+ .setIcon(icon)
+ .setExtras(Bundle.EMPTY)
+ .build(),
+ )
+ )
+
+ player.bindRecommendation(data)
+
+ assertThat(recTitle1.text).isEqualTo("")
+ verify(mediaViewController, never()).refreshState()
+ }
+
+ @Test
+ fun bindRecommendation_listHasTooFewRecsWithIcons_notDisplayed() {
+ player.attachRecommendation(recommendationViewHolder)
+ val icon = Icon.createWithResource(context, R.drawable.ic_1x_mobiledata)
+ val data = smartspaceData.copy(
+ recommendations = listOf(
+ SmartspaceAction.Builder("id1", "title1")
+ .setSubtitle("subtitle1")
+ .setIcon(icon)
+ .setExtras(Bundle.EMPTY)
+ .build(),
+ SmartspaceAction.Builder("id2", "title2")
+ .setSubtitle("subtitle2")
+ .setIcon(icon)
+ .setExtras(Bundle.EMPTY)
+ .build(),
+ SmartspaceAction.Builder("id2", "empty icon 1")
+ .setSubtitle("subtitle2")
+ .setIcon(null)
+ .setExtras(Bundle.EMPTY)
+ .build(),
+ SmartspaceAction.Builder("id2", "empty icon 2")
+ .setSubtitle("subtitle2")
+ .setIcon(null)
+ .setExtras(Bundle.EMPTY)
+ .build(),
+ )
+ )
+
+ player.bindRecommendation(data)
+
+ assertThat(recTitle1.text).isEqualTo("")
+ verify(mediaViewController, never()).refreshState()
+ }
+
+ @Test
fun bindRecommendation_hasTitlesAndSubtitles() {
player.attachRecommendation(recommendationViewHolder)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index 1f9490ab3851..6a532d74967f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -110,7 +110,7 @@ class MediaDataFilterTest : SysuiTestCase() {
`when`(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
`when`(smartspaceData.isActive).thenReturn(true)
- `when`(smartspaceData.isValid).thenReturn(true)
+ `when`(smartspaceData.isValid()).thenReturn(true)
`when`(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE)
`when`(smartspaceData.recommendations).thenReturn(listOf(smartspaceMediaRecommendationItem))
`when`(smartspaceData.headphoneConnectionTimeMillis).thenReturn(
@@ -196,22 +196,108 @@ class MediaDataFilterTest : SysuiTestCase() {
}
@Test
- fun testHasAnyMediaOrRecommendation() {
+ fun hasAnyMedia_noMediaSet_returnsFalse() {
+ assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
+ }
+
+ @Test
+ fun hasAnyMedia_mediaSet_returnsTrue() {
+ mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
+
+ assertThat(mediaDataFilter.hasAnyMedia()).isTrue()
+ }
+
+ @Test
+ fun hasAnyMedia_recommendationSet_returnsFalse() {
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+ assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
+ }
+
+ @Test
+ fun hasAnyMediaOrRecommendation_noMediaSet_returnsFalse() {
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
+ }
+ @Test
+ fun hasAnyMediaOrRecommendation_mediaSet_returnsTrue() {
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
+
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
- assertThat(mediaDataFilter.hasAnyMedia()).isTrue()
}
@Test
- fun testHasActiveMediaOrRecommendation() {
- assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
+ fun hasAnyMediaOrRecommendation_recommendationSet_returnsTrue() {
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+ assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
+ }
+
+ @Test
+ fun hasActiveMedia_noMediaSet_returnsFalse() {
+ assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
+ }
+
+ @Test
+ fun hasActiveMedia_inactiveMediaSet_returnsFalse() {
+ val data = dataMain.copy(active = false)
+ mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
+
+ assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
+ }
+
+ @Test
+ fun hasActiveMedia_activeMediaSet_returnsTrue() {
val data = dataMain.copy(active = true)
+ mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
+ assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+ }
+
+ @Test
+ fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() {
+ assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
+ }
+
+ @Test
+ fun hasActiveMediaOrRecommendation_inactiveMediaSet_returnsFalse() {
+ val data = dataMain.copy(active = false)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
+
+ assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
+ }
+
+ @Test
+ fun hasActiveMediaOrRecommendation_activeMediaSet_returnsTrue() {
+ val data = dataMain.copy(active = true)
+ mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
+
+ assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
+ }
+
+ @Test
+ fun hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalse() {
+ `when`(smartspaceData.isActive).thenReturn(false)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+ assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
+ }
+
+ @Test
+ fun hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalse() {
+ `when`(smartspaceData.isValid()).thenReturn(false)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+ assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
+ }
+
+ @Test
+ fun hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTrue() {
+ `when`(smartspaceData.isActive).thenReturn(true)
+ `when`(smartspaceData.isValid()).thenReturn(true)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
- assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
}
@Test
@@ -332,7 +418,7 @@ class MediaDataFilterTest : SysuiTestCase() {
@Test
fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedia() {
- `when`(smartspaceData.isValid).thenReturn(false)
+ `when`(smartspaceData.isValid()).thenReturn(false)
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 0cbceb6700b4..e42ae1c2f878 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -7,6 +7,7 @@ import android.app.smartspace.SmartspaceAction
import android.app.smartspace.SmartspaceTarget
import android.content.Intent
import android.graphics.Bitmap
+import android.graphics.drawable.Icon
import android.media.MediaDescription
import android.media.MediaMetadata
import android.media.session.MediaController
@@ -30,6 +31,7 @@ import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
@@ -47,6 +49,7 @@ import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.junit.MockitoJUnit
@@ -95,6 +98,7 @@ class MediaDataManagerTest : SysuiTestCase() {
lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
@Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
@Mock private lateinit var mediaRecommendationItem: SmartspaceAction
+ lateinit var validRecommendationList: List<SmartspaceAction>
@Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction
@Mock private lateinit var mediaFlags: MediaFlags
@Mock private lateinit var logger: MediaUiEventLogger
@@ -170,12 +174,17 @@ class MediaDataManagerTest : SysuiTestCase() {
putString("package_name", PACKAGE_NAME)
putParcelable("dismiss_intent", DISMISS_INTENT)
}
+ val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
whenever(mediaSmartspaceBaseAction.extras).thenReturn(recommendationExtras)
whenever(mediaSmartspaceTarget.baseAction).thenReturn(mediaSmartspaceBaseAction)
whenever(mediaRecommendationItem.extras).thenReturn(recommendationExtras)
+ whenever(mediaRecommendationItem.icon).thenReturn(icon)
+ validRecommendationList = listOf(
+ mediaRecommendationItem, mediaRecommendationItem, mediaRecommendationItem
+ )
whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE)
whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
- whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf(mediaRecommendationItem))
+ whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList)
whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L)
whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false)
whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
@@ -505,10 +514,9 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(SmartspaceMediaData(
targetId = KEY_MEDIA_SMARTSPACE,
isActive = true,
- isValid = true,
packageName = PACKAGE_NAME,
cardAction = mediaSmartspaceBaseAction,
- recommendations = listOf(mediaRecommendationItem),
+ recommendations = validRecommendationList,
dismissIntent = DISMISS_INTENT,
headphoneConnectionTimeMillis = 1234L,
instanceId = InstanceId.fakeInstanceId(instanceId))),
@@ -527,7 +535,6 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = KEY_MEDIA_SMARTSPACE,
isActive = true,
- isValid = false,
dismissIntent = DISMISS_INTENT,
headphoneConnectionTimeMillis = 1234L,
instanceId = InstanceId.fakeInstanceId(instanceId))),
@@ -553,7 +560,6 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = KEY_MEDIA_SMARTSPACE,
isActive = true,
- isValid = false,
dismissIntent = null,
headphoneConnectionTimeMillis = 1234L,
instanceId = InstanceId.fakeInstanceId(instanceId))),
@@ -938,6 +944,38 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(instanceId), eq(MediaData.PLAYBACK_CAST_REMOTE))
}
+ @Test
+ fun testPlaybackStateChange_keyExists_callsListener() {
+ // Notification has been added
+ addNotificationAndLoad()
+ val callbackCaptor = argumentCaptor<(String, PlaybackState) -> Unit>()
+ verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
+
+ // Callback gets an updated state
+ val state = PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PLAYING, 0L, 1f)
+ .build()
+ callbackCaptor.value.invoke(KEY, state)
+
+ // Listener is notified of updated state
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY),
+ capture(mediaDataCaptor), eq(true), eq(0), eq(false))
+ assertThat(mediaDataCaptor.value.isPlaying).isTrue()
+ }
+
+ @Test
+ fun testPlaybackStateChange_keyDoesNotExist_doesNothing() {
+ val state = PlaybackState.Builder().build()
+ val callbackCaptor = argumentCaptor<(String, PlaybackState) -> Unit>()
+ verify(mediaTimeoutListener).stateCallback = capture(callbackCaptor)
+
+ // No media added with this key
+
+ callbackCaptor.value.invoke(KEY, state)
+ verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), anyInt(),
+ anyBoolean())
+ }
+
/**
* Helper function to add a media notification and capture the resulting MediaData
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index 3e98a12b4564..3d3ac836d264 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -168,16 +168,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
@Test
fun testOnLoad_checksForResume_badService() {
- // Set up MBS that will allow connection but not return valid media
- val pm = mock(PackageManager::class.java)
- whenever(mockContext.packageManager).thenReturn(pm)
- val resolveInfo = ResolveInfo()
- val serviceInfo = ServiceInfo()
- serviceInfo.packageName = PACKAGE_NAME
- resolveInfo.serviceInfo = serviceInfo
- resolveInfo.serviceInfo.name = CLASS_NAME
- val resumeInfo = listOf(resolveInfo)
- whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
+ setUpMbsWithValidResolveInfo()
whenever(resumeBrowser.testConnection()).thenAnswer {
callbackCaptor.value.onError()
@@ -213,16 +204,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
@Test
fun testOnLoad_checksForResume_hasService() {
- // Set up mocks to successfully find a MBS that returns valid media
- val pm = mock(PackageManager::class.java)
- whenever(mockContext.packageManager).thenReturn(pm)
- val resolveInfo = ResolveInfo()
- val serviceInfo = ServiceInfo()
- serviceInfo.packageName = PACKAGE_NAME
- resolveInfo.serviceInfo = serviceInfo
- resolveInfo.serviceInfo.name = CLASS_NAME
- val resumeInfo = listOf(resolveInfo)
- whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
+ setUpMbsWithValidResolveInfo()
val description = MediaDescription.Builder().setTitle(TITLE).build()
val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
@@ -288,16 +270,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
@Test
fun testGetResumeAction_restarts() {
- // Set up mocks to successfully find a MBS that returns valid media
- val pm = mock(PackageManager::class.java)
- whenever(mockContext.packageManager).thenReturn(pm)
- val resolveInfo = ResolveInfo()
- val serviceInfo = ServiceInfo()
- serviceInfo.packageName = PACKAGE_NAME
- resolveInfo.serviceInfo = serviceInfo
- resolveInfo.serviceInfo.name = CLASS_NAME
- val resumeInfo = listOf(resolveInfo)
- whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
+ setUpMbsWithValidResolveInfo()
val description = MediaDescription.Builder().setTitle(TITLE).build()
val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
@@ -426,4 +399,91 @@ class MediaResumeListenerTest : SysuiTestCase() {
}
verify(sharedPrefsEditor, times(1)).apply()
}
-} \ No newline at end of file
+
+ @Test
+ fun testOnMediaDataLoaded_newKeyDifferent_oldMediaBrowserDisconnected() {
+ setUpMbsWithValidResolveInfo()
+
+ resumeListener.onMediaDataLoaded(key = KEY, oldKey = null, data)
+ executor.runAllReady()
+
+ resumeListener.onMediaDataLoaded(key = "newKey", oldKey = KEY, data)
+
+ verify(resumeBrowser).disconnect()
+ }
+
+ @Test
+ fun testOnMediaDataLoaded_updatingResumptionListError_mediaBrowserDisconnected() {
+ setUpMbsWithValidResolveInfo()
+
+ // Set up mocks to return with an error
+ whenever(resumeBrowser.testConnection()).thenAnswer {
+ callbackCaptor.value.onError()
+ }
+
+ resumeListener.onMediaDataLoaded(key = KEY, oldKey = null, data)
+ executor.runAllReady()
+
+ // Ensure we disconnect the browser
+ verify(resumeBrowser).disconnect()
+ }
+
+ @Test
+ fun testOnMediaDataLoaded_trackAdded_mediaBrowserDisconnected() {
+ setUpMbsWithValidResolveInfo()
+
+ // Set up mocks to return with a track added
+ val description = MediaDescription.Builder().setTitle(TITLE).build()
+ val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+ whenever(resumeBrowser.testConnection()).thenAnswer {
+ callbackCaptor.value.addTrack(description, component, resumeBrowser)
+ }
+
+ resumeListener.onMediaDataLoaded(key = KEY, oldKey = null, data)
+ executor.runAllReady()
+
+ // Ensure we disconnect the browser
+ verify(resumeBrowser).disconnect()
+ }
+
+ @Test
+ fun testResumeAction_oldMediaBrowserDisconnected() {
+ setUpMbsWithValidResolveInfo()
+
+ val description = MediaDescription.Builder().setTitle(TITLE).build()
+ val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+ whenever(resumeBrowser.testConnection()).thenAnswer {
+ callbackCaptor.value.addTrack(description, component, resumeBrowser)
+ }
+
+ // Load media data that will require us to get the resume action
+ val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
+ resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+ executor.runAllReady()
+ verify(mediaDataManager, times(2)).setResumeAction(eq(KEY), capture(actionCaptor))
+
+ // Set up our factory to return a new browser so we can verify we disconnected the old one
+ val newResumeBrowser = mock(ResumeMediaBrowser::class.java)
+ whenever(resumeBrowserFactory.create(capture(callbackCaptor), any()))
+ .thenReturn(newResumeBrowser)
+
+ // When the resume action is run
+ actionCaptor.value.run()
+
+ // Then we disconnect the old one
+ verify(resumeBrowser).disconnect()
+ }
+
+ /** Sets up mocks to successfully find a MBS that returns valid media. */
+ private fun setUpMbsWithValidResolveInfo() {
+ val pm = mock(PackageManager::class.java)
+ whenever(mockContext.packageManager).thenReturn(pm)
+ val resolveInfo = ResolveInfo()
+ val serviceInfo = ServiceInfo()
+ serviceInfo.packageName = PACKAGE_NAME
+ resolveInfo.serviceInfo = serviceInfo
+ resolveInfo.serviceInfo.name = CLASS_NAME
+ val resumeInfo = listOf(resolveInfo)
+ whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index 60cbb1754db6..91c0cc2ff891 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -65,6 +65,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
@Mock private lateinit var logger: MediaTimeoutLogger
private lateinit var executor: FakeExecutor
@Mock private lateinit var timeoutCallback: (String, Boolean) -> Unit
+ @Mock private lateinit var stateCallback: (String, PlaybackState) -> Unit
@Captor private lateinit var mediaCallbackCaptor: ArgumentCaptor<MediaController.Callback>
@JvmField @Rule val mockito = MockitoJUnit.rule()
private lateinit var metadataBuilder: MediaMetadata.Builder
@@ -80,6 +81,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
executor = FakeExecutor(FakeSystemClock())
mediaTimeoutListener = MediaTimeoutListener(mediaControllerFactory, executor, logger)
mediaTimeoutListener.timeoutCallback = timeoutCallback
+ mediaTimeoutListener.stateCallback = stateCallback
// Create a media session and notification for testing.
metadataBuilder = MediaMetadata.Builder().apply {
@@ -368,4 +370,169 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
// THEN the timeout runnable is cancelled
assertThat(executor.numPending()).isEqualTo(0)
}
+
+ @Test
+ fun testOnMediaDataLoaded_playbackActionsChanged_noCallback() {
+ // Load media data once
+ val pausedState = PlaybackState.Builder()
+ .setActions(PlaybackState.ACTION_PAUSE)
+ .build()
+ loadMediaDataWithPlaybackState(pausedState)
+
+ // When media data is loaded again, with different actions
+ val playingState = PlaybackState.Builder()
+ .setActions(PlaybackState.ACTION_PLAY)
+ .build()
+ loadMediaDataWithPlaybackState(playingState)
+
+ // Then the callback is not invoked
+ verify(stateCallback, never()).invoke(eq(KEY), any())
+ }
+
+ @Test
+ fun testOnPlaybackStateChanged_playbackActionsChanged_sendsCallback() {
+ // Load media data once
+ val pausedState = PlaybackState.Builder()
+ .setActions(PlaybackState.ACTION_PAUSE)
+ .build()
+ loadMediaDataWithPlaybackState(pausedState)
+
+ // When the playback state changes, and has different actions
+ val playingState = PlaybackState.Builder()
+ .setActions(PlaybackState.ACTION_PLAY)
+ .build()
+ mediaCallbackCaptor.value.onPlaybackStateChanged(playingState)
+
+ // Then the callback is invoked
+ verify(stateCallback).invoke(eq(KEY), eq(playingState!!))
+ }
+
+ @Test
+ fun testOnPlaybackStateChanged_differentCustomActions_sendsCallback() {
+ val customOne = PlaybackState.CustomAction.Builder(
+ "ACTION_1",
+ "custom action 1",
+ android.R.drawable.ic_media_ff)
+ .build()
+ val pausedState = PlaybackState.Builder()
+ .setActions(PlaybackState.ACTION_PAUSE)
+ .addCustomAction(customOne)
+ .build()
+ loadMediaDataWithPlaybackState(pausedState)
+
+ // When the playback state actions change
+ val customTwo = PlaybackState.CustomAction.Builder(
+ "ACTION_2",
+ "custom action 2",
+ android.R.drawable.ic_media_rew)
+ .build()
+ val pausedStateTwoActions = PlaybackState.Builder()
+ .setActions(PlaybackState.ACTION_PAUSE)
+ .addCustomAction(customOne)
+ .addCustomAction(customTwo)
+ .build()
+ mediaCallbackCaptor.value.onPlaybackStateChanged(pausedStateTwoActions)
+
+ // Then the callback is invoked
+ verify(stateCallback).invoke(eq(KEY), eq(pausedStateTwoActions!!))
+ }
+
+ @Test
+ fun testOnPlaybackStateChanged_sameActions_noCallback() {
+ val stateWithActions = PlaybackState.Builder()
+ .setActions(PlaybackState.ACTION_PLAY)
+ .build()
+ loadMediaDataWithPlaybackState(stateWithActions)
+
+ // When the playback state updates with the same actions
+ mediaCallbackCaptor.value.onPlaybackStateChanged(stateWithActions)
+
+ // Then the callback is not invoked again
+ verify(stateCallback, never()).invoke(eq(KEY), any())
+ }
+
+ @Test
+ fun testOnPlaybackStateChanged_sameCustomActions_noCallback() {
+ val actionName = "custom action"
+ val actionIcon = android.R.drawable.ic_media_ff
+ val customOne = PlaybackState.CustomAction.Builder(actionName, actionName, actionIcon)
+ .build()
+ val stateOne = PlaybackState.Builder()
+ .setActions(PlaybackState.ACTION_PAUSE)
+ .addCustomAction(customOne)
+ .build()
+ loadMediaDataWithPlaybackState(stateOne)
+
+ // When the playback state is updated, but has the same actions
+ val customTwo = PlaybackState.CustomAction.Builder(actionName, actionName, actionIcon)
+ .build()
+ val stateTwo = PlaybackState.Builder()
+ .setActions(PlaybackState.ACTION_PAUSE)
+ .addCustomAction(customTwo)
+ .build()
+ mediaCallbackCaptor.value.onPlaybackStateChanged(stateTwo)
+
+ // Then the callback is not invoked
+ verify(stateCallback, never()).invoke(eq(KEY), any())
+ }
+
+ @Test
+ fun testOnMediaDataLoaded_isPlayingChanged_noCallback() {
+ // Load media data in paused state
+ val pausedState = PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PAUSED, 0L, 0f)
+ .build()
+ loadMediaDataWithPlaybackState(pausedState)
+
+ // When media data is loaded again but playing
+ val playingState = PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PLAYING, 0L, 1f)
+ .build()
+ loadMediaDataWithPlaybackState(playingState)
+
+ // Then the callback is not invoked
+ verify(stateCallback, never()).invoke(eq(KEY), any())
+ }
+
+ @Test
+ fun testOnPlaybackStateChanged_isPlayingChanged_sendsCallback() {
+ // Load media data in paused state
+ val pausedState = PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PAUSED, 0L, 0f)
+ .build()
+ loadMediaDataWithPlaybackState(pausedState)
+
+ // When the playback state changes to playing
+ val playingState = PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PLAYING, 0L, 1f)
+ .build()
+ mediaCallbackCaptor.value.onPlaybackStateChanged(playingState)
+
+ // Then the callback is invoked
+ verify(stateCallback).invoke(eq(KEY), eq(playingState!!))
+ }
+
+ @Test
+ fun testOnPlaybackStateChanged_isPlayingSame_noCallback() {
+ // Load media data in paused state
+ val pausedState = PlaybackState.Builder()
+ .setState(PlaybackState.STATE_PAUSED, 0L, 0f)
+ .build()
+ loadMediaDataWithPlaybackState(pausedState)
+
+ // When the playback state is updated, but still not playing
+ val playingState = PlaybackState.Builder()
+ .setState(PlaybackState.STATE_STOPPED, 0L, 0f)
+ .build()
+ mediaCallbackCaptor.value.onPlaybackStateChanged(playingState)
+
+ // Then the callback is not invoked
+ verify(stateCallback, never()).invoke(eq(KEY), eq(playingState!!))
+ }
+
+ private fun loadMediaDataWithPlaybackState(state: PlaybackState) {
+ `when`(mediaController.playbackState).thenReturn(state)
+ mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
index dfa7c66b38f9..06d45de699e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
@@ -63,6 +63,7 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
@Mock lateinit var callback: ResumeMediaBrowser.Callback
@Mock lateinit var listener: MediaResumeListener
@Mock lateinit var service: MediaBrowserService
+ @Mock lateinit var logger: ResumeMediaBrowserLogger
@Mock lateinit var browserFactory: MediaBrowserFactory
@Mock lateinit var browser: MediaBrowser
@Mock lateinit var token: MediaSession.Token
@@ -81,8 +82,14 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
whenever(mediaController.transportControls).thenReturn(transportControls)
- resumeBrowser = TestableResumeMediaBrowser(context, callback, component, browserFactory,
- mediaController)
+ resumeBrowser = TestableResumeMediaBrowser(
+ context,
+ callback,
+ component,
+ browserFactory,
+ logger,
+ mediaController
+ )
}
@Test
@@ -282,8 +289,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
callback: Callback,
componentName: ComponentName,
browserFactory: MediaBrowserFactory,
+ logger: ResumeMediaBrowserLogger,
private val fakeController: MediaController
- ) : ResumeMediaBrowser(context, callback, componentName, browserFactory) {
+ ) : ResumeMediaBrowser(context, callback, componentName, browserFactory, logger) {
override fun createMediaController(token: MediaSession.Token): MediaController {
return fakeController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SmartspaceMediaDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SmartspaceMediaDataTest.kt
new file mode 100644
index 000000000000..b5078bc37b84
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SmartspaceMediaDataTest.kt
@@ -0,0 +1,108 @@
+package com.android.systemui.media
+
+import android.app.smartspace.SmartspaceAction
+import android.graphics.drawable.Icon
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SmallTest
+class SmartspaceMediaDataTest : SysuiTestCase() {
+
+ private val icon: Icon = Icon.createWithResource(context, R.drawable.ic_media_play)
+
+ @Test
+ fun getValidRecommendations_onlyReturnsRecsWithIcons() {
+ val withIcon1 = SmartspaceAction.Builder("id", "title").setIcon(icon).build()
+ val withIcon2 = SmartspaceAction.Builder("id", "title").setIcon(icon).build()
+ val withoutIcon1 = SmartspaceAction.Builder("id", "title").setIcon(null).build()
+ val withoutIcon2 = SmartspaceAction.Builder("id", "title").setIcon(null).build()
+ val recommendations = listOf(withIcon1, withoutIcon1, withIcon2, withoutIcon2)
+
+ val data = DEFAULT_DATA.copy(recommendations = recommendations)
+
+ assertThat(data.getValidRecommendations()).isEqualTo(listOf(withIcon1, withIcon2))
+ }
+
+ @Test
+ fun isValid_emptyList_returnsFalse() {
+ val data = DEFAULT_DATA.copy(recommendations = listOf())
+
+ assertThat(data.isValid()).isFalse()
+ }
+
+ @Test
+ fun isValid_tooFewRecs_returnsFalse() {
+ val data = DEFAULT_DATA.copy(
+ recommendations = listOf(
+ SmartspaceAction.Builder("id", "title").setIcon(icon).build()
+ )
+ )
+
+ assertThat(data.isValid()).isFalse()
+ }
+
+ @Test
+ fun isValid_tooFewRecsWithIcons_returnsFalse() {
+ val recommendations = mutableListOf<SmartspaceAction>()
+ // Add one fewer recommendation w/ icon than the number required
+ for (i in 1 until NUM_REQUIRED_RECOMMENDATIONS) {
+ recommendations.add(
+ SmartspaceAction.Builder("id", "title").setIcon(icon).build()
+ )
+ }
+ for (i in 1 until 3) {
+ recommendations.add(
+ SmartspaceAction.Builder("id", "title").setIcon(null).build()
+ )
+ }
+
+ val data = DEFAULT_DATA.copy(recommendations = recommendations)
+
+ assertThat(data.isValid()).isFalse()
+ }
+
+ @Test
+ fun isValid_enoughRecsWithIcons_returnsTrue() {
+ val recommendations = mutableListOf<SmartspaceAction>()
+ // Add the number of required recommendations
+ for (i in 0 until NUM_REQUIRED_RECOMMENDATIONS) {
+ recommendations.add(
+ SmartspaceAction.Builder("id", "title").setIcon(icon).build()
+ )
+ }
+
+ val data = DEFAULT_DATA.copy(recommendations = recommendations)
+
+ assertThat(data.isValid()).isTrue()
+ }
+
+ @Test
+ fun isValid_manyRecsWithIcons_returnsTrue() {
+ val recommendations = mutableListOf<SmartspaceAction>()
+ // Add more than enough recommendations
+ for (i in 0 until NUM_REQUIRED_RECOMMENDATIONS + 3) {
+ recommendations.add(
+ SmartspaceAction.Builder("id", "title").setIcon(icon).build()
+ )
+ }
+
+ val data = DEFAULT_DATA.copy(recommendations = recommendations)
+
+ assertThat(data.isValid()).isTrue()
+ }
+}
+
+private val DEFAULT_DATA = SmartspaceMediaData(
+ targetId = "INVALID",
+ isActive = false,
+ packageName = "INVALID",
+ cardAction = null,
+ recommendations = emptyList(),
+ dismissIntent = null,
+ headphoneConnectionTimeMillis = 0,
+ instanceId = InstanceId.fakeInstanceId(-1)
+)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
index 51088b1d929b..863484b62a00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -62,6 +62,16 @@ public class ColorSchemeTest extends SysuiTestCase {
Assert.assertEquals(rankedSeedColors, List.of(0xffaec00a));
}
+ @Test
+ public void testStyleApplied() {
+ WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xffaec00a),
+ null, null);
+ // Expressive applies hue rotations to the theme color. The input theme color has hue
+ // 117, ensuring the hue changed significantly is a strong signal styles are being applied.
+ ColorScheme colorScheme = new ColorScheme(wallpaperColors, false, Style.EXPRESSIVE);
+ Assert.assertEquals(Cam.fromInt(colorScheme.getAccent1().get(6)).getHue(), 357.46, 0.1);
+ }
+
@Test
public void testFiltersInvalidColors() {
@@ -123,7 +133,7 @@ public class ColorSchemeTest extends SysuiTestCase {
Style.VIBRANT /* style */);
int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
Cam cam = Cam.fromInt(neutralMid);
- Assert.assertTrue(cam.getChroma() <= 8.0);
+ Assert.assertTrue("chroma was " + cam.getChroma(), Math.floor(cam.getChroma()) <= 10.0);
}
@Test
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 2fe7c075bc18..e01ebbdda374 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
@@ -127,6 +127,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
protected Handler mMainHandler;
protected FeatureFlags mFeatureFlags;
+ protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
protected int mSubId;
@@ -220,12 +221,14 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
return null;
}).when(mMockProvisionController).addCallback(any());
+ mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
+ mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
+
mNetworkController = new NetworkControllerImpl(mContext,
mMockCm,
mMockTm,
mTelephonyListenerManager,
mMockWm,
- mMockNsm,
mMockSm,
mConfig,
TestableLooper.get(this).getLooper(),
@@ -238,6 +241,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mMockBd,
mDemoModeController,
mCarrierConfigTracker,
+ mWifiStatusTrackerFactory,
mMainHandler,
mFeatureFlags,
mock(DumpManager.class)
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 ccfa1b31b799..3a0c203f76e0 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
@@ -127,11 +127,13 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
mConfig.show4gForLte = true;
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
mTelephonyListenerManager, mMockWm,
- mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
+ mMockSm, mConfig, Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
- mock(CarrierConfigTracker.class), new Handler(TestableLooper.get(this).getLooper()),
+ mock(CarrierConfigTracker.class),
+ mWifiStatusTrackerFactory,
+ new Handler(TestableLooper.get(this).getLooper()),
mFeatureFlags, mock(DumpManager.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index b84750aa7ea5..ae1b3d1e1f42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -71,7 +71,6 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mMockTm,
mTelephonyListenerManager,
mMockWm,
- mMockNsm,
mMockSm,
mConfig,
TestableLooper.get(this).getLooper(),
@@ -84,6 +83,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mMockBd,
mDemoModeController,
mCarrierConfigTracker,
+ mWifiStatusTrackerFactory,
mMainHandler,
mFeatureFlags,
mock(DumpManager.class)
@@ -105,7 +105,6 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mMockTm,
mTelephonyListenerManager,
mMockWm,
- mMockNsm,
mMockSm,
mConfig,
TestableLooper.get(this).getLooper(),
@@ -118,6 +117,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mMockBd,
mDemoModeController,
mCarrierConfigTracker,
+ mWifiStatusTrackerFactory,
mMainHandler,
mFeatureFlags,
mock(DumpManager.class)
@@ -134,11 +134,12 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
when(mMockTm.isDataCapable()).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
- mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, mConfig,
+ mTelephonyListenerManager, mMockWm, mMockSm, mConfig,
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
mDemoModeController, mock(CarrierConfigTracker.class),
+ mWifiStatusTrackerFactory,
mMainHandler, mFeatureFlags,
mock(DumpManager.class));
setupNetworkController();
@@ -156,11 +157,12 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
when(mMockSm.getCompleteActiveSubscriptionInfoList()).thenReturn(Collections.emptyList());
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
- mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, mConfig,
+ mTelephonyListenerManager, mMockWm, mMockSm, mConfig,
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
mDemoModeController, mock(CarrierConfigTracker.class),
+ mWifiStatusTrackerFactory,
mMainHandler, mFeatureFlags, mock(DumpManager.class));
mNetworkController.registerListeners();
@@ -225,11 +227,12 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
when(mMockTm.isDataCapable()).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
- mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, mConfig,
+ mTelephonyListenerManager, mMockWm, mMockSm, mConfig,
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
mDemoModeController, mock(CarrierConfigTracker.class),
+ mWifiStatusTrackerFactory,
mMainHandler, mFeatureFlags, mock(DumpManager.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index ce58a6c82142..64aa7fb57da8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -128,6 +128,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
private val execution = FakeExecution()
private val fakeParent = FrameLayout(context)
private val fakePrivateLockscreenSettingUri = Uri.Builder().appendPath("test").build()
+ private val fakeNotifOnLockscreenSettingUri = Uri.Builder().appendPath("notif").build()
private val userHandlePrimary: UserHandle = UserHandle(0)
private val userHandleManaged: UserHandle = UserHandle(2)
@@ -149,6 +150,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
`when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
.thenReturn(fakePrivateLockscreenSettingUri)
+ `when`(secureSettings.getUriFor(NOTIF_ON_LOCKSCREEN_SETTING))
+ .thenReturn(fakeNotifOnLockscreenSettingUri)
`when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession)
`when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView())
`when`(userTracker.userProfiles).thenReturn(userList)
@@ -160,6 +163,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
setAllowPrivateNotifications(userHandlePrimary, true)
setAllowPrivateNotifications(userHandleManaged, true)
setAllowPrivateNotifications(userHandleSecondary, true)
+ setShowNotifications(userHandlePrimary, true)
controller = LockscreenSmartspaceController(
context,
@@ -341,6 +345,26 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
@Test
+ fun testAllTargetsAreFilteredExceptWeatherWhenNotificationsAreDisabled() {
+ // GIVEN the active user doesn't allow any notifications on lockscreen
+ setShowNotifications(userHandlePrimary, false)
+ connectSession()
+
+ // WHEN we receive a list of targets
+ val targets = listOf(
+ makeTarget(1, userHandlePrimary, isSensitive = true),
+ makeTarget(2, userHandlePrimary),
+ makeTarget(3, userHandleManaged),
+ makeTarget(4, userHandlePrimary, featureType = SmartspaceTarget.FEATURE_WEATHER)
+ )
+
+ sessionListener.onTargetsAvailable(targets)
+
+ // THEN all non-sensitive content is still shown
+ verify(plugin).onTargetsAvailable(eq(listOf(targets[3])))
+ }
+
+ @Test
fun testSensitiveTargetsAreFilteredOutForAppropriateUsers() {
// GIVEN the active and managed users don't allow sensitive lockscreen content
setAllowPrivateNotifications(userHandlePrimary, false)
@@ -391,6 +415,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
fun testRecognizeSwitchToSecondaryUser() {
// GIVEN an inactive secondary user that doesn't allow sensitive content
setAllowPrivateNotifications(userHandleSecondary, false)
+ setShowNotifications(userHandleSecondary, true)
connectSession()
// WHEN the secondary user becomes the active user
@@ -518,13 +543,15 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
fun makeTarget(
id: Int,
userHandle: UserHandle,
- isSensitive: Boolean = false
+ isSensitive: Boolean = false,
+ featureType: Int = 0
): SmartspaceTarget {
return SmartspaceTarget.Builder(
"target$id",
ComponentName("testpackage", "testclass$id"),
userHandle)
.setSensitive(isSensitive)
+ .setFeatureType(featureType)
.build()
}
@@ -536,6 +563,14 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
).thenReturn(if (value) 1 else 0)
}
+ private fun setShowNotifications(user: UserHandle, value: Boolean) {
+ `when`(secureSettings.getIntForUser(
+ eq(NOTIF_ON_LOCKSCREEN_SETTING),
+ anyInt(),
+ eq(user.identifier))
+ ).thenReturn(if (value) 1 else 0)
+ }
+
private fun createSmartspaceView(): SmartspaceView {
return spy(object : View(context), SmartspaceView {
override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
@@ -574,3 +609,5 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
private const val PRIVATE_LOCKSCREEN_SETTING =
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
+private const val NOTIF_ON_LOCKSCREEN_SETTING =
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 7638452eaf90..9ea1813377a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -1439,14 +1439,16 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// WHEN a new child is added and the old one gets filtered while group changes are disabled.
mStabilityManager.setAllowGroupChanges(false);
+ mStabilityManager.setAllowGroupPruning(false);
mFinalizeFilter.mIndicesToFilter.add(1);
addGroupChild(2, PACKAGE_1, GROUP_1);
dispatchBuild();
// THEN the new child should be shown without a group
+ // (Note that this is the same as the expected result if there were no stability rules.)
verifyBuiltList(
- notif(2) // previously promoted child
+ notif(2) // new child
);
}
@@ -1489,25 +1491,6 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Test
- public void testFinalizeFilteredChildrenPromotesSummary() {
- // GIVEN a group with only one child was already drawn
- addGroupSummary(0, PACKAGE_1, GROUP_1);
- addGroupChild(1, PACKAGE_1, GROUP_1);
- addGroupChild(2, PACKAGE_1, GROUP_1);
-
- // WHEN the parent is filtered out at the finalize step
- mFinalizeFilter.mIndicesToFilter.add(1);
- mFinalizeFilter.mIndicesToFilter.add(2);
-
- dispatchBuild();
-
- // THEN the children should be promoted to the top level
- verifyBuiltList(
- notif(0)
- );
- }
-
- @Test
public void testFinalizeFilteredChildPromotesSibling() {
// GIVEN a group with only one child was already drawn
addGroupSummary(0, PACKAGE_1, GROUP_1);
@@ -2269,6 +2252,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
private static class TestableStabilityManager extends NotifStabilityManager {
boolean mAllowPipelineRun = true;
boolean mAllowGroupChanges = true;
+ boolean mAllowGroupPruning = true;
boolean mAllowSectionChanges = true;
boolean mAllowEntryReodering = true;
@@ -2281,6 +2265,11 @@ public class ShadeListBuilderTest extends SysuiTestCase {
return this;
}
+ TestableStabilityManager setAllowGroupPruning(boolean allowGroupPruning) {
+ mAllowGroupPruning = allowGroupPruning;
+ return this;
+ }
+
TestableStabilityManager setAllowSectionChanges(boolean allowSectionChanges) {
mAllowSectionChanges = allowSectionChanges;
return this;
@@ -2311,6 +2300,11 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Override
+ public boolean isGroupPruneAllowed(@NonNull GroupEntry entry) {
+ return mAllowGroupPruning;
+ }
+
+ @Override
public boolean isSectionChangeAllowed(@NonNull NotificationEntry entry) {
return mAllowSectionChanges;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 6f8e5d8e514e..f3aa20ba4527 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -35,6 +35,8 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -83,6 +85,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
private NotifPanelEvents.Listener mNotifPanelEventsCallback;
private NotifStabilityManager mNotifStabilityManager;
private NotificationEntry mEntry;
+ private GroupEntry mGroupEntry;
@Before
public void setUp() {
@@ -117,6 +120,10 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
.setPkg("testPkg1")
.build();
+ mGroupEntry = new GroupEntryBuilder()
+ .setSummary(mEntry)
+ .build();
+
when(mHeadsUpManager.isAlerting(mEntry.getKey())).thenReturn(false);
// Whenever we invalidate, the pipeline runs again, so we invalidate the state
@@ -135,6 +142,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// THEN group changes are allowed
assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+ assertTrue(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
// THEN section changes are allowed
assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
@@ -149,6 +157,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// THEN group changes are allowed
assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+ assertTrue(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
// THEN section changes are allowed
assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
@@ -163,6 +172,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// THEN group changes are NOT allowed
assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+ assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
// THEN section changes are NOT allowed
assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
@@ -176,6 +186,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// THEN group changes are NOT allowed
assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+ assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
// THEN section changes are NOT allowed
assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
@@ -190,6 +201,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// THEN group changes are NOT allowed
assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+ assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
// THEN section changes are NOT allowed
assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
@@ -208,6 +220,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// THEN group changes aren't allowed
assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+ assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
// THEN section changes are allowed for this notification but not other notifications
assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
@@ -321,6 +334,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
setPanelExpanded(true);
assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+ assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
// WHEN the panel isn't expanded anymore
setPanelExpanded(false);
@@ -422,6 +436,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
setPanelExpanded(true);
setPulsing(true);
assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+ assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
// GIVEN mEntry is a HUN
@@ -431,6 +446,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
+ // BUT pruning the group for which this is the summary would still NOT be allowed.
+ assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
}
private void setActivityLaunching(boolean activityLaunching) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 6409967aca9b..94a93ad6cf33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -74,6 +74,7 @@ import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -135,6 +136,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private StackStateLogger mStackLogger;
@Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
+ @Mock private ShadeTransitionController mShadeTransitionController;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -179,6 +181,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mNotifCollection,
mEntryManager,
mLockscreenShadeTransitionController,
+ mShadeTransitionController,
mIStatusBarService,
mUiEventLogger,
mLayoutInflater,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index c3658ba11b2d..663490ebfde0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -49,7 +49,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
@Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
@Mock private lateinit var stackLayout: NotificationStackScrollLayout
- private val testableResources = mContext.getOrCreateTestableResources()
+ private val testableResources = mContext.orCreateTestableResources
private lateinit var sizeCalculator: NotificationStackSizeCalculator
@@ -121,17 +121,16 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
}
@Test
- fun computeHeight_returnsAtMostSpaceAvailable_withGapBeforeShelf() {
+ fun computeHeight_gapBeforeShelf_returnsSpaceUsed() {
+ // Each row in separate section.
setGapHeight(gapHeight)
- val shelfHeight = shelfHeight
- val availableSpace =
+ val spaceUsed =
listOf(
- rowHeight + dividerHeight,
- gapHeight + rowHeight + dividerHeight,
- gapHeight + dividerHeight + shelfHeight)
+ dividerHeight + rowHeight,
+ dividerHeight + gapHeight + rowHeight,
+ dividerHeight + gapHeight + shelfHeight)
.sum()
-
- // All rows in separate sections (default setup).
+ val availableSpace = spaceUsed + 1;
val rows =
listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight))
@@ -139,23 +138,29 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
assertThat(maxNotifications).isEqualTo(2)
val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
- assertThat(height).isAtMost(availableSpace)
+ assertThat(height).isEqualTo(spaceUsed)
}
@Test
- fun computeHeight_noGapBeforeShelf_returnsAtMostSpaceAvailable() {
+ fun computeHeight_noGapBeforeShelf_returnsSpaceUsed() {
// Both rows are in the same section.
setGapHeight(0f)
+
val rowHeight = rowHeight
val shelfHeight = shelfHeight
- val availableSpace = listOf(rowHeight + dividerHeight, dividerHeight + shelfHeight).sum()
+ val spaceUsed =
+ listOf(
+ dividerHeight + rowHeight,
+ dividerHeight + shelfHeight)
+ .sum()
+ val availableSpace = spaceUsed + 1
val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight))
val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight)
assertThat(maxNotifications).isEqualTo(1)
val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
- assertThat(height).isAtMost(availableSpace)
+ assertThat(height).isEqualTo(spaceUsed)
}
@Test
@@ -190,7 +195,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
val space = sizeCalculator.spaceNeeded(expandableView, visibleIndex = 0,
previousView = null, stack = stackLayout, onLockscreen = true)
- assertThat(space).isEqualTo(5)
+ assertThat(space).isEqualTo(5 + dividerHeight)
}
@Test
@@ -204,7 +209,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
val space = sizeCalculator.spaceNeeded(expandableView, visibleIndex = 0,
previousView = null, stack = stackLayout, onLockscreen = false)
- assertThat(space).isEqualTo(10)
+ assertThat(space).isEqualTo(10 + dividerHeight)
}
private fun computeMaxKeyguardNotifications(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 84edabdaa302..d364505445f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -178,12 +178,12 @@ import dagger.Lazy;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
-public class CentralSurfacesTest extends SysuiTestCase {
+public class CentralSurfacesImplTest extends SysuiTestCase {
private static final int FOLD_STATE_FOLDED = 0;
private static final int FOLD_STATE_UNFOLDED = 1;
- private CentralSurfaces mCentralSurfaces;
+ private CentralSurfacesImpl mCentralSurfaces;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
private TestableNotificationInterruptStateProviderImpl mNotificationInterruptStateProvider;
@@ -383,7 +383,7 @@ public class CentralSurfacesTest extends SysuiTestCase {
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
- mCentralSurfaces = new CentralSurfaces(
+ mCentralSurfaces = new CentralSurfacesImpl(
mContext,
mNotificationsController,
mock(FragmentService.class),
@@ -479,7 +479,7 @@ public class CentralSurfacesTest extends SysuiTestCase {
mDreamOverlayStateController,
mWiredChargingRippleController);
when(mKeyguardViewMediator.registerCentralSurfaces(
- any(CentralSurfaces.class),
+ any(CentralSurfacesImpl.class),
any(NotificationPanelViewController.class),
any(PanelExpansionStateManager.class),
any(BiometricUnlockController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
new file mode 100644
index 000000000000..2ff6dd43e84e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -0,0 +1,168 @@
+/*
+ * 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.phone
+
+import android.service.notification.StatusBarNotification
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.StatusBarIconView
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when` as whenever
+
+/**
+ * Tests for {@link NotificationIconContainer}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationIconContainerTest : SysuiTestCase() {
+
+ private val iconContainer = NotificationIconContainer(context, /* attrs= */ null)
+
+ @Test
+ fun calculateWidthFor_zeroIcons_widthIsZero() {
+ assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 0f),
+ /* actual= */ 0f)
+ }
+
+ @Test
+ fun calculateWidthFor_oneIcon_widthForOneIcon() {
+ iconContainer.setActualPaddingStart(10f)
+ iconContainer.setActualPaddingEnd(10f)
+ iconContainer.setIconSize(10);
+
+ assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f),
+ /* actual= */ 30f)
+ }
+
+ @Test
+ fun calculateWidthFor_fourIcons_widthForFourIcons() {
+ iconContainer.setActualPaddingStart(10f)
+ iconContainer.setActualPaddingEnd(10f)
+ iconContainer.setIconSize(10);
+
+ assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f),
+ /* actual= */ 60f)
+ }
+
+ @Test
+ fun calculateWidthFor_fiveIcons_widthForFourIcons() {
+ iconContainer.setActualPaddingStart(10f)
+ iconContainer.setActualPaddingEnd(10f)
+ iconContainer.setIconSize(10);
+ assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f),
+ /* actual= */ 60f)
+ }
+
+ @Test
+ fun calculateIconXTranslations_shortShelfOneIcon_atCorrectXWithoutOverflowDot() {
+ iconContainer.setActualPaddingStart(10f)
+ iconContainer.setActualPaddingEnd(10f)
+ iconContainer.setIconSize(10);
+
+ val icon = mockStatusBarIcon()
+ iconContainer.addView(icon)
+ assertEquals(1, iconContainer.childCount)
+
+ val iconState = iconContainer.getIconState(icon)
+ iconState.iconAppearAmount = 1f
+
+ val width = iconContainer.calculateWidthFor(/* numIcons= */ 1f)
+ iconContainer.setActualLayoutWidth(width.toInt())
+
+ iconContainer.calculateIconXTranslations()
+ assertEquals(10f, iconState.xTranslation)
+ assertFalse(iconContainer.hasOverflow())
+ }
+
+ @Test
+ fun calculateIconXTranslations_shortShelfFourIcons_atCorrectXWithoutOverflowDot() {
+ iconContainer.setActualPaddingStart(10f)
+ iconContainer.setActualPaddingEnd(10f)
+ iconContainer.setIconSize(10);
+
+ val iconOne = mockStatusBarIcon()
+ val iconTwo = mockStatusBarIcon()
+ val iconThree = mockStatusBarIcon()
+ val iconFour = mockStatusBarIcon()
+
+ iconContainer.addView(iconOne)
+ iconContainer.addView(iconTwo)
+ iconContainer.addView(iconThree)
+ iconContainer.addView(iconFour)
+ assertEquals(4, iconContainer.childCount)
+
+ val width = iconContainer.calculateWidthFor(/* numIcons= */ 4f)
+ iconContainer.setActualLayoutWidth(width.toInt())
+
+ iconContainer.calculateIconXTranslations()
+ assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation)
+ assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation)
+ assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation)
+ assertEquals(40f, iconContainer.getIconState(iconFour).xTranslation)
+
+ assertFalse(iconContainer.hasOverflow())
+ }
+
+ @Test
+ fun calculateIconXTranslations_shortShelfFiveIcons_atCorrectXWithOverflowDot() {
+ iconContainer.setActualPaddingStart(10f)
+ iconContainer.setActualPaddingEnd(10f)
+ iconContainer.setIconSize(10);
+
+ val iconOne = mockStatusBarIcon()
+ val iconTwo = mockStatusBarIcon()
+ val iconThree = mockStatusBarIcon()
+ val iconFour = mockStatusBarIcon()
+ val iconFive = mockStatusBarIcon()
+
+ iconContainer.addView(iconOne)
+ iconContainer.addView(iconTwo)
+ iconContainer.addView(iconThree)
+ iconContainer.addView(iconFour)
+ iconContainer.addView(iconFive)
+ assertEquals(5, iconContainer.childCount)
+
+ val width = iconContainer.calculateWidthFor(/* numIcons= */ 5f)
+ iconContainer.setActualLayoutWidth(width.toInt())
+
+ iconContainer.calculateIconXTranslations()
+ assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation)
+ assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation)
+ assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation)
+ assertTrue(iconContainer.hasOverflow())
+ }
+
+ private fun mockStatusBarIcon() : StatusBarIconView {
+ val iconView = mock(StatusBarIconView::class.java)
+ whenever(iconView.width).thenReturn(10)
+
+ val icon = mock(android.graphics.drawable.Icon::class.java)
+ whenever(iconView.sourceIcon).thenReturn(icon)
+
+ val sbn = mock(StatusBarNotification::class.java)
+ whenever(sbn.groupKey).thenReturn("groupKey")
+ whenever(iconView.notification).thenReturn(sbn)
+ return iconView
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index f51c428be28a..6997b3e4bd70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -128,6 +128,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
+import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -337,6 +338,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Mock
+ private ShadeTransitionController mShadeTransitionController;
+ @Mock
private QS mQs;
@Mock
private View mQsHeader;
@@ -529,7 +532,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mNotificationListContainer,
mPanelEventsEmitter,
mNotificationStackSizeCalculator,
- mUnlockedScreenOffAnimationController);
+ mUnlockedScreenOffAnimationController,
+ mShadeTransitionController);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
() -> {},
@@ -591,6 +595,27 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
+ public void getLockscreenSpaceForNotifications_includesOverlapWithLockIcon() {
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
+ .thenReturn(0);
+ mNotificationPanelViewController.setAmbientIndicationTop(
+ /* ambientIndicationTop= */ 0, /* ambientTextVisible */ false);
+
+ // Use lock icon padding (100 - 80 - 5 = 15) as bottom padding
+ when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(100);
+ when(mLockIconViewController.getTop()).thenReturn(80f);
+ when(mResources.getDimensionPixelSize(R.dimen.shelf_and_lock_icon_overlap)).thenReturn(5);
+
+ // Available space (100 - 10 - 15 = 75)
+ when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(100);
+ when(mNotificationStackScrollLayoutController.getTopPadding()).thenReturn(10);
+ mNotificationPanelViewController.updateResources();
+
+ assertThat(mNotificationPanelViewController.getSpaceForLockscreenNotifications())
+ .isEqualTo(75);
+ }
+
+ @Test
public void testSetPanelScrimMinFraction() {
mNotificationPanelViewController.setPanelScrimMinFraction(0.5f);
verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt
new file mode 100644
index 000000000000..39d33e86925e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt
@@ -0,0 +1,118 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ShadeTransitionControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var npvc: NotificationPanelViewController
+ @Mock private lateinit var nsslController: NotificationStackScrollLayoutController
+ @Mock private lateinit var qs: QS
+ @Mock private lateinit var noOpOverScroller: NoOpOverScroller
+ @Mock private lateinit var splitShadeOverScroller: SplitShadeOverScroller
+
+ private lateinit var controller: ShadeTransitionController
+
+ private val configurationController = FakeConfigurationController()
+ private val panelExpansionStateManager = PanelExpansionStateManager()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ controller =
+ ShadeTransitionController(
+ configurationController,
+ panelExpansionStateManager,
+ context,
+ splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
+ noOpOverScroller)
+
+ // Resetting as they are notified upon initialization.
+ reset(noOpOverScroller, splitShadeOverScroller)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_forwardsToSplitShadeOverScroller() {
+ initLateProperties()
+ enableSplitShade()
+
+ startPanelExpansion()
+
+ verify(splitShadeOverScroller).onPanelStateChanged(STATE_OPENING)
+ verify(splitShadeOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
+ verifyZeroInteractions(noOpOverScroller)
+ }
+
+ @Test
+ fun onPanelStateChanged_inSplitShade_propertiesNotInitialized_forwardsToNoOpOverScroller() {
+ enableSplitShade()
+
+ startPanelExpansion()
+
+ verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
+ verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
+ verifyZeroInteractions(splitShadeOverScroller)
+ }
+
+ @Test
+ fun onPanelStateChanged_notInSplitShade_forwardsToNoOpOverScroller() {
+ initLateProperties()
+ disableSplitShade()
+
+ startPanelExpansion()
+
+ verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
+ verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
+ verifyZeroInteractions(splitShadeOverScroller)
+ }
+
+ private fun initLateProperties() {
+ controller.qs = qs
+ controller.notificationStackScrollLayoutController = nsslController
+ controller.notificationPanelViewController = npvc
+ }
+
+ private fun disableSplitShade() {
+ setSplitShadeEnabled(false)
+ }
+
+ private fun enableSplitShade() {
+ setSplitShadeEnabled(true)
+ }
+
+ private fun setSplitShadeEnabled(enabled: Boolean) {
+ overrideResource(R.bool.config_use_split_notification_shade, enabled)
+ configurationController.notifyConfigurationChanged()
+ }
+
+ private fun startPanelExpansion() {
+ panelExpansionStateManager.onPanelExpansionChanged(
+ fraction = 0.5f,
+ expanded = true,
+ tracking = true,
+ dragDownPxAmount = DEFAULT_DRAG_DOWN_AMOUNT)
+ }
+
+ companion object {
+ private const val DEFAULT_DRAG_DOWN_AMOUNT = 123f
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt
new file mode 100644
index 000000000000..219737d1dfb4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt
@@ -0,0 +1,107 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import org.mockito.Mockito.`when` as whenever
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.panelstate.STATE_CLOSED
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPEN
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class SplitShadeOverScrollerTest : SysuiTestCase() {
+
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var scrimController: ScrimController
+ @Mock private lateinit var qs: QS
+ @Mock private lateinit var nsslController: NotificationStackScrollLayoutController
+
+ private val configurationController = FakeConfigurationController()
+ private lateinit var overScroller: SplitShadeOverScroller
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(nsslController.height).thenReturn(1000)
+ overScroller =
+ SplitShadeOverScroller(
+ configurationController, dumpManager, context, scrimController, qs, nsslController)
+ }
+
+ @Test
+ fun onDragDownAmountChanged_panelOpening_overScrolls_basedOnHeightAndMaxAmount() {
+ val maxOverScrollAmount = 50
+ val dragDownAmount = 100f
+ overrideResource(R.dimen.shade_max_over_scroll_amount, maxOverScrollAmount)
+ configurationController.notifyConfigurationChanged()
+
+ overScroller.onPanelStateChanged(STATE_OPENING)
+ overScroller.onDragDownAmountChanged(dragDownAmount)
+
+ val expectedOverScrollAmount =
+ (dragDownAmount / nsslController.height * maxOverScrollAmount).toInt()
+ verify(qs).setOverScrollAmount(expectedOverScrollAmount)
+ verify(nsslController).setOverScrollAmount(expectedOverScrollAmount)
+ verify(scrimController).setNotificationsOverScrollAmount(expectedOverScrollAmount)
+ }
+
+ @Test
+ fun onDragDownAmountChanged_panelClosed_doesNotOverScroll() {
+ overScroller.onPanelStateChanged(STATE_CLOSED)
+ overScroller.onDragDownAmountChanged(100f)
+
+ verifyZeroInteractions(qs, scrimController, nsslController)
+ }
+
+ @Test
+ fun onDragDownAmountChanged_panelOpen_doesNotOverScroll() {
+ overScroller.onPanelStateChanged(STATE_OPEN)
+ overScroller.onDragDownAmountChanged(100f)
+
+ verifyZeroInteractions(qs, scrimController, nsslController)
+ }
+
+ @Test
+ fun onPanelStateChanged_opening_thenOpen_releasesOverScroll() {
+ overScroller.onPanelStateChanged(STATE_OPENING)
+ overScroller.onDragDownAmountChanged(100f)
+
+ overScroller.onPanelStateChanged(STATE_OPEN)
+ overScroller.finishAnimations()
+
+ verify(qs, atLeastOnce()).setOverScrollAmount(0)
+ verify(scrimController, atLeastOnce()).setNotificationsOverScrollAmount(0)
+ verify(nsslController, atLeastOnce()).setOverScrollAmount(0)
+ }
+
+ @Test
+ fun onPanelStateChanged_opening_thenClosed_releasesOverScroll() {
+ overScroller.onPanelStateChanged(STATE_OPENING)
+ overScroller.onDragDownAmountChanged(100f)
+
+ overScroller.onPanelStateChanged(STATE_CLOSED)
+ overScroller.finishAnimations()
+
+ verify(qs, atLeastOnce()).setOverScrollAmount(0)
+ verify(scrimController, atLeastOnce()).setNotificationsOverScrollAmount(0)
+ verify(nsslController, atLeastOnce()).setOverScrollAmount(0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 3dfc94bcd5b6..c625dc7d4b50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -729,6 +729,18 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
USER_SYSTEM);
+ reset(mResources);
+ when(mResources.getColor(eq(android.R.color.system_accent1_500), any()))
+ .thenReturn(mThemeOverlayController.mColorScheme.getAccent1().get(6));
+ when(mResources.getColor(eq(android.R.color.system_accent2_500), any()))
+ .thenReturn(mThemeOverlayController.mColorScheme.getAccent2().get(6));
+ when(mResources.getColor(eq(android.R.color.system_accent3_500), any()))
+ .thenReturn(mThemeOverlayController.mColorScheme.getAccent3().get(6));
+ when(mResources.getColor(eq(android.R.color.system_neutral1_500), any()))
+ .thenReturn(mThemeOverlayController.mColorScheme.getNeutral1().get(6));
+ when(mResources.getColor(eq(android.R.color.system_neutral2_500), any()))
+ .thenReturn(mThemeOverlayController.mColorScheme.getNeutral2().get(6));
+
// Defers event because we already have initial colors.
verify(mThemeOverlayApplier, never())
.applyCurrentUserOverlays(any(), any(), anyInt(), any());
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 5acae4859ee8..8fe57e18ea37 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -4414,7 +4414,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// a given package. Keep track of what we've done so far here; the list is
// cleared at the start of every system restore pass, but preserved through
// any install-time restore operations.
- private final HashSet<String> mPrunedApps = new HashSet<>();
+ private final SparseArray<Set<String>> mPrunedAppsPerUser = new SparseArray<>();
private final HashMap<Provider, ArrayList<RestoreUpdateRecord>> mUpdatesByProvider =
new HashMap<>();
@@ -4537,7 +4537,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// We're starting a new "system" restore operation, so any widget restore
// state that we see from here on is intended to replace the current
// widget configuration of any/all of the affected apps.
- mPrunedApps.clear();
+ getPrunedAppsLocked(userId).clear();
mUpdatesByProvider.clear();
mUpdatesByHost.clear();
}
@@ -4934,8 +4934,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// instances that are hosted by that app, and (b) all instances in other hosts
// for which 'pkg' is the provider. We assume that we'll be restoring all of
// these hosts & providers, so will be reconstructing a correct live state.
+ @GuardedBy("mLock")
private void pruneWidgetStateLocked(String pkg, int userId) {
- if (!mPrunedApps.contains(pkg)) {
+ final Set<String> prunedApps = getPrunedAppsLocked(userId);
+ if (!prunedApps.contains(pkg)) {
if (DEBUG) {
Slog.i(TAG, "pruning widget state for restoring package " + pkg);
}
@@ -4958,7 +4960,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
removeWidgetLocked(widget);
}
}
- mPrunedApps.add(pkg);
+ prunedApps.add(pkg);
} else {
if (DEBUG) {
Slog.i(TAG, "already pruned " + pkg + ", continuing normally");
@@ -4966,6 +4968,15 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
+ @GuardedBy("mLock")
+ @NonNull
+ private Set<String> getPrunedAppsLocked(int userId) {
+ if (!mPrunedAppsPerUser.contains(userId)) {
+ mPrunedAppsPerUser.set(userId, new ArraySet<>());
+ }
+ return mPrunedAppsPerUser.get(userId);
+ }
+
private boolean isProviderAndHostInUser(Widget widget, int userId) {
// Backup only widgets hosted or provided by the owner profile.
return widget.host.getUserId() == userId && (widget.provider == null
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a67b858d7b63..1033aea4b09f 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2803,6 +2803,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ " callback.asBinder=" + callback.asBinder());
}
+ // In case this is triggered from the caller who has handled multiple SIM config change
+ // firstly, we need to update the status (mNumPhone and mCarrierPrivilegeStates) firstly.
+ // This is almost a no-op if there is no multiple SIM config change in advance.
+ onMultiSimConfigChanged();
+
synchronized (mRecords) {
if (!validatePhoneId(phoneId)) {
throw new IllegalArgumentException("Invalid slot index: " + phoneId);
@@ -2865,6 +2870,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ ", <packages=" + pii(privilegedPackageNames)
+ ", uids=" + Arrays.toString(privilegedUids) + ">");
}
+
+ // In case this is triggered from the caller who has handled multiple SIM config change
+ // firstly, we need to update the status (mNumPhone and mCarrierPrivilegeStates) firstly.
+ // This is almost a no-op if there is no multiple SIM config change in advance.
+ onMultiSimConfigChanged();
+
synchronized (mRecords) {
if (!validatePhoneId(phoneId)) {
throw new IllegalArgumentException("Invalid slot index: " + phoneId);
@@ -2900,6 +2911,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ ", package=" + pii(packageName) + ", uid=" + uid);
}
+ // In case this is triggered from the caller who has handled multiple SIM config change
+ // firstly, we need to update the status (mNumPhone and mCarrierServiceStates) firstly.
+ // This is almost a no-op if there is no multiple SIM config change in advance.
+ onMultiSimConfigChanged();
+
synchronized (mRecords) {
mCarrierServiceStates.set(
phoneId, new Pair<>(packageName, uid));
@@ -3365,7 +3381,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
private boolean validatePhoneId(int phoneId) {
- boolean valid = (phoneId >= 0) && (phoneId < mNumPhones);
+ // Call getActiveModemCount to get the latest value instead of depending on mNumPhone
+ boolean valid = (phoneId >= 0) && (phoneId < getTelephonyManager().getActiveModemCount());
if (VDBG) log("validatePhoneId: " + valid);
return valid;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 730907c7d049..235dbeeb9e27 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -21,7 +21,6 @@ import static android.Manifest.permission.REQUEST_COMPANION_START_FOREGROUND_SER
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
-import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -6527,10 +6526,14 @@ public final class ActiveServices {
for (int con = 0; con < crs.size(); con++) {
final ConnectionRecord cr = crs.get(con);
final ProcessRecord clientPr = cr.binding.client;
- // Persistent process does not propagate BG-FGS-start capability
- // down to service over binding.
- if (clientPr.mState.getCurProcState()
- <= PROCESS_STATE_PERSISTENT_UI) {
+ // If a binding is from a persistent process, we don't automatically
+ // always allow the bindee to allow FGS BG starts. In this case,
+ // the binder will have to explicitly make sure the bindee's
+ // procstate will be BFGS or above. Otherwise, for example, even if
+ // the system server binds to an app with BIND_NOT_FOREGROUND,
+ // the binder would have to be able to start FGS, which is not what
+ // we want. (e.g. job services shouldn't be allowed BG-FGS.)
+ if (clientPr.isPersistent()) {
continue;
}
final int clientPid = clientPr.mPid;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c5a4ca4dc0e5..91f6eeb875f6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1024,30 +1024,9 @@ public class ActivityManagerService extends IActivityManager.Stub
private final ActivityMetricsLaunchObserver mActivityLaunchObserver =
new ActivityMetricsLaunchObserver() {
@Override
- public void onActivityLaunched(byte[] activity, int temperature) {
+ public void onActivityLaunched(long id, ComponentName name, int temperature) {
mAppProfiler.onActivityLaunched();
}
-
- // The other observer methods are unused
- @Override
- public void onIntentStarted(Intent intent, long timestampNs) {
- }
-
- @Override
- public void onIntentFailed() {
- }
-
- @Override
- public void onActivityLaunchCancelled(byte[] abortingActivity) {
- }
-
- @Override
- public void onActivityLaunchFinished(byte[] finalActivity, long timestampNs) {
- }
-
- @Override
- public void onReportFullyDrawn(byte[] finalActivity, long timestampNs) {
- }
};
private volatile boolean mBinderTransactionTrackingEnabled = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 71ae92aecbcf..3e5786eaa333 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1200,8 +1200,19 @@ final class ActivityManagerShellCommand extends ShellCommand {
} catch (NumberFormatException e) {
packageName = arg;
}
- mInterface.crashApplicationWithType(-1, pid, packageName, userId, "shell-induced crash",
- false, CrashedByAdbException.TYPE_ID);
+
+ int[] userIds = (userId == UserHandle.USER_ALL) ? mInternal.mUserController.getUserIds()
+ : new int[]{userId};
+ for (int id : userIds) {
+ if (mInternal.mUserController.hasUserRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, id)) {
+ getOutPrintWriter().println(
+ "Shell does not have permission to crash packages for user " + id);
+ continue;
+ }
+ mInterface.crashApplicationWithType(-1, pid, packageName, id, "shell-induced crash",
+ false, CrashedByAdbException.TYPE_ID);
+ }
return 0;
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 36c40a1b97dc..ed492bc7344c 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -33,6 +33,7 @@ import android.app.ActivityOptions;
import android.app.AnrController;
import android.app.ApplicationErrorReport;
import android.app.ApplicationExitInfo;
+import android.app.RemoteServiceException.CrashedByAdbException;
import android.app.usage.UsageStatsManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -523,6 +524,16 @@ class AppErrors {
return;
}
+ if (exceptionTypeId == CrashedByAdbException.TYPE_ID) {
+ String[] packages = proc.getPackageList();
+ for (int i = 0; i < packages.length; i++) {
+ if (mService.mPackageManagerInt.isPackageStateProtected(packages[i], proc.userId)) {
+ Slog.w(TAG, "crashApplication: Can not crash protected package " + packages[i]);
+ return;
+ }
+ }
+ }
+
proc.scheduleCrashLocked(message, exceptionTypeId, extras);
if (force) {
// If the app is responsive, the scheduled crash will happen as expected
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 672736df88f3..18fb6a480f29 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -64,9 +64,10 @@ public class DropboxRateLimiter {
}
if (now - errRecord.getStartTime() > RATE_LIMIT_BUFFER_DURATION) {
+ final int errCount = recentlyDroppedCount(errRecord);
errRecord.setStartTime(now);
errRecord.setCount(1);
- return new RateLimitResult(false, recentlyDroppedCount(errRecord));
+ return new RateLimitResult(false, errCount);
}
errRecord.incrementCount();
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 19a93f30937f..63609f77dc75 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -46,6 +46,7 @@ import java.util.Date;
import java.util.Deque;
import java.util.List;
import java.util.Locale;
+import java.util.function.Consumer;
/**
* A scheduler for biometric HAL operations. Maintains a queue of {@link BaseClientMonitor}
@@ -457,22 +458,33 @@ public class BiometricScheduler {
}
/**
+ * Get current operation <code>BaseClientMonitor</code>
+ * @deprecated TODO: b/229994966, encapsulate client monitors
* @return the current operation
*/
+ @Deprecated
+ @Nullable
public BaseClientMonitor getCurrentClient() {
return mCurrentOperation != null ? mCurrentOperation.getClientMonitor() : null;
}
- /** The current operation if the requestId is set and matches. */
+ /**
+ * The current operation if the requestId is set and matches.
+ * @deprecated TODO: b/229994966, encapsulate client monitors
+ */
@Deprecated
@Nullable
- public BaseClientMonitor getCurrentClientIfMatches(long requestId) {
- if (mCurrentOperation != null) {
- if (mCurrentOperation.isMatchingRequestId(requestId)) {
- return mCurrentOperation.getClientMonitor();
+ public void getCurrentClientIfMatches(long requestId,
+ @NonNull Consumer<BaseClientMonitor> clientMonitorConsumer) {
+ mHandler.post(() -> {
+ if (mCurrentOperation != null) {
+ if (mCurrentOperation.isMatchingRequestId(requestId)) {
+ clientMonitorConsumer.accept(mCurrentOperation.getClientMonitor());
+ return;
+ }
}
- }
- return null;
+ clientMonitorConsumer.accept(null);
+ });
}
public int getCurrentPendingCount() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 7d5b77c2d711..998a8e1e9f90 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -582,35 +582,35 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@Override
public void onPointerDown(long requestId, int sensorId, int x, int y,
float minor, float major) {
- final BaseClientMonitor client =
- mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId);
- if (!(client instanceof Udfps)) {
- Slog.e(getTag(), "onPointerDown received during client: " + client);
- return;
- }
- ((Udfps) client).onPointerDown(x, y, minor, major);
+ mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId, (client) -> {
+ if (!(client instanceof Udfps)) {
+ Slog.e(getTag(), "onPointerDown received during client: " + client);
+ return;
+ }
+ ((Udfps) client).onPointerDown(x, y, minor, major);
+ });
}
@Override
public void onPointerUp(long requestId, int sensorId) {
- final BaseClientMonitor client =
- mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId);
- if (!(client instanceof Udfps)) {
- Slog.e(getTag(), "onPointerUp received during client: " + client);
- return;
- }
- ((Udfps) client).onPointerUp();
+ mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId, (client) -> {
+ if (!(client instanceof Udfps)) {
+ Slog.e(getTag(), "onPointerUp received during client: " + client);
+ return;
+ }
+ ((Udfps) client).onPointerUp();
+ });
}
@Override
public void onUiReady(long requestId, int sensorId) {
- final BaseClientMonitor client =
- mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId);
- if (!(client instanceof Udfps)) {
- Slog.e(getTag(), "onUiReady received during client: " + client);
- return;
- }
- ((Udfps) client).onUiReady();
+ mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId, (client) -> {
+ if (!(client instanceof Udfps)) {
+ Slog.e(getTag(), "onUiReady received during client: " + client);
+ return;
+ }
+ ((Udfps) client).onUiReady();
+ });
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 52dbe2460e1c..78a30e820c80 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -794,32 +794,35 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@Override
public void onPointerDown(long requestId, int sensorId, int x, int y,
float minor, float major) {
- final BaseClientMonitor client = mScheduler.getCurrentClientIfMatches(requestId);
- if (!(client instanceof Udfps)) {
- Slog.w(TAG, "onFingerDown received during client: " + client);
- return;
- }
- ((Udfps) client).onPointerDown(x, y, minor, major);
+ mScheduler.getCurrentClientIfMatches(requestId, (client) -> {
+ if (!(client instanceof Udfps)) {
+ Slog.w(TAG, "onFingerDown received during client: " + client);
+ return;
+ }
+ ((Udfps) client).onPointerDown(x, y, minor, major);
+ });
}
@Override
public void onPointerUp(long requestId, int sensorId) {
- final BaseClientMonitor client = mScheduler.getCurrentClientIfMatches(requestId);
- if (!(client instanceof Udfps)) {
- Slog.w(TAG, "onFingerDown received during client: " + client);
- return;
- }
- ((Udfps) client).onPointerUp();
+ mScheduler.getCurrentClientIfMatches(requestId, (client) -> {
+ if (!(client instanceof Udfps)) {
+ Slog.w(TAG, "onFingerDown received during client: " + client);
+ return;
+ }
+ ((Udfps) client).onPointerUp();
+ });
}
@Override
public void onUiReady(long requestId, int sensorId) {
- final BaseClientMonitor client = mScheduler.getCurrentClientIfMatches(requestId);
- if (!(client instanceof Udfps)) {
- Slog.w(TAG, "onUiReady received during client: " + client);
- return;
- }
- ((Udfps) client).onUiReady();
+ mScheduler.getCurrentClientIfMatches(requestId, (client) -> {
+ if (!(client instanceof Udfps)) {
+ Slog.w(TAG, "onUiReady received during client: " + client);
+ return;
+ }
+ ((Udfps) client).onUiReady();
+ });
}
@Override
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index f68d22acd6ab..4e1d899b26a6 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -17,13 +17,21 @@
package com.android.server.dreams;
import static android.Manifest.permission.BIND_DREAM_SERVICE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID;
+
+import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.TaskInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
@@ -54,6 +62,7 @@ import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.wm.ActivityInterceptorCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
@@ -83,6 +92,7 @@ public final class DreamManagerService extends SystemService {
private final UiEventLogger mUiEventLogger;
private final DreamUiEventLogger mDreamUiEventLogger;
private final ComponentName mAmbientDisplayComponent;
+ private final boolean mDismissDreamOnActivityStart;
private Binder mCurrentDreamToken;
private ComponentName mCurrentDreamName;
@@ -99,6 +109,26 @@ public final class DreamManagerService extends SystemService {
private ComponentName mDreamOverlayServiceName;
private AmbientDisplayConfiguration mDozeConfig;
+ private final ActivityInterceptorCallback mActivityInterceptorCallback =
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+
+ @Override
+ public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
+ ActivityInterceptorInfo info) {
+ final int activityType = taskInfo.getActivityType();
+ final boolean activityAllowed = activityType == ACTIVITY_TYPE_HOME
+ || activityType == ACTIVITY_TYPE_DREAM
+ || activityType == ACTIVITY_TYPE_ASSISTANT;
+ if (mCurrentDreamToken != null && !mCurrentDreamIsWaking && !activityAllowed) {
+ stopDreamInternal(false, "activity starting: " + activityInfo.name);
+ }
+ }
+ };
public DreamManagerService(Context context) {
super(context);
@@ -118,6 +148,8 @@ public final class DreamManagerService extends SystemService {
mAmbientDisplayComponent = ComponentName.unflattenFromString(adc.ambientDisplayComponent());
mDreamsOnlyEnabledForSystemUser =
mContext.getResources().getBoolean(R.bool.config_dreamsOnlyEnabledForSystemUser);
+ mDismissDreamOnActivityStart = mContext.getResources().getBoolean(
+ R.bool.config_dismissDreamOnActivityStart);
}
@Override
@@ -145,6 +177,12 @@ public final class DreamManagerService extends SystemService {
Settings.Secure.getUriFor(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE), false,
mDozeEnabledObserver, UserHandle.USER_ALL);
writePulseGestureEnabled();
+
+ if (mDismissDreamOnActivityStart) {
+ mAtmInternal.registerActivityStartInterceptor(
+ DREAM_MANAGER_ORDERED_ID,
+ mActivityInterceptorCallback);
+ }
}
}
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index db2cb52d778e..5253d34a38f0 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -20,7 +20,11 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -34,7 +38,9 @@ import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/**
* Gets the service name using a framework resources, temporarily changing the service if necessary
@@ -49,10 +55,14 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
/** Handler message to {@link #resetTemporaryService(int)} */
private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
- @NonNull private final Context mContext;
- @NonNull private final Object mLock = new Object();
- @StringRes private final int mStringResourceId;
- @ArrayRes private final int mArrayResourceId;
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final Object mLock = new Object();
+ @StringRes
+ private final int mStringResourceId;
+ @ArrayRes
+ private final int mArrayResourceId;
private final boolean mIsMultiple;
/**
* Map of temporary service name list set by {@link #setTemporaryServices(int, String[], int)},
@@ -71,7 +81,8 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
*/
@GuardedBy("mLock")
private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray();
- @Nullable private NameResolverListener mOnSetCallback;
+ @Nullable
+ private NameResolverListener mOnSetCallback;
/**
* When the temporary service will expire (and reset back to the default).
*/
@@ -160,10 +171,33 @@ public final class FrameworkResourcesServiceNameResolver implements ServiceNameR
public String[] getDefaultServiceNameList(int userId) {
synchronized (mLock) {
if (mIsMultiple) {
- return mContext.getResources().getStringArray(mArrayResourceId);
+ String[] serviceNameList = mContext.getResources().getStringArray(mArrayResourceId);
+ // Filter out unimplemented services
+ // Initialize the validated array as null because we do not know the final size.
+ List<String> validatedServiceNameList = new ArrayList<>();
+ try {
+ for (int i = 0; i < serviceNameList.length; i++) {
+ if (TextUtils.isEmpty(serviceNameList[i])) {
+ continue;
+ }
+ ComponentName serviceComponent = ComponentName.unflattenFromString(
+ serviceNameList[i]);
+ ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
+ serviceComponent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ if (serviceInfo != null) {
+ validatedServiceNameList.add(serviceNameList[i]);
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not validate provided services.", e);
+ }
+ String[] validatedServiceNameArray = new String[validatedServiceNameList.size()];
+ return validatedServiceNameList.toArray(validatedServiceNameArray);
} else {
final String name = mContext.getString(mStringResourceId);
- return TextUtils.isEmpty(name) ? new String[0] : new String[] { name };
+ return TextUtils.isEmpty(name) ? new String[0] : new String[]{name};
}
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index b978131e175c..57d89dae588e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -133,10 +133,13 @@ public abstract class InputMethodManagerInternal {
*
* @param windowToken the window token that is now in control, or {@code null} if no client
* window is in control of the IME.
- * @param imeParentChanged {@code true} when the window manager thoughts the IME surface parent
- * will end up to change later, or {@code false} otherwise.
*/
- public abstract void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged);
+ public abstract void reportImeControl(@Nullable IBinder windowToken);
+
+ /**
+ * Indicates that the IME window has re-parented to the new target when the IME control changed.
+ */
+ public abstract void onImeParentChanged();
/**
* Destroys the IME surface.
@@ -226,8 +229,11 @@ public abstract class InputMethodManagerInternal {
}
@Override
- public void reportImeControl(@Nullable IBinder windowToken,
- boolean imeParentChanged) {
+ public void reportImeControl(@Nullable IBinder windowToken) {
+ }
+
+ @Override
+ public void onImeParentChanged() {
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ce8b9fabd5a0..6af00b3fbeea 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5700,19 +5700,23 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
+ public void reportImeControl(@Nullable IBinder windowToken) {
synchronized (ImfLock.class) {
if (mCurFocusedWindow != windowToken) {
// mCurPerceptible was set by the focused window, but it is no longer in
// control, so we reset mCurPerceptible.
mCurPerceptible = true;
}
- if (imeParentChanged) {
- // Hide the IME method menu earlier when the IME surface parent will change in
- // case seeing the dialog dismiss flickering during the next focused window
- // starting the input connection.
- mMenuController.hideInputMethodMenu();
- }
+ }
+ }
+
+ @Override
+ public void onImeParentChanged() {
+ synchronized (ImfLock.class) {
+ // Hide the IME method menu when the IME surface parent will change in
+ // case seeing the dialog dismiss flickering during the next focused window
+ // starting the input connection.
+ mMenuController.hideInputMethodMenu();
}
}
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index 70a222fb09c5..dc5299077cc9 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -34,6 +34,8 @@ import java.util.Objects;
public class SystemEmergencyHelper extends EmergencyHelper {
private final Context mContext;
+ private final EmergencyCallTelephonyCallback mEmergencyCallTelephonyCallback =
+ new EmergencyCallTelephonyCallback();
TelephonyManager mTelephonyManager;
@@ -56,7 +58,7 @@ public class SystemEmergencyHelper extends EmergencyHelper {
// TODO: this doesn't account for multisim phones
mTelephonyManager.registerTelephonyCallback(FgThread.getExecutor(),
- new EmergencyCallTelephonyCallback());
+ mEmergencyCallTelephonyCallback);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java
index d175d87651de..a56380827f2c 100644
--- a/services/core/java/com/android/server/media/MediaShellCommand.java
+++ b/services/core/java/com/android/server/media/MediaShellCommand.java
@@ -107,7 +107,6 @@ public class MediaShellCommand extends ShellCommand {
public void onHelp() {
mWriter.println("usage: media_session [subcommand] [options]");
mWriter.println(" media_session dispatch KEY");
- mWriter.println(" media_session dispatch KEY");
mWriter.println(" media_session list-sessions");
mWriter.println(" media_session monitor <tag>");
mWriter.println(" media_session volume [options]");
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 2d8d4f588192..57a1fe04b690 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -4187,8 +4187,8 @@ final class InstallPackageHelper {
assertOverlayIsValid(pkg, parseFlags, scanFlags);
}
- // If the package is not on a system partition ensure it is signed with at least the
- // minimum signature scheme version required for its target SDK.
+ // Ensure the package is signed with at least the minimum signature scheme version
+ // required for its target SDK.
ScanPackageUtils.assertMinSignatureSchemeIsValid(pkg, parseFlags);
}
}
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 4e8313bf1891..0dc188b75d5e 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -690,16 +690,14 @@ final class ScanPackageUtils {
public static void assertMinSignatureSchemeIsValid(AndroidPackage pkg,
@ParsingPackageUtils.ParseFlags int parseFlags) throws PackageManagerException {
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- int minSignatureSchemeVersion =
- ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
- pkg.getTargetSdkVersion());
- if (pkg.getSigningDetails().getSignatureSchemeVersion()
- < minSignatureSchemeVersion) {
- throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + pkg.getPackageName());
- }
+ int minSignatureSchemeVersion =
+ ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+ pkg.getTargetSdkVersion());
+ if (pkg.getSigningDetails().getSignatureSchemeVersion()
+ < minSignatureSchemeVersion) {
+ throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "No signature found in package of version " + minSignatureSchemeVersion
+ + " or newer for package " + pkg.getPackageName());
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 2960bc9a3790..c0c234953297 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -420,4 +420,14 @@ class ShortcutLauncher extends ShortcutPackageItem {
ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) {
return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)));
}
+
+ @Override
+ protected File getShortcutPackageItemFile() {
+ final File path = new File(mShortcutUser.mService.injectUserDataPath(
+ mShortcutUser.getUserId()), ShortcutUser.DIRECTORY_LUANCHERS);
+ // Package user id and owner id can have different values for ShortcutLaunchers. Adding
+ // user Id to the file name to create a unique path. Owner id is used in the root path.
+ final String fileName = getPackageName() + getPackageUserId() + ".xml";
+ return new File(path, fileName);
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index f57eaaef25a4..fef6ce1f67b3 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -160,8 +160,6 @@ class ShortcutPackage extends ShortcutPackageItem {
private static final String KEY_BITMAPS = "bitmaps";
private static final String KEY_BITMAP_BYTES = "bitmapBytes";
- private final Object mLock = new Object();
-
private final Executor mExecutor;
/**
@@ -779,7 +777,7 @@ class ShortcutPackage extends ShortcutPackageItem {
return false;
}
mApiCallCount++;
- s.scheduleSaveUser(getOwnerUserId());
+ scheduleSave();
return true;
}
@@ -789,7 +787,7 @@ class ShortcutPackage extends ShortcutPackageItem {
}
if (mApiCallCount > 0) {
mApiCallCount = 0;
- mShortcutUser.mService.scheduleSaveUser(getOwnerUserId());
+ scheduleSave();
}
}
@@ -1890,15 +1888,12 @@ class ShortcutPackage extends ShortcutPackageItem {
final ShortcutPackage ret = new ShortcutPackage(shortcutUser,
shortcutUser.getUserId(), packageName);
-
synchronized (ret.mLock) {
ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute(
parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION;
}
- ret.mApiCallCount =
- ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
- ret.mLastResetTime =
- ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
+ ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
+ ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
final int outerDepth = parser.getDepth();
@@ -2440,16 +2435,15 @@ class ShortcutPackage extends ShortcutPackageItem {
})));
}
- void persistsAllShortcutsAsync() {
- synchronized (mLock) {
- final Map<String, ShortcutInfo> copy = mShortcuts;
- if (!mTransientShortcuts.isEmpty()) {
- copy.putAll(mTransientShortcuts);
- mTransientShortcuts.clear();
- }
- saveShortcutsAsync(copy.values().stream().filter(ShortcutInfo::usesQuota).collect(
- Collectors.toList()));
+ @Override
+ void scheduleSaveToAppSearchLocked() {
+ final Map<String, ShortcutInfo> copy = new ArrayMap<>(mShortcuts);
+ if (!mTransientShortcuts.isEmpty()) {
+ copy.putAll(mTransientShortcuts);
+ mTransientShortcuts.clear();
}
+ saveShortcutsAsync(copy.values().stream().filter(ShortcutInfo::usesQuota).collect(
+ Collectors.toList()));
}
private void saveShortcutsAsync(
@@ -2548,4 +2542,12 @@ class ShortcutPackage extends ShortcutPackageItem {
Binder.restoreCallingIdentity(callingIdentity);
}
}
+
+ @Override
+ protected File getShortcutPackageItemFile() {
+ final File path = new File(mShortcutUser.mService.injectUserDataPath(
+ mShortcutUser.getUserId()), ShortcutUser.DIRECTORY_PACKAGES);
+ final String fileName = getPackageName() + ".xml";
+ return new File(path, fileName);
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 829133c9854a..6e0436f208e3 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -23,6 +23,7 @@ import android.util.Slog;
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import org.json.JSONException;
@@ -36,7 +37,7 @@ import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
- * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
+ * All methods should be either guarded by {@code #mShortcutUser.mService.mLock} or {@code #mLock}.
*/
abstract class ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
@@ -49,6 +50,8 @@ abstract class ShortcutPackageItem {
protected ShortcutUser mShortcutUser;
+ protected final Object mLock = new Object();
+
protected ShortcutPackageItem(@NonNull ShortcutUser shortcutUser,
int packageUserId, @NonNull String packageName,
@NonNull ShortcutPackageInfo packageInfo) {
@@ -98,7 +101,7 @@ abstract class ShortcutPackageItem {
}
final ShortcutService s = mShortcutUser.mService;
mPackageInfo.refreshSignature(s, this);
- s.scheduleSaveUser(getOwnerUserId());
+ scheduleSave();
}
public void attemptToRestoreIfNeededAndSave() {
@@ -138,7 +141,7 @@ abstract class ShortcutPackageItem {
// Either way, it's no longer a shadow.
mPackageInfo.setShadow(false);
- s.scheduleSaveUser(mPackageUserId);
+ scheduleSave();
}
protected abstract boolean canRestoreAnyVersion();
@@ -148,7 +151,8 @@ abstract class ShortcutPackageItem {
public abstract void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException;
- public void saveToFile(File path, boolean forBackup) {
+ @GuardedBy("mLock")
+ public void saveToFileLocked(File path, boolean forBackup) {
final AtomicFile file = new AtomicFile(path);
FileOutputStream os = null;
try {
@@ -176,6 +180,11 @@ abstract class ShortcutPackageItem {
}
}
+ @GuardedBy("mLock")
+ void scheduleSaveToAppSearchLocked() {
+
+ }
+
public JSONObject dumpCheckin(boolean clear) throws JSONException {
final JSONObject result = new JSONObject();
result.put(KEY_NAME, mPackageName);
@@ -187,4 +196,36 @@ abstract class ShortcutPackageItem {
*/
public void verifyStates() {
}
+
+ public void scheduleSave() {
+ mShortcutUser.mService.injectPostToHandlerDebounced(
+ mSaveShortcutPackageRunner, mSaveShortcutPackageRunner);
+ }
+
+ private final Runnable mSaveShortcutPackageRunner = this::saveShortcutPackageItem;
+
+ void saveShortcutPackageItem() {
+ // Wait for bitmap saves to conclude before proceeding to saving shortcuts.
+ mShortcutUser.mService.waitForBitmapSaves();
+ // Save each ShortcutPackageItem in a separate Xml file.
+ final File path = getShortcutPackageItemFile();
+ if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
+ Slog.d(TAG, "Saving package item " + getPackageName() + " to " + path);
+ }
+ synchronized (mLock) {
+ path.getParentFile().mkdirs();
+ // TODO: Since we are persisting shortcuts into AppSearch, we should read from/write to
+ // AppSearch as opposed to maintaining a separate XML file.
+ saveToFileLocked(path, false /*forBackup*/);
+ scheduleSaveToAppSearchLocked();
+ }
+ }
+
+ void removeShortcutPackageItem() {
+ synchronized (mLock) {
+ getShortcutPackageItemFile().delete();
+ }
+ }
+
+ protected abstract File getShortcutPackageItemFile();
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 9627c4394db7..780f976d2a40 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -748,7 +748,7 @@ public class ShortcutService extends IShortcutService.Stub {
getUserShortcutsLocked(userId).cancelAllInFlightTasks();
// Save all dirty information.
- saveDirtyInfo(false);
+ saveDirtyInfo();
// Unload
mUsers.delete(userId);
@@ -1203,10 +1203,6 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting
void saveDirtyInfo() {
- saveDirtyInfo(true);
- }
-
- private void saveDirtyInfo(boolean saveShortcutsInAppSearch) {
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "saveDirtyInfo");
}
@@ -1221,10 +1217,6 @@ public class ShortcutService extends IShortcutService.Stub {
if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
saveBaseStateLocked();
} else {
- if (saveShortcutsInAppSearch) {
- getUserShortcutsLocked(userId).forAllPackages(
- ShortcutPackage::persistsAllShortcutsAsync);
- }
saveUserLocked(userId);
}
}
@@ -1816,7 +1808,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
injectPostToHandlerDebounced(sp, notifyListenerRunnable(packageName, userId));
notifyShortcutChangeCallbacks(packageName, userId, changedShortcuts, removedShortcuts);
- scheduleSaveUser(userId);
+ sp.scheduleSave();
}
private void notifyListeners(@NonNull final String packageName, @UserIdInt final int userId) {
@@ -2878,12 +2870,11 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutUser user = getUserShortcutsLocked(owningUserId);
boolean doNotify = false;
-
// First, remove the package from the package list (if the package is a publisher).
- if (packageUserId == owningUserId) {
- if (user.removePackage(packageName) != null) {
- doNotify = true;
- }
+ final ShortcutPackage sp = (packageUserId == owningUserId)
+ ? user.removePackage(packageName) : null;
+ if (sp != null) {
+ doNotify = true;
}
// Also remove from the launcher list (if the package is a launcher).
@@ -2906,6 +2897,10 @@ public class ShortcutService extends IShortcutService.Stub {
// notifyListeners.
user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
}
+ if (!appStillExists && (packageUserId == owningUserId) && sp != null) {
+ // If the app is removed altogether, we can get rid of the xml as well
+ injectPostToHandler(() -> sp.removeShortcutPackageItem());
+ }
if (!wasUserLoaded) {
// Note this will execute the scheduled save.
@@ -3788,7 +3783,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (mHandler.hasCallbacks(mSaveDirtyInfoRunner)) {
mHandler.removeCallbacks(mSaveDirtyInfoRunner);
forEachLoadedUserLocked(ShortcutUser::cancelAllInFlightTasks);
- saveDirtyInfo(false);
+ saveDirtyInfo();
}
mShutdown.set(true);
}
@@ -4457,7 +4452,7 @@ public class ShortcutService extends IShortcutService.Stub {
// Save to the filesystem.
scheduleSaveUser(userId);
- saveDirtyInfo(false);
+ saveDirtyInfo();
// Note, in case of backup, we don't have to wait on bitmap saving, because we don't
// back up bitmaps anyway.
@@ -5352,8 +5347,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
- @VisibleForTesting
- void waitForBitmapSavesForTest() {
+ void waitForBitmapSaves() {
synchronized (mLock) {
mShortcutBitmapSaver.waitForAllSavesLocked();
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 4bb5dcfa4b26..75e18b547c55 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -407,35 +407,10 @@ class ShortcutUser {
}
spi.saveToXml(out, forBackup);
} else {
- // Save each ShortcutPackageItem in a separate Xml file.
- final File path = getShortcutPackageItemFile(spi);
- if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Saving package item " + spi.getPackageName() + " to " + path);
- }
-
- path.getParentFile().mkdirs();
- spi.saveToFile(path, forBackup);
+ spi.saveShortcutPackageItem();
}
}
- private File getShortcutPackageItemFile(ShortcutPackageItem spi) {
- boolean isShortcutLauncher = spi instanceof ShortcutLauncher;
-
- final File path = new File(mService.injectUserDataPath(mUserId),
- isShortcutLauncher ? DIRECTORY_LUANCHERS : DIRECTORY_PACKAGES);
-
- final String fileName;
- if (isShortcutLauncher) {
- // Package user id and owner id can have different values for ShortcutLaunchers. Adding
- // user Id to the file name to create a unique path. Owner id is used in the root path.
- fileName = spi.getPackageName() + spi.getPackageUserId() + ".xml";
- } else {
- fileName = spi.getPackageName() + ".xml";
- }
-
- return new File(path, fileName);
- }
-
public static ShortcutUser loadFromXml(ShortcutService s, TypedXmlPullParser parser, int userId,
boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException {
final ShortcutUser ret = new ShortcutUser(s, userId);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 358e71a70550..0dabff8370ba 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4291,7 +4291,7 @@ public class UserManagerService extends IUserManager.Stub {
for (int i = 0; i < userSize; i++) {
final UserData user = mUsers.valueAt(i);
if (DBG) Slog.d(LOG_TAG, i + ":" + user.info.toFullString());
- if (user.info.preCreated && user.info.userType.equals(userType)) {
+ if (user.info.preCreated && !user.info.partial && user.info.userType.equals(userType)) {
if (!user.info.isInitialized()) {
Slog.w(LOG_TAG, "found pre-created user of type " + userType
+ ", but it's not initialized yet: " + user.info.toFullString());
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 284c29ecfbe9..a04f6d64ef8f 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -904,14 +904,6 @@ final class DefaultPermissionGrantPolicy {
COARSE_BACKGROUND_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
}
- // Attention Service
- String attentionServicePackageName =
- mContext.getPackageManager().getAttentionServicePackageName();
- if (!TextUtils.isEmpty(attentionServicePackageName)) {
- grantPermissionsToSystemPackage(pm, attentionServicePackageName, userId,
- CAMERA_PERMISSIONS);
- }
-
// There is no real "marker" interface to identify the shared storage backup, it is
// hardcoded in BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE.
grantSystemFixedPermissionsToSystemPackage(pm, "com.android.sharedstoragebackup", userId,
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 9897c42e4cec..e1ff9ead6740 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -3105,11 +3105,9 @@ public class ParsingPackageUtils {
}
final ParseResult<SigningDetails> verified;
if (skipVerify) {
- // systemDir APKs are already trusted, save time by not verifying; since the
- // signature is not verified and some system apps can have their V2+ signatures
- // stripped allow pulling the certs from the jar signature.
+ // systemDir APKs are already trusted, save time by not verifying
verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(input, baseCodePath,
- SigningDetails.SignatureSchemeVersion.JAR);
+ minSignatureScheme);
} else {
verified = ApkSignatureVerifier.verify(input, baseCodePath, minSignatureScheme);
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING b/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING
index ba4a62cdbbf1..8a1982a339ea 100644
--- a/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING
@@ -12,9 +12,6 @@
"name": "CtsDomainVerificationDeviceStandaloneTestCases"
},
{
- "name": "CtsDomainVerificationDeviceMultiUserTestCases"
- },
- {
"name": "CtsDomainVerificationHostTestCases"
}
]
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
index 92b9944b74cf..77885c7ab8ba 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
@@ -74,10 +74,13 @@ public abstract class PermissionPolicyInternal {
*
* @param taskInfo The task to be checked
* @param currPkg The package of the current top visible activity
+ * @param callingPkg The package that started the top visible activity
* @param intent The intent of the current top visible activity
+ * @param activityName The name of the current top visible activity
*/
public abstract boolean shouldShowNotificationDialogForTask(@Nullable TaskInfo taskInfo,
- @Nullable String currPkg, @Nullable Intent intent);
+ @Nullable String currPkg, @Nullable String callingPkg, @Nullable Intent intent,
+ @NonNull String activityName);
/**
* @return true if an intent will resolve to a permission request dialog activity
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 14abc9aabc29..7ba1cadc5c8b 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -35,6 +35,7 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
@@ -1166,7 +1167,8 @@ public final class PermissionPolicyService extends SystemService {
ActivityInterceptorInfo info) {
super.onActivityLaunched(taskInfo, activityInfo, info);
if (!shouldShowNotificationDialogOrClearFlags(taskInfo,
- activityInfo.packageName, info.intent, info.checkedOptions, true)
+ activityInfo.packageName, info.callingPackage, info.intent,
+ info.checkedOptions, activityInfo.name, true)
|| isNoDisplayActivity(activityInfo)) {
return;
}
@@ -1237,9 +1239,9 @@ public final class PermissionPolicyService extends SystemService {
@Override
public boolean shouldShowNotificationDialogForTask(TaskInfo taskInfo, String currPkg,
- Intent intent) {
- return shouldShowNotificationDialogOrClearFlags(
- taskInfo, currPkg, intent, null, false);
+ String callingPkg, Intent intent, String activityName) {
+ return shouldShowNotificationDialogOrClearFlags(taskInfo, currPkg, callingPkg, intent,
+ null, activityName, false);
}
private boolean isNoDisplayActivity(@NonNull ActivityInfo aInfo) {
@@ -1265,23 +1267,61 @@ public final class PermissionPolicyService extends SystemService {
* 1. The isEligibleForLegacyPermissionPrompt ActivityOption is set, or
* 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER), or
* 3. The activity belongs to the same package as the one which launched the task
- * originally, and the task was started with a launcher intent
+ * originally, and the task was started with a launcher intent, or
+ * 4. The activity is the first activity in a new task, and was started by the app the
+ * activity belongs to, and that app has another task that is currently focused, which was
+ * started with a launcher intent. This case seeks to identify cases where an app launches,
+ * then immediately trampolines to a new activity and task.
* @param taskInfo The task to be checked
* @param currPkg The package of the current top visible activity
+ * @param callingPkg The package that initiated this dialog action
* @param intent The intent of the current top visible activity
+ * @param options The ActivityOptions of the newly started activity, if this is called due
+ * to an activity start
+ * @param startedActivity The ActivityInfo of the newly started activity, if this is called
+ * due to an activity start
*/
private boolean shouldShowNotificationDialogOrClearFlags(TaskInfo taskInfo, String currPkg,
- Intent intent, ActivityOptions options, boolean activityStart) {
- if (intent == null || currPkg == null || taskInfo == null
+ String callingPkg, Intent intent, ActivityOptions options,
+ String topActivityName, boolean startedActivity) {
+ if (intent == null || currPkg == null || taskInfo == null || topActivityName == null
|| (!(taskInfo.isFocused && taskInfo.isVisible && taskInfo.isRunning)
- && !activityStart)) {
+ && !startedActivity)) {
return false;
}
-
return isLauncherIntent(intent)
|| (options != null && options.isEligibleForLegacyPermissionPrompt())
- || (currPkg.equals(taskInfo.baseActivity.getPackageName())
- && isLauncherIntent(taskInfo.baseIntent));
+ || isTaskStartedFromLauncher(currPkg, taskInfo)
+ || (isTaskPotentialTrampoline(topActivityName, currPkg, callingPkg, taskInfo,
+ intent)
+ && (!startedActivity || pkgHasRunningLauncherTask(currPkg, taskInfo)));
+ }
+
+ private boolean isTaskPotentialTrampoline(String activityName, String currPkg,
+ String callingPkg, TaskInfo taskInfo, Intent intent) {
+ return currPkg.equals(callingPkg) && taskInfo.baseIntent.filterEquals(intent)
+ && taskInfo.numActivities == 1
+ && activityName.equals(taskInfo.topActivityInfo.name);
+ }
+
+ private boolean pkgHasRunningLauncherTask(String currPkg, TaskInfo taskInfo) {
+ ActivityTaskManagerInternal m =
+ LocalServices.getService(ActivityTaskManagerInternal.class);
+ try {
+ // TODO(b/230616478) Investigate alternatives like ActivityMetricsLaunchObserver
+ List<ActivityManager.AppTask> tasks =
+ m.getAppTasks(currPkg, mPackageManager.getPackageUid(currPkg, 0));
+ for (int i = 0; i < tasks.size(); i++) {
+ TaskInfo other = tasks.get(i).getTaskInfo();
+ if (other.taskId != taskInfo.taskId && other.isFocused && other.isRunning
+ && isTaskStartedFromLauncher(currPkg, other)) {
+ return true;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Fall through
+ }
+ return false;
}
private boolean isLauncherIntent(Intent intent) {
@@ -1292,6 +1332,11 @@ public final class PermissionPolicyService extends SystemService {
|| intent.getCategories().contains(Intent.CATEGORY_CAR_LAUNCHER));
}
+ private boolean isTaskStartedFromLauncher(String currPkg, TaskInfo taskInfo) {
+ return currPkg.equals(taskInfo.baseActivity.getPackageName())
+ && isLauncherIntent(taskInfo.baseIntent);
+ }
+
private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle user) {
if ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user)
& FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index 400460a1e656..34483957ca12 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -61,6 +61,7 @@ public abstract class ActivityInterceptorCallback {
PERMISSION_POLICY_ORDERED_ID,
INTENT_RESOLVER_ORDERED_ID,
VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
+ DREAM_MANAGER_ORDERED_ID,
LAST_ORDERED_ID // Update this when adding new ids
})
@Retention(RetentionPolicy.SOURCE)
@@ -88,10 +89,15 @@ public abstract class ActivityInterceptorCallback {
public static final int VIRTUAL_DEVICE_SERVICE_ORDERED_ID = 3;
/**
+ * The identifier for {@link com.android.server.dreams.DreamManagerService} interceptor.
+ */
+ public static final int DREAM_MANAGER_ORDERED_ID = 4;
+
+ /**
* The final id, used by the framework to determine the valid range of ids. Update this when
* adding new ids.
*/
- static final int LAST_ORDERED_ID = VIRTUAL_DEVICE_SERVICE_ORDERED_ID;
+ static final int LAST_ORDERED_ID = DREAM_MANAGER_ORDERED_ID;
/**
* Data class for storing the various arguments needed for activity interception.
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index c6b17e24b1de..81e5fbd564e0 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -18,17 +18,19 @@ package com.android.server.wm;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Intent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * Observe activity manager launch sequences.
+ * Observe activity launch sequences.
*
- * The activity manager can have at most 1 concurrent launch sequences. Calls to this interface
- * are ordered by a happens-before relation for each defined state transition (see below).
+ * Multiple calls to the callback methods can occur without first terminating the current launch
+ * sequence because activity can be launched concurrently. So the implementation should associate
+ * the corresponding event according to the timestamp from {@link #onIntentStarted} which is also
+ * used as the identifier to indicate which launch sequence it belongs to.
*
* When a new launch sequence is made, that sequence is in the {@code INTENT_STARTED} state which
* is communicated by the {@link #onIntentStarted} callback. This is a transient state.
@@ -47,7 +49,7 @@ import java.lang.annotation.RetentionPolicy;
* Note this transition may not happen if the reportFullyDrawn event is not receivied,
* in which case {@code FINISHED} is terminal.
*
- * Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't
+ * Note that the {@code ComponentName} provided as a parameter to some state transitions isn't
* necessarily the same within a single launch sequence: it is only the top-most activity at the
* time (if any). Trampoline activities coalesce several activity starts into a single launch
* sequence.
@@ -67,7 +69,7 @@ import java.lang.annotation.RetentionPolicy;
* ╚════════════════╝ ╚═══════════════════════════╝ ╚═══════════════════════════╝
* </pre>
*/
-public interface ActivityMetricsLaunchObserver {
+public class ActivityMetricsLaunchObserver {
/**
* The 'temperature' at which a launch sequence had started.
*
@@ -99,40 +101,31 @@ public interface ActivityMetricsLaunchObserver {
public static final int TEMPERATURE_HOT = 3;
/**
- * Typedef marker that a {@code byte[]} actually contains an
- * <a href="proto/android/server/activitymanagerservice.proto">ActivityRecordProto</a>
- * in the protobuf format.
- */
- @Retention(RetentionPolicy.SOURCE)
- @interface ActivityRecordProto {}
-
- /**
* Notifies the observer that a new launch sequence has begun as a result of a new intent.
*
* Once a launch sequence begins, the resolved activity will either subsequently start with
* {@link #onActivityLaunched} or abort early (for example due to a resolution error or due to
* a security error) with {@link #onIntentFailed}.
*
- * Multiple calls to this method cannot occur without first terminating the current
- * launch sequence.
+ * @param timestampNanos The timestamp when receiving the intent. It is also use as an
+ * identifier for other callback methods to known which launch sequence
+ * it is associated with.
*/
- public void onIntentStarted(@NonNull Intent intent, long timestampNanos);
+ public void onIntentStarted(@NonNull Intent intent, long timestampNanos) {
+ }
/**
* Notifies the observer that the current launch sequence has failed to launch an activity.
*
- * This function call terminates the current launch sequence. The next method call, if any,
- * must be {@link #onIntentStarted}.
+ * This function call terminates the current launch sequence.
*
* Examples of this happening:
* - Failure to resolve to an activity
* - Calling package did not have the security permissions to call the requested activity
* - Resolved activity was already running and only needed to be brought to the top
- *
- * Multiple calls to this method cannot occur without first terminating the current
- * launch sequence.
*/
- public void onIntentFailed();
+ public void onIntentFailed(long id) {
+ }
/**
* Notifies the observer that the current launch sequence had begun starting an activity.
@@ -145,62 +138,58 @@ public interface ActivityMetricsLaunchObserver {
* necessarily the activity which will be considered as displayed when the activity
* finishes launching (e.g. {@code activity} in {@link #onActivityLaunchFinished}).
*
- * Multiple calls to this method cannot occur without first terminating the current
- * launch sequence.
+ * @param id The timestamp as an identifier from {@link #onIntentStarted}. It may be a new id
+ * if the launching activity is started from an existing launch sequence (trampoline)
+ * but cannot coalesce to the existing one, e.g. to a different display.
+ * @param name The launching activity name.
*/
- public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
- @Temperature int temperature);
+ public void onActivityLaunched(long id, ComponentName name, @Temperature int temperature) {
+ }
/**
* Notifies the observer that the current launch sequence has been aborted.
*
- * This function call terminates the current launch sequence. The next method call, if any,
- * must be {@link #onIntentStarted}.
+ * This function call terminates the current launch sequence.
*
* This can happen for many reasons, for example the user switches away to another app
* prior to the launch sequence completing, or the application being killed.
*
- * Multiple calls to this method cannot occur without first terminating the current
- * launch sequence.
- *
- * @param abortingActivity the last activity that had the top-most window during abort
- * (this can be {@code null} in rare situations its unknown).
+ * @param id The timestamp as an identifier from {@link #onIntentStarted}.
*
* @apiNote The aborting activity isn't necessarily the same as the starting activity;
* in the case of a trampoline, multiple activities could've been started
* and only the latest activity is reported here.
*/
- public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] abortingActivity);
+ public void onActivityLaunchCancelled(long id) {
+ }
/**
* Notifies the observer that the current launch sequence has been successfully finished.
*
- * This function call terminates the current launch sequence. The next method call, if any,
- * must be {@link #onIntentStarted}.
+ * This function call terminates the current launch sequence.
*
* A launch sequence is considered to be successfully finished when a frame is fully
* drawn for the first time: the top-most activity at the time is what's reported here.
*
- * @param finalActivity the top-most activity whose windows were first to fully draw
+ * @param id The timestamp as an identifier from {@link #onIntentStarted}.
+ * @param name The name of drawn activity. It can be different from {@link #onActivityLaunched}
+ * if the transition contains multiple launching activities (e.g. trampoline).
* @param timestampNanos the timestamp of ActivityLaunchFinished event in nanoseconds.
* To compute the TotalTime duration, deduct the timestamp {@link #onIntentStarted}
* from {@code timestampNanos}.
*
- * Multiple calls to this method cannot occur without first terminating the current
- * launch sequence.
- *
* @apiNote The finishing activity isn't necessarily the same as the starting activity;
* in the case of a trampoline, multiple activities could've been started
* and only the latest activity that was top-most during first-frame drawn
* is reported here.
*/
- public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity,
- long timestampNanos);
+ public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos) {
+ }
/**
* Notifies the observer that the application self-reported itself as being fully drawn.
*
- * @param activity the activity that triggers the ReportFullyDrawn event.
+ * @param id The timestamp as an identifier from {@link #onIntentStarted}.
* @param timestampNanos the timestamp of ReportFullyDrawn event in nanoseconds.
* To compute the duration, deduct the deduct the timestamp {@link #onIntentStarted}
* from {@code timestampNanos}.
@@ -209,7 +198,7 @@ public interface ActivityMetricsLaunchObserver {
* It is used as an accurate estimate of meanfully app startup time.
* This event may be missing for many apps.
*/
- public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
- long timestampNanos);
+ public void onReportFullyDrawn(long id, long timestampNanos) {
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 7f84f61a91ff..1ea08f5cfea2 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -97,7 +97,6 @@ import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -176,7 +175,6 @@ class ActivityMetricsLogger {
* in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver.
*/
private final LaunchObserverRegistryImpl mLaunchObserver;
- @VisibleForTesting static final int LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE = 512;
private final ArrayMap<String, Boolean> mLastHibernationStates = new ArrayMap<>();
private AppHibernationManagerInternal mAppHibernationManagerInternal;
@@ -675,7 +673,7 @@ class ActivityMetricsLogger {
launchObserverNotifyActivityLaunched(newInfo);
} else {
// As abort for no process switch.
- launchObserverNotifyIntentFailed();
+ launchObserverNotifyIntentFailed(newInfo.mTransitionStartTimeNs);
}
scheduleCheckActivityToBeDrawnIfSleeping(launchedActivity);
@@ -910,7 +908,7 @@ class ActivityMetricsLogger {
}
if (DEBUG_METRICS) Slog.i(TAG, "abort launch cause=" + cause);
state.stopTrace(true /* abort */);
- launchObserverNotifyIntentFailed();
+ launchObserverNotifyIntentFailed(state.mCurrentTransitionStartTimeNs);
}
/** Aborts tracking of current launch metrics. */
@@ -1187,7 +1185,7 @@ class ActivityMetricsLogger {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// Notify reportFullyDrawn event.
- launchObserverNotifyReportFullyDrawn(r, currentTimestampNs);
+ launchObserverNotifyReportFullyDrawn(info, currentTimestampNs);
return infoSnapshot;
}
@@ -1531,11 +1529,11 @@ class ActivityMetricsLogger {
* aborted due to intent failure (e.g. intent resolve failed or security error, etc) or
* intent being delivered to the top running activity.
*/
- private void launchObserverNotifyIntentFailed() {
+ private void launchObserverNotifyIntentFailed(long id) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyIntentFailed");
- mLaunchObserver.onIntentFailed();
+ mLaunchObserver.onIntentFailed(id);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1552,8 +1550,8 @@ class ActivityMetricsLogger {
convertTransitionTypeToLaunchObserverTemperature(info.mTransitionType);
// Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onActivityLaunched(convertActivityRecordToProto(info.mLastLaunchedActivity),
- temperature);
+ mLaunchObserver.onActivityLaunched(info.mTransitionStartTimeNs,
+ info.mLastLaunchedActivity.mActivityComponent, temperature);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1561,10 +1559,10 @@ class ActivityMetricsLogger {
/**
* Notifies the {@link ActivityMetricsLaunchObserver} the reportFullDrawn event.
*/
- private void launchObserverNotifyReportFullyDrawn(ActivityRecord r, long timestampNs) {
+ private void launchObserverNotifyReportFullyDrawn(TransitionInfo info, long timestampNs) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyReportFullyDrawn");
- mLaunchObserver.onReportFullyDrawn(convertActivityRecordToProto(r), timestampNs);
+ mLaunchObserver.onReportFullyDrawn(info.mTransitionStartTimeNs, timestampNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1576,10 +1574,7 @@ class ActivityMetricsLogger {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyActivityLaunchCancelled");
- final @ActivityMetricsLaunchObserver.ActivityRecordProto byte[] activityRecordProto =
- info != null ? convertActivityRecordToProto(info.mLastLaunchedActivity) : null;
-
- mLaunchObserver.onActivityLaunchCancelled(activityRecordProto);
+ mLaunchObserver.onActivityLaunchCancelled(info.mTransitionStartTimeNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1592,31 +1587,10 @@ class ActivityMetricsLogger {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyActivityLaunchFinished");
- mLaunchObserver.onActivityLaunchFinished(
- convertActivityRecordToProto(info.mLastLaunchedActivity), timestampNs);
-
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
-
- @VisibleForTesting
- static @ActivityMetricsLaunchObserver.ActivityRecordProto byte[]
- convertActivityRecordToProto(ActivityRecord record) {
- // May take non-negligible amount of time to convert ActivityRecord into a proto,
- // so track the time.
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- "MetricsLogger:convertActivityRecordToProto");
-
- // There does not appear to be a way to 'reset' a ProtoOutputBuffer stream,
- // so create a new one every time.
- final ProtoOutputStream protoOutputStream =
- new ProtoOutputStream(LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
- // Write this data out as the top-most ActivityRecordProto (i.e. it is not a sub-object).
- record.dumpDebug(protoOutputStream, WindowTraceLogLevel.ALL);
- final byte[] bytes = protoOutputStream.getBytes();
+ mLaunchObserver.onActivityLaunchFinished(info.mTransitionStartTimeNs,
+ info.mLastLaunchedActivity.mActivityComponent, timestampNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
- return bytes;
}
private static @ActivityMetricsLaunchObserver.Temperature int
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b2f9b52cad8d..d00d8b8c9f8a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2447,6 +2447,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
boolean allowTaskSnapshot, boolean activityCreated, boolean activityAllDrawn,
TaskSnapshot snapshot) {
+ // A special case that a new activity is launching to an existing task which is moving to
+ // front. If the launching activity is the one that started the task, it could be a
+ // trampoline that will be always created and finished immediately. Then give a chance to
+ // see if the snapshot is usable for the current running activity so the transition will
+ // look smoother, instead of showing a splash screen on the second launch.
+ if (!newTask && taskSwitch && processRunning && !activityCreated && task.intent != null
+ && mActivityComponent.equals(task.intent.getComponent())) {
+ final ActivityRecord topAttached = task.getActivity(ActivityRecord::attachedToProcess);
+ if (topAttached != null && topAttached.isSnapshotCompatible(snapshot)) {
+ return STARTING_WINDOW_TYPE_SNAPSHOT;
+ }
+ }
final boolean isActivityHome = isActivityTypeHome();
if ((newTask || !processRunning || (taskSwitch && !activityCreated))
&& !isActivityHome) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d254aaff1a1c..41b724f2596f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -64,6 +64,7 @@ import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
import static android.provider.Settings.System.FONT_SCALE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static android.view.WindowManager.TRANSIT_WAKE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
@@ -3398,6 +3399,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
+ // Keyguard asked us to clear the home task snapshot before going away, so do that.
+ if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) {
+ mActivityClientController.invalidateHomeTaskSnapshot(null /* token */);
+ }
+
mRootWindowContainer.forAllDisplays(displayContent -> {
mKeyguardController.keyguardGoingAway(displayContent.getDisplayId(), flags);
});
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 6e46fa6b67d0..e80c2607a0ad 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -27,6 +27,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -48,8 +49,9 @@ class AppTaskImpl extends IAppTask.Stub {
mCallingUid = callingUid;
}
- private void checkCaller() {
- if (mCallingUid != Binder.getCallingUid()) {
+ private void checkCallerOrSystemOrRoot() {
+ if (mCallingUid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()
+ && Process.ROOT_UID != Binder.getCallingUid()) {
throw new SecurityException("Caller " + mCallingUid
+ " does not match caller of getAppTasks(): " + Binder.getCallingUid());
}
@@ -67,7 +69,7 @@ class AppTaskImpl extends IAppTask.Stub {
@Override
public void finishAndRemoveTask() {
- checkCaller();
+ checkCallerOrSystemOrRoot();
synchronized (mService.mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
@@ -85,7 +87,7 @@ class AppTaskImpl extends IAppTask.Stub {
@Override
public ActivityManager.RecentTaskInfo getTaskInfo() {
- checkCaller();
+ checkCallerOrSystemOrRoot();
synchronized (mService.mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
@@ -105,7 +107,7 @@ class AppTaskImpl extends IAppTask.Stub {
@Override
public void moveToFront(IApplicationThread appThread, String callingPackage) {
- checkCaller();
+ checkCallerOrSystemOrRoot();
// Will bring task to front if it already has a root activity.
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
@@ -136,7 +138,7 @@ class AppTaskImpl extends IAppTask.Stub {
@Override
public int startActivity(IBinder whoThread, String callingPackage, String callingFeatureId,
Intent intent, String resolvedType, Bundle bOptions) {
- checkCaller();
+ checkCallerOrSystemOrRoot();
mService.assertPackageMatchesCallingUid(callingPackage);
int callingUser = UserHandle.getCallingUserId();
@@ -167,7 +169,7 @@ class AppTaskImpl extends IAppTask.Stub {
@Override
public void setExcludeFromRecents(boolean exclude) {
- checkCaller();
+ checkCallerOrSystemOrRoot();
synchronized (mService.mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 4f015d8d77a0..5410dd8508f1 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -22,6 +22,7 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
@@ -1258,6 +1259,9 @@ public class AppTransition implements Dump {
"TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER"));
sFlagToString.add(new Pair<>(TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION,
"TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION"));
+ sFlagToString.add(new Pair<>(
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT,
+ "TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_WITH_IN_WINDOW_ANIMATIONS"));
sFlagToString.add(new Pair<>(TRANSIT_FLAG_APP_CRASHED,
"TRANSIT_FLAG_APP_CRASHED"));
sFlagToString.add(new Pair<>(TRANSIT_FLAG_OPEN_BEHIND,
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 3bda2e60334a..701fc9441acb 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -912,6 +912,15 @@ public class AppTransitionController {
canPromote = false;
}
+ // If the current window container is task and it have adjacent task, it means
+ // both tasks will open or close app toghther but we want get their opening or
+ // closing animation target independently so do not promote.
+ if (current.asTask() != null
+ && current.asTask().getAdjacentTaskFragment() != null
+ && current.asTask().getAdjacentTaskFragment().asTask() != null) {
+ canPromote = false;
+ }
+
// Find all siblings of the current WindowContainer in "candidates", move them into
// a separate list "siblings", and checks if an animation target can be promoted
// to its parent.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index dc441860f7c8..ed1bbf8e4b74 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -60,6 +60,7 @@ import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
+import static android.view.WindowManager.LayoutParams;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -4291,18 +4292,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Update Ime parent when IME insets leash created or the new IME layering target might
// updated from setImeLayeringTarget, which is the best time that default IME visibility
// has been settled down after IME control target changed.
- final boolean imeParentChanged =
- prevImeControlTarget != mImeControlTarget || forceUpdateImeParent;
- if (imeParentChanged) {
+ final boolean imeControlChanged = prevImeControlTarget != mImeControlTarget;
+ if (imeControlChanged || forceUpdateImeParent) {
updateImeParent();
}
final WindowState win = InsetsControlTarget.asWindowOrNull(mImeControlTarget);
final IBinder token = win != null ? win.mClient.asBinder() : null;
// Note: not allowed to call into IMMS with the WM lock held, hence the post.
- mWmService.mH.post(() ->
- InputMethodManagerInternal.get().reportImeControl(token, imeParentChanged)
- );
+ mWmService.mH.post(() -> InputMethodManagerInternal.get().reportImeControl(token));
}
void updateImeParent() {
@@ -4324,6 +4322,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// do a force update to make sure there is a layer set for the new parent.
assignRelativeLayerForIme(getSyncTransaction(), true /* forceUpdate */);
scheduleAnimation();
+
+ mWmService.mH.post(() -> InputMethodManagerInternal.get().onImeParentChanged());
}
}
@@ -4347,10 +4347,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
@VisibleForTesting
SurfaceControl computeImeParent() {
- if (mImeLayeringTarget != null && mImeInputTarget != null
- && mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord()) {
- // Do not change parent if the window hasn't requested IME.
- return null;
+ if (mImeLayeringTarget != null) {
+ // Ensure changing the IME parent when the layering target that may use IME has
+ // became to the input target for preventing IME flickers.
+ // Note that:
+ // 1) For the imeLayeringTarget that may not use IME but requires IME on top
+ // of it (e.g. an overlay window with NOT_FOCUSABLE|ALT_FOCUSABLE_IM flags), we allow
+ // it to re-parent the IME on top the display to keep the legacy behavior.
+ // 2) Even though the starting window won't use IME, the associated activity
+ // behind the starting window may request the input. If so, then we should still hold
+ // the IME parent change until the activity started the input.
+ boolean imeLayeringTargetMayUseIme =
+ LayoutParams.mayUseInputMethod(mImeLayeringTarget.mAttrs.flags)
+ || mImeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING;
+ if (imeLayeringTargetMayUseIme && mImeInputTarget != null
+ && mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord()) {
+ // Do not change parent if the window hasn't requested IME.
+ return null;
+ }
}
// Attach it to app if the target is part of an app and such app is covering the entire
// screen. If it's not covering the entire screen the IME might extend beyond the apps
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 76aa7f963aa6..fd0631320520 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -83,7 +83,7 @@ public class DisplayFrames {
final Rect safe = mDisplayCutoutSafe;
final DisplayCutout cutout = displayCutout.getDisplayCutout();
if (mDisplayWidth == info.logicalWidth && mDisplayHeight == info.logicalHeight
- && mRotation != info.rotation
+ && mRotation == info.rotation
&& state.getDisplayCutout().equals(cutout)
&& state.getRoundedCorners().equals(roundedCorners)
&& state.getPrivacyIndicatorBounds().equals(indicatorBounds)) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 2ebb59751634..f36dbfa2316e 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
@@ -32,6 +33,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
+import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
@@ -309,6 +311,10 @@ class KeyguardController {
if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS) != 0) {
result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
}
+ if ((keyguardGoingAwayFlags
+ & KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) {
+ result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
+ }
return result;
}
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
index 362ed3c380c5..9cbc1bdcbeeb 100644
--- a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -16,12 +16,11 @@
package com.android.server.wm;
+import android.content.ComponentName;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.util.function.pooled.PooledLambda;
import java.util.ArrayList;
@@ -39,8 +38,8 @@ import java.util.ArrayList;
*
* @see ActivityTaskManagerInternal#getLaunchObserverRegistry()
*/
-class LaunchObserverRegistryImpl implements
- ActivityMetricsLaunchObserverRegistry, ActivityMetricsLaunchObserver {
+class LaunchObserverRegistryImpl extends ActivityMetricsLaunchObserver implements
+ ActivityMetricsLaunchObserverRegistry {
private final ArrayList<ActivityMetricsLaunchObserver> mList = new ArrayList<>();
/**
@@ -79,45 +78,36 @@ class LaunchObserverRegistryImpl implements
}
@Override
- public void onIntentFailed() {
+ public void onIntentFailed(long id) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- LaunchObserverRegistryImpl::handleOnIntentFailed, this));
+ LaunchObserverRegistryImpl::handleOnIntentFailed, this, id));
}
@Override
- public void onActivityLaunched(
- @ActivityRecordProto byte[] activity,
- int temperature) {
+ public void onActivityLaunched(long id, ComponentName name, int temperature) {
mHandler.sendMessage(PooledLambda.obtainMessage(
LaunchObserverRegistryImpl::handleOnActivityLaunched,
- this, activity, temperature));
+ this, id, name, temperature));
}
@Override
- public void onActivityLaunchCancelled(
- @ActivityRecordProto byte[] activity) {
+ public void onActivityLaunchCancelled(long id) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- LaunchObserverRegistryImpl::handleOnActivityLaunchCancelled, this, activity));
+ LaunchObserverRegistryImpl::handleOnActivityLaunchCancelled, this, id));
}
@Override
- public void onActivityLaunchFinished(
- @ActivityRecordProto byte[] activity,
- long timestampNs) {
+ public void onActivityLaunchFinished(long id, ComponentName name, long timestampNs) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- LaunchObserverRegistryImpl::handleOnActivityLaunchFinished,
- this,
- activity,
- timestampNs));
+ LaunchObserverRegistryImpl::handleOnActivityLaunchFinished,
+ this, id, name, timestampNs));
}
@Override
- public void onReportFullyDrawn(@ActivityRecordProto byte[] activity, long timestampNs) {
+ public void onReportFullyDrawn(long id, long timestampNs) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- LaunchObserverRegistryImpl::handleOnReportFullyDrawn,
- this,
- activity,
- timestampNs));
+ LaunchObserverRegistryImpl::handleOnReportFullyDrawn,
+ this, id, timestampNs));
}
// Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be
@@ -135,53 +125,43 @@ class LaunchObserverRegistryImpl implements
private void handleOnIntentStarted(Intent intent, long timestampNs) {
// Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
for (int i = 0; i < mList.size(); i++) {
- ActivityMetricsLaunchObserver o = mList.get(i);
- o.onIntentStarted(intent, timestampNs);
+ mList.get(i).onIntentStarted(intent, timestampNs);
}
}
- private void handleOnIntentFailed() {
+ private void handleOnIntentFailed(long id) {
// Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
for (int i = 0; i < mList.size(); i++) {
- ActivityMetricsLaunchObserver o = mList.get(i);
- o.onIntentFailed();
+ mList.get(i).onIntentFailed(id);
}
}
- private void handleOnActivityLaunched(
- @ActivityRecordProto byte[] activity,
+ private void handleOnActivityLaunched(long id, ComponentName name,
@Temperature int temperature) {
// Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
for (int i = 0; i < mList.size(); i++) {
- ActivityMetricsLaunchObserver o = mList.get(i);
- o.onActivityLaunched(activity, temperature);
+ mList.get(i).onActivityLaunched(id, name, temperature);
}
}
- private void handleOnActivityLaunchCancelled(
- @ActivityRecordProto byte[] activity) {
+ private void handleOnActivityLaunchCancelled(long id) {
// Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
for (int i = 0; i < mList.size(); i++) {
- ActivityMetricsLaunchObserver o = mList.get(i);
- o.onActivityLaunchCancelled(activity);
+ mList.get(i).onActivityLaunchCancelled(id);
}
}
- private void handleOnActivityLaunchFinished(
- @ActivityRecordProto byte[] activity, long timestampNs) {
+ private void handleOnActivityLaunchFinished(long id, ComponentName name, long timestampNs) {
// Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
for (int i = 0; i < mList.size(); i++) {
- ActivityMetricsLaunchObserver o = mList.get(i);
- o.onActivityLaunchFinished(activity, timestampNs);
+ mList.get(i).onActivityLaunchFinished(id, name, timestampNs);
}
}
- private void handleOnReportFullyDrawn(
- @ActivityRecordProto byte[] activity, long timestampNs) {
+ private void handleOnReportFullyDrawn(long id, long timestampNs) {
// Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
for (int i = 0; i < mList.size(); i++) {
- ActivityMetricsLaunchObserver o = mList.get(i);
- o.onReportFullyDrawn(activity, timestampNs);
+ mList.get(i).onReportFullyDrawn(id, timestampNs);
}
}
}
diff --git a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
index 11a27c593d9e..3f6fb622481f 100644
--- a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
+++ b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
@@ -16,15 +16,11 @@
package com.android.server.wm;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-
import android.hardware.display.DisplayManagerInternal;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayInfo;
-import android.view.Surface;
import java.util.Set;
@@ -44,8 +40,7 @@ public class PossibleDisplayInfoMapper {
/**
* Map of all logical displays, indexed by logical display id.
- * Each logical display has multiple entries, one for each possible rotation and device
- * state.
+ * Each logical display has multiple entries, one for each device state.
*
* Emptied and re-calculated when a display is added, removed, or changed.
*/
@@ -57,8 +52,8 @@ public class PossibleDisplayInfoMapper {
/**
- * Returns, for the given displayId, a set of display infos. Set contains the possible rotations
- * for each supported device state.
+ * Returns, for the given displayId, a set of display infos. Set contains each supported device
+ * state.
*/
public Set<DisplayInfo> getPossibleDisplayInfos(int displayId) {
// Update display infos before returning, since any cached values would have been removed
@@ -73,13 +68,13 @@ public class PossibleDisplayInfoMapper {
}
/**
- * Updates the possible {@link DisplayInfo}s for the given display, by calculating the
- * DisplayInfo for each rotation across supported device states.
+ * Updates the possible {@link DisplayInfo}s for the given display, by saving the DisplayInfo
+ * across supported device states.
*/
public void updatePossibleDisplayInfos(int displayId) {
Set<DisplayInfo> displayInfos = mDisplayManagerInternal.getPossibleDisplayInfo(displayId);
if (DEBUG) {
- Slog.v(TAG, "updatePossibleDisplayInfos, calculate rotations for given DisplayInfo "
+ Slog.v(TAG, "updatePossibleDisplayInfos, given DisplayInfo "
+ displayInfos.size() + " on display " + displayId);
}
updateDisplayInfos(displayInfos);
@@ -99,40 +94,12 @@ public class PossibleDisplayInfoMapper {
private void updateDisplayInfos(Set<DisplayInfo> displayInfos) {
// Empty out cache before re-computing.
mDisplayInfos.clear();
- DisplayInfo[] originalDisplayInfos = new DisplayInfo[displayInfos.size()];
- displayInfos.toArray(originalDisplayInfos);
// Iterate over each logical display layout for the current state.
- Set<DisplayInfo> rotatedDisplayInfos;
- for (DisplayInfo di : originalDisplayInfos) {
- rotatedDisplayInfos = new ArraySet<>();
- // Calculate all possible rotations for each logical display.
- for (int rotation = ROTATION_0; rotation <= ROTATION_270; rotation++) {
- rotatedDisplayInfos.add(applyRotation(di, rotation));
- }
+ for (DisplayInfo di : displayInfos) {
// Combine all results under the logical display id.
Set<DisplayInfo> priorDisplayInfos = mDisplayInfos.get(di.displayId, new ArraySet<>());
- priorDisplayInfos.addAll(rotatedDisplayInfos);
+ priorDisplayInfos.add(di);
mDisplayInfos.put(di.displayId, priorDisplayInfos);
}
}
-
- private static DisplayInfo applyRotation(DisplayInfo displayInfo,
- @Surface.Rotation int rotation) {
- DisplayInfo updatedDisplayInfo = new DisplayInfo();
- updatedDisplayInfo.copyFrom(displayInfo);
- // Apply rotations before updating width and height
- updatedDisplayInfo.roundedCorners = updatedDisplayInfo.roundedCorners.rotate(rotation,
- updatedDisplayInfo.logicalWidth, updatedDisplayInfo.logicalHeight);
- updatedDisplayInfo.displayCutout =
- DisplayContent.calculateDisplayCutoutForRotationAndDisplaySizeUncached(
- updatedDisplayInfo.displayCutout, rotation, updatedDisplayInfo.logicalWidth,
- updatedDisplayInfo.logicalHeight).getDisplayCutout();
-
- updatedDisplayInfo.rotation = rotation;
- final int naturalWidth = updatedDisplayInfo.getNaturalWidth();
- final int naturalHeight = updatedDisplayInfo.getNaturalHeight();
- updatedDisplayInfo.logicalWidth = naturalWidth;
- updatedDisplayInfo.logicalHeight = naturalHeight;
- return updatedDisplayInfo;
- }
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ca4c450a4592..c0dff14e5de5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3382,7 +3382,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (record != null && record.isUid(uid)
&& Objects.equals(pkgName, record.packageName)
&& pPi.shouldShowNotificationDialogForTask(record.getTask().getTaskInfo(),
- pkgName, record.intent)) {
+ pkgName, record.launchedFromPackage, record.intent, record.getName())) {
validTaskId[0] = record.getTask().mTaskId;
return true;
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 65ae3fcb4c90..b4029d185b9f 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -219,7 +219,7 @@ class ScreenRotationAnimation {
// If hdr layers are on-screen, e.g. picture-in-picture mode, the screenshot of
// rotation animation is an sdr image containing tone-mapping hdr content, then
// disable dimming effect to get avoid of hdr content being dimmed during animation.
- t.setDimmingEnabled(mScreenshotLayer, false);
+ t.setDimmingEnabled(mScreenshotLayer, !screenshotBuffer.containsHdrLayers());
t.setLayer(mBackColorSurface, -1);
t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
t.setAlpha(mBackColorSurface, 1);
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 813e06fecf48..ccd018faf075 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -26,6 +26,7 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
+import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SNAPSHOT;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -114,7 +115,7 @@ public class StartingSurfaceController {
if (allowTaskSnapshot) {
parameter |= TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
}
- if (activityCreated) {
+ if (activityCreated || startingWindowType == STARTING_WINDOW_TYPE_SNAPSHOT) {
parameter |= TYPE_PARAMETER_ACTIVITY_CREATED;
}
if (isSolidColor) {
@@ -138,7 +139,6 @@ public class StartingSurfaceController {
final WindowState topFullscreenOpaqueWindow;
final Task task;
synchronized (mService.mGlobalLock) {
- final WindowState mainWindow = activity.findMainWindow();
task = activity.getTask();
if (task == null) {
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for activity="
@@ -153,9 +153,9 @@ public class StartingSurfaceController {
return null;
}
topFullscreenOpaqueWindow = topFullscreenActivity.getTopFullscreenOpaqueWindow();
- if (mainWindow == null || topFullscreenOpaqueWindow == null) {
- Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for activity="
- + activity);
+ if (topFullscreenOpaqueWindow == null) {
+ Slog.w(TAG, "TaskSnapshotSurface.create: no opaque window in "
+ + topFullscreenActivity);
return null;
}
if (topFullscreenActivity.getWindowConfiguration().getRotation()
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 17e1dd26a602..00e61171cb68 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -56,6 +56,7 @@ import static android.view.SurfaceControl.METADATA_TASK_ID;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
@@ -3525,10 +3526,13 @@ class Task extends TaskFragment {
mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY);
info.startingWindowTypeParameter = activity.mStartingData.mTypeParams;
- final WindowState mainWindow = activity.findMainWindow();
- if (mainWindow != null) {
- info.mainWindowLayoutParams = mainWindow.getAttrs();
- info.requestedVisibilities.set(mainWindow.getRequestedVisibilities());
+ if ((info.startingWindowTypeParameter
+ & StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED) != 0) {
+ final WindowState topMainWin = getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION);
+ if (topMainWin != null) {
+ info.mainWindowLayoutParams = topMainWin.getAttrs();
+ info.requestedVisibilities.set(topMainWin.getRequestedVisibilities());
+ }
}
// If the developer has persist a different configuration, we need to override it to the
// starting window because persisted configuration does not effect to Task.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a9b0f0dc28ad..48168a08a543 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8755,8 +8755,7 @@ public class WindowManagerService extends IWindowManager.Stub
return new ArrayList<>();
}
- // Retrieve the DisplayInfo for all possible rotations across all possible display
- // layouts.
+ // Retrieve the DisplayInfo across all possible display layouts.
return List.copyOf(mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId));
}
} finally {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c6288a7da26a..cd19f64ba12e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2464,7 +2464,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
dc.setImeLayeringTarget(null);
dc.computeImeTarget(true /* updateImeTarget */);
}
- if (dc.getImeInputTarget() == this) {
+ if (dc.getImeInputTarget() == this
+ && (mActivityRecord == null || !mActivityRecord.isRelaunching())) {
dc.updateImeInputAndControlTarget(null);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d1fac871fa2e..0a426eb80c54 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -13057,9 +13057,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (callerPackage == null) {
return false;
}
+
+ CallerIdentity caller = new CallerIdentity(callerUid, null, null);
if (isUserAffiliatedWithDevice(UserHandle.getUserId(callerUid))
&& (isActiveProfileOwner(callerUid)
- || isActiveDeviceOwner(callerUid))) {
+ || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller))) {
// device owner or a profile owner affiliated with the device owner
return true;
}
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index bbc28d78d6f2..d3222901db1e 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -274,36 +274,11 @@ public final class ProfcollectForwardingService extends SystemService {
}
}
- private class AppLaunchObserver implements ActivityMetricsLaunchObserver {
+ private class AppLaunchObserver extends ActivityMetricsLaunchObserver {
@Override
public void onIntentStarted(Intent intent, long timestampNanos) {
traceOnAppStart(intent.getPackage());
}
-
- @Override
- public void onIntentFailed() {
- // Ignored
- }
-
- @Override
- public void onActivityLaunched(byte[] activity, int temperature) {
- // Ignored
- }
-
- @Override
- public void onActivityLaunchCancelled(byte[] abortingActivity) {
- // Ignored
- }
-
- @Override
- public void onActivityLaunchFinished(byte[] finalActivity, long timestampNanos) {
- // Ignored
- }
-
- @Override
- public void onReportFullyDrawn(byte[] activity, long timestampNanos) {
- // Ignored
- }
}
private void registerOTAObserver() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 529def3697cd..8461b39f8899 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -2758,7 +2758,7 @@ public class AlarmManagerServiceTest {
mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), false);
// No permission revoked.
- verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(anyInt(), anyString(),
+ verify(mService, never()).removeExactAlarmsOnPermissionRevoked(anyInt(), anyString(),
anyBoolean());
// Permission got granted only for (appId1, userId2).
@@ -2813,14 +2813,14 @@ public class AlarmManagerServiceTest {
mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), true);
// Permission got revoked only for (appId1, userId2)
- verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(
+ verify(mService, never()).removeExactAlarmsOnPermissionRevoked(
eq(UserHandle.getUid(userId1, appId1)), eq(packages[0]), eq(true));
- verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(
+ verify(mService, never()).removeExactAlarmsOnPermissionRevoked(
eq(UserHandle.getUid(userId1, appId2)), eq(packages[1]), eq(true));
- verify(mService, never()).removeExactAlarmsOnPermissionRevokedLocked(
+ verify(mService, never()).removeExactAlarmsOnPermissionRevoked(
eq(UserHandle.getUid(userId2, appId2)), eq(packages[1]), eq(true));
- verify(mService).removeExactAlarmsOnPermissionRevokedLocked(
+ verify(mService).removeExactAlarmsOnPermissionRevoked(
eq(UserHandle.getUid(userId2, appId1)), eq(packages[0]), eq(true));
}
@@ -2833,7 +2833,7 @@ public class AlarmManagerServiceTest {
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
assertAndHandleMessageSync(REMOVE_EXACT_ALARMS);
- verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID,
+ verify(mService).removeExactAlarmsOnPermissionRevoked(TEST_CALLING_UID,
TEST_CALLING_PACKAGE, true);
}
@@ -2919,7 +2919,7 @@ public class AlarmManagerServiceTest {
null);
assertEquals(6, mService.mAlarmStore.size());
- mService.removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, TEST_CALLING_PACKAGE,
+ mService.removeExactAlarmsOnPermissionRevoked(TEST_CALLING_UID, TEST_CALLING_PACKAGE,
true);
final ArrayList<Alarm> remaining = mService.mAlarmStore.asList();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index c747a5fd982b..2f68306e9ba1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -191,6 +191,8 @@ public class MockingOomAdjusterTests {
mock(ActivityManagerService.LocalService.class));
setFieldValue(ActivityManagerService.class, sService, "mBatteryStatsService",
mock(BatteryStatsService.class));
+ setFieldValue(ActivityManagerService.class, sService, "mInjector",
+ new ActivityManagerService.Injector(sContext));
doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager();
doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(any(String.class));
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index e3be3a792549..16df5deb2e5c 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -19,9 +19,6 @@ android_test {
"src/**/*.java",
"src/**/*.kt",
- "aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl",
- "aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl",
-
"test-apps/JobTestApp/src/**/*.java",
"test-apps/SuspendTestApp/src/**/*.java",
@@ -67,10 +64,6 @@ android_test {
"ActivityContext",
],
- aidl: {
- local_include_dirs: ["aidl"],
- },
-
libs: [
"android.hardware.power-V1-java",
"android.hardware.tv.cec-V1.0-java",
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index bb3eb81df6ed..158bd39a4fd0 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -28,7 +28,6 @@
<option name="install-arg" value="-t" />
<option name="test-file-name" value="FrameworksServicesTests.apk" />
<option name="test-file-name" value="JobTestApp.apk" />
- <option name="test-file-name" value="ConnTestApp.apk" />
<option name="test-file-name" value="SuspendTestApp.apk" />
<option name="test-file-name" value="SimpleServiceTestApp1.apk" />
<option name="test-file-name" value="SimpleServiceTestApp2.apk" />
diff --git a/services/tests/servicestests/aidl/Android.bp b/services/tests/servicestests/aidl/Android.bp
deleted file mode 100644
index 678053192e82..000000000000
--- a/services/tests/servicestests/aidl/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-java_library {
- name: "servicestests-aidl",
- sdk_version: "current",
- srcs: [
- "com/android/servicestests/aidl/INetworkStateObserver.aidl",
- "com/android/servicestests/aidl/ICmdReceiverService.aidl",
- ],
-}
diff --git a/services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl b/services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl
deleted file mode 100644
index ca9fc4c439d2..000000000000
--- a/services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2017 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.servicestests.aidl;
-
-oneway interface INetworkStateObserver {
- /**
- * {@param resultData} will be in the format
- * NetinfoState|NetinfoDetailedState|RealConnectionCheck|RealConnectionCheckDetails|Netinfo.
- * For detailed info, see
- * servicestests/test-apps/ConnTestApp/.../ConnTestActivity#checkNetworkStatus
- */
- void onNetworkStateChecked(String resultData);
-} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
index 5c91b8b4717f..d1390c68e130 100644
--- a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
@@ -90,6 +90,16 @@ public class DropboxRateLimiterTest {
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
assertEquals(2,
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
+
+ // After 11 seconds the rate limiting buffer will be cleared and rate limiting will stop.
+ mClock.setOffsetMillis(11000);
+
+ // The first call after rate limiting stops will still return the number of dropped events.
+ assertEquals(2,
+ mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
+ // The next call should show that the dropped event counter was reset.
+ assertEquals(0,
+ mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
}
private static class TestClock implements DropboxRateLimiter.Clock {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index bf3c7c3e05fb..d5612e7a27b2 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -27,6 +27,7 @@ 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.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -38,6 +39,7 @@ import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.display.BrightnessConfiguration;
@@ -72,6 +74,7 @@ import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
@@ -93,6 +96,7 @@ import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.time.Duration;
@@ -203,6 +207,10 @@ public class DisplayManagerServiceTest {
@Test
public void testCreateVirtualDisplay_sentToInputManager() {
+ // This is to update the display device config such that DisplayManagerService can ignore
+ // the usage of SensorManager, which is available only after the PowerManagerService
+ // is ready.
+ resetConfigToIgnoreSensorManager(mContext);
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
@@ -275,6 +283,10 @@ public class DisplayManagerServiceTest {
@Test
public void testPhysicalViewports() {
+ // This is to update the display device config such that DisplayManagerService can ignore
+ // the usage of SensorManager, which is available only after the PowerManagerService
+ // is ready.
+ resetConfigToIgnoreSensorManager(mContext);
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
@@ -1343,6 +1355,20 @@ public class DisplayManagerServiceTest {
}
}
+ private void resetConfigToIgnoreSensorManager(Context context) {
+ final Resources res = Mockito.spy(context.getResources());
+ doReturn(new int[]{-1}).when(res).getIntArray(R.array
+ .config_ambientThresholdsOfPeakRefreshRate);
+ doReturn(new int[]{-1}).when(res).getIntArray(R.array
+ .config_brightnessThresholdsOfPeakRefreshRate);
+ doReturn(new int[]{-1}).when(res).getIntArray(R.array
+ .config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
+ doReturn(new int[]{-1}).when(res).getIntArray(R.array
+ .config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
+
+ when(context.getResources()).thenReturn(res);
+ }
+
private class FakeDisplayManagerCallback extends IDisplayManagerCallback.Stub {
int mDisplayId;
boolean mDisplayAddedCalled = false;
diff --git a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
deleted file mode 100644
index 25b41db1aea3..000000000000
--- a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * Copyright (C) 2017 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.net;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.os.BatteryManager;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.SystemClock;
-import android.support.test.uiautomator.UiDevice;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.servicestests.aidl.ICmdReceiverService;
-import com.android.servicestests.aidl.INetworkStateObserver;
-
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for verifying network availability on activity start.
- *
- * To run the tests, use
- *
- * runtest -c com.android.server.net.ConnOnActivityStartTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.net.ConnOnActivityStartTest -w \
- * com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class ConnOnActivityStartTest {
- private static final String TAG = ConnOnActivityStartTest.class.getSimpleName();
-
- private static final String TEST_PKG = "com.android.servicestests.apps.conntestapp";
- private static final String TEST_ACTIVITY_CLASS = TEST_PKG + ".ConnTestActivity";
- private static final String TEST_SERVICE_CLASS = TEST_PKG + ".CmdReceiverService";
-
- private static final String EXTRA_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
-
- private static final long BATTERY_OFF_TIMEOUT_MS = 2000; // 2 sec
- private static final long BATTERY_OFF_CHECK_INTERVAL_MS = 200; // 0.2 sec
-
- private static final long NETWORK_CHECK_TIMEOUT_MS = 4000; // 4 sec
-
- private static final long SCREEN_ON_DELAY_MS = 2000; // 2 sec
-
- private static final long BIND_SERVICE_TIMEOUT_SEC = 4;
-
- private static final int REPEAT_TEST_COUNT = 5;
-
- private static Context mContext;
- private static UiDevice mUiDevice;
- private static int mTestPkgUid;
- private static BatteryManager mBatteryManager;
-
- private static ServiceConnection mServiceConnection;
- private static ICmdReceiverService mCmdReceiverService;
-
- @BeforeClass
- public static void setUpOnce() throws Exception {
- mContext = InstrumentationRegistry.getContext();
- mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-
- mContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG,
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
- mTestPkgUid = mContext.getPackageManager().getPackageUid(TEST_PKG, 0);
-
- mBatteryManager = (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE);
- bindService();
- }
-
- @AfterClass
- public static void tearDownOnce() throws Exception {
- batteryReset();
- unbindService();
- }
-
- private static void bindService() throws Exception {
- final CountDownLatch bindLatch = new CountDownLatch(1);
- mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.i(TAG, "Service connected");
- mCmdReceiverService = ICmdReceiverService.Stub.asInterface(service);
- bindLatch.countDown();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.i(TAG, "Service disconnected");
- }
- };
- final Intent intent = new Intent()
- .setComponent(new ComponentName(TEST_PKG, TEST_SERVICE_CLASS));
- // Needs to use BIND_ALLOW_OOM_MANAGEMENT and BIND_NOT_FOREGROUND so that the test app
- // does not run in the same process state as this app.
- mContext.bindService(intent, mServiceConnection,
- Context.BIND_AUTO_CREATE
- | Context.BIND_ALLOW_OOM_MANAGEMENT
- | Context.BIND_NOT_FOREGROUND);
- if (!bindLatch.await(BIND_SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS)) {
- fail("Timed out waiting for the service to bind in " + mTestPkgUid);
- }
- }
-
- private static void unbindService() {
- if (mCmdReceiverService != null) {
- mContext.unbindService(mServiceConnection);
- }
- }
-
- @Test
- public void testStartActivity_batterySaver() throws Exception {
- setBatterySaverMode(true);
- try {
- testConnOnActivityStart("testStartActivity_batterySaver");
- } finally {
- setBatterySaverMode(false);
- }
- }
-
- @Test
- public void testStartActivity_dataSaver() throws Exception {
- setDataSaverMode(true);
- try {
- testConnOnActivityStart("testStartActivity_dataSaver");
- } finally {
- setDataSaverMode(false);
- }
- }
-
- @Test
- public void testStartActivity_dozeMode() throws Exception {
- setDozeMode(true);
- try {
- testConnOnActivityStart("testStartActivity_dozeMode");
- } finally {
- setDozeMode(false);
- }
- }
-
- @Test
- public void testStartActivity_appStandby() throws Exception {
- try{
- turnBatteryOn();
- setAppIdle(true);
- turnScreenOn();
- startActivityAndCheckNetworkAccess();
- } finally {
- turnBatteryOff();
- finishActivity();
- setAppIdle(false);
- }
- }
-
- @Test
- public void testStartActivity_backgroundRestrict() throws Exception {
- updateRestrictBackgroundBlacklist(true);
- try {
- testConnOnActivityStart("testStartActivity_backgroundRestrict");
- } finally {
- updateRestrictBackgroundBlacklist(false);
- }
- }
-
- private void testConnOnActivityStart(String testName) throws Exception {
- for (int i = 1; i <= REPEAT_TEST_COUNT; ++i) {
- try {
- Log.d(TAG, testName + " Start #" + i);
- turnScreenOn();
- startActivityAndCheckNetworkAccess();
- } finally {
- finishActivity();
- Log.d(TAG, testName + " end #" + i);
- }
- }
- }
-
- // TODO: Some of these methods are also used in CTS, so instead of duplicating code,
- // create a static library which can be used by both servicestests and cts.
- private void setBatterySaverMode(boolean enabled) throws Exception {
- if (enabled) {
- turnBatteryOn();
- executeCommand("settings put global low_power 1");
- } else {
- executeCommand("settings put global low_power 0");
- turnBatteryOff();
- }
- final String result = executeCommand("settings get global low_power");
- assertEquals(enabled ? "1" : "0", result);
- }
-
- private void setDataSaverMode(boolean enabled) throws Exception {
- executeCommand("cmd netpolicy set restrict-background " + enabled);
- final String output = executeCommand("cmd netpolicy get restrict-background");
- final String expectedSuffix = enabled ? "enabled" : "disabled";
- assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
- output.endsWith(expectedSuffix));
- }
-
- private void setDozeMode(boolean enabled) throws Exception {
- if (enabled) {
- turnBatteryOn();
- turnScreenOff();
- executeCommand("dumpsys deviceidle force-idle deep");
- } else {
- turnScreenOn();
- turnBatteryOff();
- executeCommand("dumpsys deviceidle unforce");
- }
- assertDelayedCommandResult("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE",
- 5 /* maxTries */, 500 /* napTimeMs */);
- }
-
- private void setAppIdle(boolean enabled) throws Exception {
- executeCommand("am set-inactive " + TEST_PKG + " " + enabled);
- assertDelayedCommandResult("am get-inactive " + TEST_PKG, "Idle=" + enabled,
- 15 /* maxTries */, 2000 /* napTimeMs */);
- }
-
- private void updateRestrictBackgroundBlacklist(boolean add) throws Exception {
- if (add) {
- executeCommand("cmd netpolicy add restrict-background-blacklist " + mTestPkgUid);
- } else {
- executeCommand("cmd netpolicy remove restrict-background-blacklist " + mTestPkgUid);
- }
- assertRestrictBackground("restrict-background-blacklist", mTestPkgUid, add);
- }
-
- private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception {
- final int maxTries = 5;
- boolean actual = false;
- final String expectedUid = Integer.toString(uid);
- String uids = "";
- for (int i = 1; i <= maxTries; i++) {
- final String output = executeCommand("cmd netpolicy list " + list);
- uids = output.split(":")[1];
- for (String candidate : uids.split(" ")) {
- actual = candidate.trim().equals(expectedUid);
- if (expected == actual) {
- return;
- }
- }
- Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected "
- + expected + ", got " + actual + "); sleeping 1s before polling again");
- SystemClock.sleep(1000);
- }
- fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual
- + ". Full list: " + uids);
- }
-
- private void turnBatteryOn() throws Exception {
- executeCommand("cmd battery unplug");
- executeCommand("cmd battery set status " + BatteryManager.BATTERY_STATUS_NOT_CHARGING);
- assertBatteryOn();
- }
-
- private void assertBatteryOn() throws Exception {
- final long endTime = SystemClock.uptimeMillis() + BATTERY_OFF_TIMEOUT_MS;
- while (mBatteryManager.isCharging() && SystemClock.uptimeMillis() < endTime) {
- SystemClock.sleep(BATTERY_OFF_CHECK_INTERVAL_MS);
- }
- assertFalse("Power should be disconnected", mBatteryManager.isCharging());
- }
-
- private void turnBatteryOff() throws Exception {
- executeCommand("cmd battery set ac " + BatteryManager.BATTERY_PLUGGED_AC);
- executeCommand("cmd battery set status " + BatteryManager.BATTERY_STATUS_CHARGING);
- }
-
- private static void batteryReset() throws Exception {
- executeCommand("cmd battery reset");
- }
-
- private void turnScreenOff() throws Exception {
- executeCommand("input keyevent KEYCODE_SLEEP");
- }
-
- private void turnScreenOn() throws Exception {
- executeCommand("input keyevent KEYCODE_WAKEUP");
- executeCommand("wm dismiss-keyguard");
- // Wait for screen-on state to propagate through the system.
- SystemClock.sleep(SCREEN_ON_DELAY_MS);
- }
-
- private static String executeCommand(String cmd) throws IOException {
- final String result = executeSilentCommand(cmd);
- Log.d(TAG, String.format("Result for '%s': %s", cmd, result));
- return result;
- }
-
- private static String executeSilentCommand(String cmd) throws IOException {
- return mUiDevice.executeShellCommand(cmd).trim();
- }
-
- private void assertDelayedCommandResult(String cmd, String expectedResult,
- int maxTries, int napTimeMs) throws Exception {
- String result = "";
- for (int i = 1; i <= maxTries; ++i) {
- result = executeCommand(cmd);
- if (expectedResult.equals(result)) {
- return;
- }
- Log.v(TAG, "Command '" + cmd + "' returned '" + result + " instead of '"
- + expectedResult + "' on attempt #" + i
- + "; sleeping " + napTimeMs + "ms before trying again");
- SystemClock.sleep(napTimeMs);
- }
- fail("Command '" + cmd + "' did not return '" + expectedResult + "' after "
- + maxTries + " attempts. Last result: '" + result + "'");
- }
-
- private void startActivityAndCheckNetworkAccess() throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- final Intent launchIntent = new Intent().setComponent(
- new ComponentName(TEST_PKG, TEST_ACTIVITY_CLASS));
- final Bundle extras = new Bundle();
- final String[] errors = new String[] {null};
- extras.putBinder(EXTRA_NETWORK_STATE_OBSERVER, new INetworkStateObserver.Stub() {
- @Override
- public void onNetworkStateChecked(String resultData) {
- errors[0] = resultData;
- latch.countDown();
- }
- });
- launchIntent.putExtras(extras)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(launchIntent);
- if (latch.await(NETWORK_CHECK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- if (errors[0] != null) {
- fail("Network not available for test app " + mTestPkgUid + ". " + errors[0]);
- }
- } else {
- fail("Timed out waiting for network availability status from test app " + mTestPkgUid);
- }
- }
-
- private static void fail(String msg) throws Exception {
- dumpOnFailure();
- Assert.fail(msg);
- }
-
- private static void dumpOnFailure() throws Exception {
- dump("network_management");
- dump("netpolicy");
- dumpUsageStats();
- }
-
- private static void dumpUsageStats() throws Exception {
- final String output = executeSilentCommand("dumpsys usagestats");
- final StringBuilder sb = new StringBuilder();
- final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
- splitter.setString(output);
- String str;
- while (splitter.hasNext()) {
- str = splitter.next();
- if (str.contains("package=") && !str.contains(TEST_PKG)) {
- continue;
- }
- if (str.trim().startsWith("config=") || str.trim().startsWith("time=")) {
- continue;
- }
- sb.append(str).append('\n');
- }
- dump("usagestats", sb.toString());
- }
-
- private static void dump(String service) throws Exception {
- dump(service, executeSilentCommand("dumpsys " + service));
- }
-
- private static void dump(String service, String dump) throws Exception {
- Log.d(TAG, ">>> Begin dump " + service);
- Log.printlns(Log.LOG_ID_MAIN, Log.DEBUG, TAG, dump, null);
- Log.d(TAG, "<<< End dump " + service);
- }
-
- private void finishActivity() throws Exception {
- mCmdReceiverService.finishActivity();
- }
-} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index fdf9354747a0..40943774c0af 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -1975,7 +1975,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
if (si == null) {
return null;
}
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
return new File(si.getBitmapPath()).getName();
}
@@ -1984,7 +1984,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
if (si == null) {
return null;
}
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
return new File(si.getBitmapPath()).getAbsolutePath();
}
@@ -2139,7 +2139,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
}
protected boolean bitmapDirectoryExists(String packageName, int userId) {
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
final File path = new File(mService.getUserBitmapFilePath(userId), packageName);
return path.isDirectory();
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 867890f938ba..411b52155abb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -1040,7 +1040,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
dumpsysOnLogcat();
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
// Check files and directories.
// Package 3 has no bitmaps, so we don't create a directory.
assertBitmapDirectories(USER_0, CALLING_PACKAGE_1, CALLING_PACKAGE_2);
@@ -1096,7 +1096,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
makeFile(mService.getUserBitmapFilePath(USER_10), CALLING_PACKAGE_2, "3").createNewFile();
makeFile(mService.getUserBitmapFilePath(USER_10), CALLING_PACKAGE_2, "4").createNewFile();
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
assertBitmapDirectories(USER_0, CALLING_PACKAGE_1, CALLING_PACKAGE_2, CALLING_PACKAGE_3,
"a.b.c", "d.e.f");
@@ -1111,7 +1111,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// The below check is the same as above, except this time USER_0 use the CALLING_PACKAGE_3
// directory.
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
assertBitmapDirectories(USER_0, CALLING_PACKAGE_1, CALLING_PACKAGE_2, CALLING_PACKAGE_3);
assertBitmapDirectories(USER_10, CALLING_PACKAGE_1, CALLING_PACKAGE_2);
@@ -1390,7 +1390,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.setIcon(Icon.createWithContentUri("test_uri"))
.build()
)));
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
assertWith(getCallerShortcuts())
.forShortcutWithId("s1", si -> {
assertTrue(si.hasIconUri());
@@ -1402,13 +1402,13 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
.build()
)));
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
assertWith(getCallerShortcuts())
.forShortcutWithId("s1", si -> {
assertTrue(si.hasIconResource());
assertEquals(R.drawable.black_32x32, si.getIconResourceId());
});
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
@@ -1419,7 +1419,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
getTestContext().getResources(), R.drawable.black_64x64)))
.build()
)));
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
assertWith(getCallerShortcuts())
.forShortcutWithId("s1", si -> {
assertTrue(si.hasIconFile());
@@ -1437,7 +1437,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
getTestContext().getResources(), R.drawable.black_64x64)))
.build()
)));
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
assertWith(getCallerShortcuts())
.forShortcutWithId("s1", si -> {
assertTrue(si.hasIconFile());
@@ -1451,7 +1451,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
.build()
)));
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
assertWith(getCallerShortcuts())
.forShortcutWithId("s1", si -> {
assertTrue(si.hasIconResource());
@@ -1463,7 +1463,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.setIcon(Icon.createWithContentUri("test_uri"))
.build()
)));
- mService.waitForBitmapSavesForTest();
+ mService.waitForBitmapSaves();
assertWith(getCallerShortcuts())
.forShortcutWithId("s1", si -> {
assertTrue(si.hasIconUri());
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
deleted file mode 100644
index 0731e2ca1f41..000000000000
--- a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
- name: "ConnTestApp",
-
- test_suites: ["device-tests"],
-
- static_libs: ["servicestests-aidl"],
- srcs: ["**/*.java"],
-
- platform_apis: true,
- certificate: "platform",
- dex_preopt: {
- enabled: false,
- },
- optimize: {
- enabled: false,
- },
-}
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml
deleted file mode 100644
index 201cd05052ea..000000000000
--- a/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.servicestests.apps.conntestapp">
-
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
-
- <application>
- <activity android:name=".ConnTestActivity"
- android:exported="true" />
- <service android:name=".CmdReceiverService"
- android:exported="true" />
- </application>
-
-</manifest> \ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/OWNERS b/services/tests/servicestests/test-apps/ConnTestApp/OWNERS
deleted file mode 100644
index aa87958f1d53..000000000000
--- a/services/tests/servicestests/test-apps/ConnTestApp/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/net/OWNERS
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/CmdReceiverService.java b/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/CmdReceiverService.java
deleted file mode 100644
index 6130f3a3fc76..000000000000
--- a/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/CmdReceiverService.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 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.servicestests.apps.conntestapp;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-import com.android.servicestests.aidl.ICmdReceiverService;
-
-public class CmdReceiverService extends Service {
- private ICmdReceiverService.Stub mBinder = new ICmdReceiverService.Stub() {
- @Override
- public void finishActivity() {
- ConnTestActivity.finishSelf();
- }
- };
-
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-} \ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java b/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java
deleted file mode 100644
index f8d1d03cd7a6..000000000000
--- a/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2017 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.servicestests.apps.conntestapp;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.INetworkPolicyManager;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.INetworkManagementService;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.servicestests.aidl.INetworkStateObserver;
-
-public class ConnTestActivity extends Activity {
- private static final String TAG = ConnTestActivity.class.getSimpleName();
-
- private static final String TEST_PKG = ConnTestActivity.class.getPackage().getName();
- private static final String ACTION_FINISH_ACTIVITY = TEST_PKG + ".FINISH";
- private static final String EXTRA_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
-
- private static final Object INSTANCE_LOCK = new Object();
- @GuardedBy("instanceLock")
- private static Activity sInstance;
-
- private BroadcastReceiver finishCommandReceiver = null;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- synchronized (INSTANCE_LOCK) {
- sInstance = this;
- }
- Log.i(TAG, "onCreate called");
-
- notifyNetworkStateObserver();
-
- finishCommandReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.i(TAG, "finish command received");
- ConnTestActivity.this.finish();
- }
- };
- registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY));
- }
-
- @Override
- public void onResume() {
- super.onResume();
- Log.i(TAG, "onResume called");
- }
-
- @Override
- public void onStop() {
- Log.i(TAG, "onStop called");
- if (finishCommandReceiver != null) {
- unregisterReceiver(finishCommandReceiver);
- finishCommandReceiver = null;
- }
- super.onStop();
- }
-
- @Override
- public void finish() {
- synchronized (INSTANCE_LOCK) {
- sInstance = null;
- }
- Log.i(TAG, "finish called");
- super.finish();
- }
-
- public static void finishSelf() {
- synchronized (INSTANCE_LOCK) {
- if (sInstance != null) {
- sInstance.finish();
- }
- }
- }
-
- private void notifyNetworkStateObserver() {
- if (getIntent() == null) {
- return;
- }
-
- final Bundle extras = getIntent().getExtras();
- if (extras == null) {
- return;
- }
- final INetworkStateObserver observer = INetworkStateObserver.Stub.asInterface(
- extras.getBinder(EXTRA_NETWORK_STATE_OBSERVER));
- if (observer != null) {
- AsyncTask.execute(() -> {
- try {
- observer.onNetworkStateChecked(checkNetworkStatus());
- } catch (RemoteException e) {
- Log.e(TAG, "Error occured while notifying the observer: " + e);
- }
- });
- }
- }
-
- /**
- * Checks whether the network is restricted.
- *
- * @return null if network is not restricted, otherwise an error message.
- */
- private String checkNetworkStatus() {
- final INetworkManagementService nms = INetworkManagementService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
- final INetworkPolicyManager npms = INetworkPolicyManager.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
- try {
- final boolean restrictedByFwRules = nms.isNetworkRestricted(Process.myUid());
- final boolean restrictedByUidRules = npms.isUidNetworkingBlocked(Process.myUid(), true);
- if (restrictedByFwRules || restrictedByUidRules) {
- return "Network is restricted by fwRules: " + restrictedByFwRules
- + " and uidRules: " + restrictedByUidRules;
- }
- return null;
- } catch (RemoteException e) {
- return "Error talking to system server: " + e;
- }
- }
-} \ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 7689e08bc3f3..2fea2284ff2a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -27,10 +27,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMor
import static com.google.common.truth.Truth.assertWithMessage;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
@@ -40,24 +38,22 @@ import android.app.ActivityOptions;
import android.app.ActivityOptions.SourceInfo;
import android.app.WaitResult;
import android.app.WindowConfiguration;
+import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
+import android.util.Log;
import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
-import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
-
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
+import org.mockito.ArgumentCaptor;
-import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.function.ToIntFunction;
@@ -75,13 +71,14 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
private ActivityMetricsLogger mActivityMetricsLogger;
private ActivityMetricsLogger.LaunchingState mLaunchingState;
private ActivityMetricsLaunchObserver mLaunchObserver;
- private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
private ActivityRecord mTrampolineActivity;
private ActivityRecord mTopActivity;
private ActivityOptions mActivityOptions;
private boolean mLaunchTopByTrampoline;
private boolean mNewActivityCreated = true;
+ private long mExpectedStartedId;
+ private final ArrayMap<ComponentName, Long> mLastLaunchedIds = new ArrayMap<>();
@Before
public void setUpAMLO() {
@@ -89,9 +86,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
// ActivityTaskSupervisor always creates its own instance of ActivityMetricsLogger.
mActivityMetricsLogger = mSupervisor.getActivityMetricsLogger();
-
- mLaunchObserverRegistry = mActivityMetricsLogger.getLaunchObserverRegistry();
- mLaunchObserverRegistry.registerLaunchObserver(mLaunchObserver);
+ mActivityMetricsLogger.getLaunchObserverRegistry().registerLaunchObserver(mLaunchObserver);
// Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
// This seems to be the easiest way to create an ActivityRecord.
@@ -107,65 +102,70 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
mTrampolineActivity.mVisibleRequested = false;
}
- @After
- public void tearDownAMLO() {
- if (mLaunchObserverRegistry != null) { // Don't NPE if setUp failed.
- mLaunchObserverRegistry.unregisterLaunchObserver(mLaunchObserver);
- }
+ private <T> T verifyAsync(T mock) {
+ // With WindowTestRunner, all test methods are inside WM lock, so we have to unblock any
+ // messages that are waiting for the lock.
+ waitHandlerIdle(mAtm.mH);
+ // AMLO callbacks happen on a separate thread than AML calls, so we need to use a timeout.
+ return verify(mock, timeout(TIMEOUT_MS));
}
- static class ActivityRecordMatcher implements ArgumentMatcher</*@ActivityRecordProto*/ byte[]> {
- private final @ActivityRecordProto byte[] mExpected;
-
- public ActivityRecordMatcher(ActivityRecord activityRecord) {
- mExpected = activityRecordToProto(activityRecord);
- }
+ private void verifyOnActivityLaunched(ActivityRecord activity) {
+ final ArgumentCaptor<Long> idCaptor = ArgumentCaptor.forClass(Long.class);
+ verifyAsync(mLaunchObserver).onActivityLaunched(idCaptor.capture(),
+ eq(activity.mActivityComponent), anyInt());
+ final long id = idCaptor.getValue();
+ setExpectedStartedId(id, activity);
+ mLastLaunchedIds.put(activity.mActivityComponent, id);
+ }
- public boolean matches(@ActivityRecordProto byte[] actual) {
- return Arrays.equals(mExpected, actual);
- }
+ private void verifyOnActivityLaunchFinished(ActivityRecord activity) {
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eq(mExpectedStartedId),
+ eq(activity.mActivityComponent), anyLong());
}
- static @ActivityRecordProto byte[] activityRecordToProto(ActivityRecord record) {
- return ActivityMetricsLogger.convertActivityRecordToProto(record);
+ private void setExpectedStartedId(long id, Object reason) {
+ mExpectedStartedId = id;
+ Log.i("AMLTest", "setExpectedStartedId=" + id + " from " + reason);
}
- static @ActivityRecordProto byte[] eqProto(ActivityRecord record) {
- return argThat(new ActivityRecordMatcher(record));
+ private void setLastExpectedStartedId(ActivityRecord r) {
+ setExpectedStartedId(getLastStartedId(r), r);
}
- private <T> T verifyAsync(T mock) {
- // With WindowTestRunner, all test methods are inside WM lock, so we have to unblock any
- // messages that are waiting for the lock.
- waitHandlerIdle(mAtm.mH);
- // AMLO callbacks happen on a separate thread than AML calls, so we need to use a timeout.
- return verify(mock, timeout(TIMEOUT_MS));
+ private long getLastStartedId(ActivityRecord r) {
+ final Long id = mLastLaunchedIds.get(r.mActivityComponent);
+ return id != null ? id : -1;
}
- private void verifyOnActivityLaunchFinished(ActivityRecord activity) {
- verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(activity), anyLong());
+ private long eqLastStartedId(ActivityRecord r) {
+ return eq(getLastStartedId(r));
}
- private void onIntentStarted(Intent intent) {
+ private long onIntentStarted(Intent intent) {
notifyActivityLaunching(intent);
+ long timestamp = -1;
// If it is launching top activity from trampoline activity, the observer shouldn't receive
// onActivityLaunched because the activities should belong to the same transition.
if (!mLaunchTopByTrampoline) {
- verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
+ final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
+ verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), captor.capture());
+ timestamp = captor.getValue();
}
verifyNoMoreInteractions(mLaunchObserver);
+ return timestamp;
}
@Test
public void testOnIntentFailed() {
- onIntentStarted(new Intent("testOnIntentFailed"));
+ final long id = onIntentStarted(new Intent("testOnIntentFailed"));
// Bringing an intent that's already running 'to front' is not considered
// as an ACTIVITY_LAUNCHED state transition.
notifyActivityLaunched(START_TASK_TO_FRONT, null /* launchedActivity */);
- verifyAsync(mLaunchObserver).onIntentFailed();
+ verifyAsync(mLaunchObserver).onIntentFailed(eq(id));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -208,9 +208,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
private void onActivityLaunched(ActivityRecord activity) {
onIntentStarted(activity.intent);
- notifyActivityLaunched(START_SUCCESS, activity);
+ notifyAndVerifyActivityLaunched(activity);
- verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(activity), anyInt());
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -235,7 +234,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
// Cannot time already-visible activities.
notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
- verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity));
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqLastStartedId(mTopActivity));
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -250,33 +249,33 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
.build();
notifyActivityLaunching(noDrawnActivity.intent);
- notifyActivityLaunched(START_SUCCESS, noDrawnActivity);
+ notifyAndVerifyActivityLaunched(noDrawnActivity);
noDrawnActivity.mVisibleRequested = false;
mActivityMetricsLogger.notifyVisibilityChanged(noDrawnActivity);
- verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(noDrawnActivity));
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqLastStartedId(noDrawnActivity));
// If an activity is removed immediately before visibility update, it should cancel too.
final ActivityRecord removedImm = new ActivityBuilder(mAtm).setCreateTask(true).build();
clearInvocations(mLaunchObserver);
onActivityLaunched(removedImm);
removedImm.removeImmediately();
- // Verify any() instead of proto because the field of record may be changed.
- verifyAsync(mLaunchObserver).onActivityLaunchCancelled(any());
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqLastStartedId(removedImm));
}
@Test
public void testOnActivityLaunchWhileSleeping() {
notifyActivityLaunching(mTrampolineActivity.intent);
- notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
+ notifyAndVerifyActivityLaunched(mTrampolineActivity);
doReturn(true).when(mTrampolineActivity.mDisplayContent).isSleeping();
mTrampolineActivity.setState(ActivityRecord.State.RESUMED, "test");
mTrampolineActivity.setVisibility(false);
waitHandlerIdle(mAtm.mH);
// Not cancel immediately because in one of real cases, the keyguard may be going away or
// occluded later, then the activity can be drawn.
- verify(mLaunchObserver, never()).onActivityLaunchCancelled(eqProto(mTrampolineActivity));
+ verify(mLaunchObserver, never()).onActivityLaunchCancelled(
+ eqLastStartedId(mTrampolineActivity));
clearInvocations(mLaunchObserver);
mLaunchTopByTrampoline = true;
@@ -289,9 +288,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
// The posted message will acquire wm lock, so the test needs to release the lock to verify.
final Throwable error = awaitInWmLock(() -> {
try {
- // Though the aborting target should be eqProto(mTopActivity), use any() to avoid
- // any changes in proto that may cause failure by different arguments.
- verify(mLaunchObserver, timeout(TIMEOUT_MS)).onActivityLaunchCancelled(any());
+ verify(mLaunchObserver, timeout(TIMEOUT_MS)).onActivityLaunchCancelled(
+ mExpectedStartedId);
} catch (Throwable e) {
// Catch any errors including assertion because this runs in another thread.
return e;
@@ -314,9 +312,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
mActivityOptions = ActivityOptions.makeBasic();
mActivityOptions.setSourceInfo(SourceInfo.TYPE_LAUNCHER, SystemClock.uptimeMillis() - 10);
onIntentStarted(mTopActivity.intent);
- notifyActivityLaunched(START_SUCCESS, mTopActivity);
- verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt());
- verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(prev));
+ notifyAndVerifyActivityLaunched(mTopActivity);
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eq(getLastStartedId(prev)));
// The activity reports fully drawn before windows drawn, then the fully drawn event will
// be pending (see {@link WindowingModeTransitionInfo#pendingFullyDrawn}).
@@ -328,7 +325,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
.isEqualTo(SourceInfo.TYPE_LAUNCHER);
assertWithMessage("Record event time").that(info.sourceEventDelayMs).isAtLeast(10);
- verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong());
+ verifyAsync(mLaunchObserver).onReportFullyDrawn(eq(mExpectedStartedId), anyLong());
verifyOnActivityLaunchFinished(mTopActivity);
verifyNoMoreInteractions(mLaunchObserver);
@@ -339,9 +336,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
private void onActivityLaunchedTrampoline() {
onIntentStarted(mTrampolineActivity.intent);
- notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
-
- verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTrampolineActivity), anyInt());
+ notifyAndVerifyActivityLaunched(mTrampolineActivity);
// A second, distinct, activity launch is coalesced into the current app launch sequence.
mLaunchTopByTrampoline = true;
@@ -370,6 +365,11 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
mNewActivityCreated, activity, mActivityOptions);
}
+ private void notifyAndVerifyActivityLaunched(ActivityRecord activity) {
+ notifyActivityLaunched(START_SUCCESS, activity);
+ verifyOnActivityLaunched(activity);
+ }
+
private void notifyTransitionStarting(ActivityRecord activity) {
final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
reasons.put(activity, ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN);
@@ -430,7 +430,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
// Cannot time already-visible activities.
notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
- verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity));
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(mExpectedStartedId);
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -447,25 +447,14 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
// be reported successfully.
notifyTransitionStarting(mTopActivity);
+ verifyOnActivityLaunched(mTopActivity);
verifyOnActivityLaunchFinished(mTopActivity);
}
@Test
- public void testActivityRecordProtoIsNotTooBig() {
- // The ActivityRecordProto must not be too big, otherwise converting it at runtime
- // will become prohibitively expensive.
- assertWithMessage("mTopActivity: %s", mTopActivity)
- .that(activityRecordToProto(mTopActivity).length)
- .isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
-
- assertWithMessage("mTrampolineActivity: %s", mTrampolineActivity)
- .that(activityRecordToProto(mTrampolineActivity).length)
- .isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
- }
-
- @Test
public void testConcurrentLaunches() {
onActivityLaunched(mTopActivity);
+ clearInvocations(mLaunchObserver);
final ActivityMetricsLogger.LaunchingState previousState = mLaunchingState;
final ActivityRecord otherActivity = new ActivityBuilder(mAtm)
@@ -476,11 +465,13 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
// state should be created here.
onActivityLaunched(otherActivity);
- assertWithMessage("Different callers should get 2 indepedent launching states")
+ assertWithMessage("Different callers should get 2 independent launching states")
.that(previousState).isNotEqualTo(mLaunchingState);
+ setLastExpectedStartedId(otherActivity);
transitToDrawnAndVerifyOnLaunchFinished(otherActivity);
// The first transition should still be valid.
+ setLastExpectedStartedId(mTopActivity);
transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
}
@@ -534,10 +525,12 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
// Before TopActivity is drawn, it launches another activity on a different display.
mActivityMetricsLogger.notifyActivityLaunching(activityOnNewDisplay.intent,
mTopActivity /* caller */, mTopActivity.getUid());
- notifyActivityLaunched(START_SUCCESS, activityOnNewDisplay);
+ notifyAndVerifyActivityLaunched(activityOnNewDisplay);
// There should be 2 events instead of coalescing as one event.
+ setLastExpectedStartedId(mTopActivity);
transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
+ setLastExpectedStartedId(activityOnNewDisplay);
transitToDrawnAndVerifyOnLaunchFinished(activityOnNewDisplay);
}
@@ -548,9 +541,11 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
onActivityLaunched(mTrampolineActivity);
mActivityMetricsLogger.notifyActivityLaunching(mTopActivity.intent,
mTrampolineActivity /* caller */, mTrampolineActivity.getUid());
- notifyActivityLaunched(START_SUCCESS, mTopActivity);
+ notifyAndVerifyActivityLaunched(mTopActivity);
// Different windowing modes should be independent launch events.
+ setLastExpectedStartedId(mTrampolineActivity);
transitToDrawnAndVerifyOnLaunchFinished(mTrampolineActivity);
+ setLastExpectedStartedId(mTopActivity);
transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 67f02c7fab55..8474a36dc681 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
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.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -553,6 +554,37 @@ public class AppTransitionControllerTest extends WindowTestsBase {
}
@Test
+ public void testGetAnimationTargets_splitScreenOpening() {
+ // [DisplayContent] - [Task] -+- [split task 1] -+- [Task1] - [AR1] (opening, invisible)
+ // +- [split task 2] -+- [Task2] - [AR2] (opening, invisible)
+ final Task singleTopRoot = createTask(mDisplayContent);
+ final TaskBuilder builder = new TaskBuilder(mSupervisor)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+ .setParentTaskFragment(singleTopRoot)
+ .setCreatedByOrganizer(true);
+ final Task splitRoot1 = builder.build();
+ final Task splitRoot2 = builder.build();
+ splitRoot1.setAdjacentTaskFragment(splitRoot2, false /* moveTogether */);
+ final ActivityRecord activity1 = createActivityRecordWithParentTask(splitRoot1);
+ activity1.setVisible(false);
+ activity1.mVisibleRequested = true;
+ final ActivityRecord activity2 = createActivityRecordWithParentTask(splitRoot2);
+ activity2.setVisible(false);
+ activity2.mVisibleRequested = true;
+
+ final ArraySet<ActivityRecord> opening = new ArraySet<>();
+ opening.add(activity1);
+ opening.add(activity2);
+ final ArraySet<ActivityRecord> closing = new ArraySet<>();
+
+ // Promote animation targets up to Task level, not beyond.
+ assertEquals(
+ new ArraySet<>(new WindowContainer[]{splitRoot1, splitRoot2}),
+ AppTransitionController.getAnimationTargets(
+ opening, closing, true /* visible */));
+ }
+
+ @Test
public void testGetAnimationTargets_openingClosingTaskFragment() {
// [DefaultTDA] - [Task] -+- [TaskFragment1] - [ActivityRecord1] (opening, invisible)
// +- [TaskFragment2] - [ActivityRecord2] (closing, visible)
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 32d201fafcfb..263c9364c965 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -41,8 +41,10 @@ import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -1167,6 +1169,20 @@ public class DisplayContentTests extends WindowTestsBase {
assertNull(mDisplayContent.computeImeParent());
}
+ @UseTestDisplay(addWindows = W_ACTIVITY)
+ @Test
+ public void testComputeImeParent_updateParentWhenTargetNotUseIme() throws Exception {
+ WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay");
+ overlay.setBounds(100, 100, 200, 200);
+ overlay.mAttrs.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;
+ WindowState app = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ mDisplayContent.setImeLayeringTarget(overlay);
+ mDisplayContent.setImeInputTarget(app);
+ assertFalse(mDisplayContent.shouldImeAttachedToApp());
+ assertEquals(mDisplayContent.getImeContainer().getParentSurfaceControl(),
+ mDisplayContent.computeImeParent());
+ }
+
@Test
public void testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved() throws Exception {
final DisplayContent dc = createNewDisplay();
diff --git a/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java b/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
index 6e0056821aab..8b0a54050c3c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
@@ -19,7 +19,6 @@ package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRESENTATION;
import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
import static com.google.common.truth.Truth.assertThat;
@@ -90,8 +89,8 @@ public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
- // An entry for each possible rotation, for a display that can be in a single state.
- assertThat(displayInfos.size()).isEqualTo(4);
+ // An entry for rotation 0, for a display that can be in a single state.
+ assertThat(displayInfos.size()).isEqualTo(1);
assertPossibleDisplayInfoEntries(displayInfos, mDefaultDisplayInfo);
}
@@ -100,7 +99,7 @@ public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
mPossibleDisplayInfo.add(mDefaultDisplayInfo);
mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
- assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY).size()).isEqualTo(4);
+ assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY).size()).isEqualTo(1);
// Add another display layout to the set of supported states.
mPossibleDisplayInfo.add(mSecondDisplayInfo);
@@ -116,12 +115,12 @@ public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
defaultDisplayInfos.add(di);
}
}
- // An entry for each possible rotation, for the default display.
- assertThat(defaultDisplayInfos).hasSize(4);
+ // An entry for rotation 0, for the default display.
+ assertThat(defaultDisplayInfos).hasSize(1);
assertPossibleDisplayInfoEntries(defaultDisplayInfos, mDefaultDisplayInfo);
- // An entry for each possible rotation, for the second display.
- assertThat(secondDisplayInfos).hasSize(4);
+ // An entry for rotation 0, for the second display.
+ assertThat(secondDisplayInfos).hasSize(1);
assertPossibleDisplayInfoEntries(secondDisplayInfos, mSecondDisplayInfo);
}
@@ -130,7 +129,7 @@ public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
mPossibleDisplayInfo.add(mDefaultDisplayInfo);
mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
- assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY).size()).isEqualTo(4);
+ assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY).size()).isEqualTo(1);
// Add another display to a different group.
mSecondDisplayInfo.displayId = DEFAULT_DISPLAY + 1;
@@ -139,14 +138,14 @@ public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
mDisplayInfoMapper.updatePossibleDisplayInfos(mSecondDisplayInfo.displayId);
Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
- // An entry for each possible rotation, for the default display.
- assertThat(displayInfos).hasSize(4);
+ // An entry for rotation 0, for the default display.
+ assertThat(displayInfos).hasSize(1);
assertPossibleDisplayInfoEntries(displayInfos, mDefaultDisplayInfo);
Set<DisplayInfo> secondStateEntries =
mDisplayInfoMapper.getPossibleDisplayInfos(mSecondDisplayInfo.displayId);
- // An entry for each possible rotation, for the second display.
- assertThat(secondStateEntries).hasSize(4);
+ // An entry for rotation 0, for the second display.
+ assertThat(secondStateEntries).hasSize(1);
assertPossibleDisplayInfoEntries(secondStateEntries, mSecondDisplayInfo);
}
@@ -160,23 +159,10 @@ public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
private static void assertPossibleDisplayInfoEntries(Set<DisplayInfo> displayInfos,
DisplayInfo expectedDisplayInfo) {
- boolean[] seenEveryRotation = new boolean[4];
for (DisplayInfo displayInfo : displayInfos) {
- final int rotation = displayInfo.rotation;
- seenEveryRotation[rotation] = true;
assertThat(displayInfo.displayId).isEqualTo(expectedDisplayInfo.displayId);
- assertEqualsRotatedDisplayInfo(displayInfo, expectedDisplayInfo);
- }
- assertThat(seenEveryRotation).isEqualTo(new boolean[]{true, true, true, true});
- }
-
- private static void assertEqualsRotatedDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
- if (actual.rotation == ROTATION_0 || actual.rotation == ROTATION_180) {
- assertThat(actual.logicalWidth).isEqualTo(expected.logicalWidth);
- assertThat(actual.logicalHeight).isEqualTo(expected.logicalHeight);
- } else {
- assertThat(actual.logicalWidth).isEqualTo(expected.logicalHeight);
- assertThat(actual.logicalHeight).isEqualTo(expected.logicalWidth);
+ assertThat(displayInfo.logicalWidth).isEqualTo(expectedDisplayInfo.logicalWidth);
+ assertThat(displayInfo.logicalHeight).isEqualTo(expectedDisplayInfo.logicalHeight);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 7507df6eba74..9957d05d0a52 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1290,6 +1290,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
private TaskFragment mParentTaskFragment;
private boolean mCreateActivity = false;
+ private boolean mCreatedByOrganizer = false;
TaskBuilder(ActivityTaskSupervisor supervisor) {
mSupervisor = supervisor;
@@ -1381,6 +1382,11 @@ class WindowTestsBase extends SystemServiceTestsBase {
return this;
}
+ TaskBuilder setCreatedByOrganizer(boolean createdByOrganizer) {
+ mCreatedByOrganizer = createdByOrganizer;
+ return this;
+ }
+
Task build() {
SystemServicesTestRule.checkHoldsLock(mSupervisor.mService.mGlobalLock);
@@ -1416,7 +1422,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
.setActivityInfo(mActivityInfo)
.setIntent(mIntent)
.setOnTop(mOnTop)
- .setVoiceSession(mVoiceSession);
+ .setVoiceSession(mVoiceSession)
+ .setCreatedByOrganizer(mCreatedByOrganizer);
final Task task;
if (mParentTaskFragment == null) {
task = builder.setActivityType(mActivityType)
diff --git a/telephony/java/Android.bp b/telephony/java/Android.bp
index 3941b300206f..76a420c430d1 100644
--- a/telephony/java/Android.bp
+++ b/telephony/java/Android.bp
@@ -13,6 +13,15 @@ filegroup {
srcs: [
"**/*.java",
"**/*.aidl",
+ ":statslog-telephony-java-gen",
],
visibility: ["//frameworks/base"],
}
+
+genrule {
+ name: "statslog-telephony-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module telephony" +
+ " --javaPackage com.android.internal.telephony --javaClass TelephonyStatsLog",
+ out: ["com/android/internal/telephony/TelephonyStatsLog.java"],
+}
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index ffdb23f98fb8..f47cf3384791 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static com.android.internal.telephony.TelephonyStatsLog.TELEPHONY_ANOMALY_DETECTED;
+
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.content.Context;
@@ -24,6 +26,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.ParcelUuid;
+import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
@@ -83,6 +86,12 @@ public final class AnomalyReporter {
return;
}
+ TelephonyStatsLog.write(
+ TELEPHONY_ANOMALY_DETECTED,
+ 0, // TODO: carrier id needs to be populated
+ eventId.getLeastSignificantBits(),
+ eventId.getMostSignificantBits());
+
// If this event has already occurred, skip sending intents for it; regardless log its
// invocation here.
Integer count = sEvents.containsKey(eventId) ? sEvents.get(eventId) + 1 : 1;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 93b987ea8787..aacc17a49a24 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -16,6 +16,10 @@
package com.android.server.wm.flicker.helpers
+import android.view.WindowInsets.Type.ime
+import android.view.WindowInsets.Type.navigationBars
+import android.view.WindowInsets.Type.statusBars
+
import android.app.Instrumentation
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
@@ -25,6 +29,8 @@ import com.android.server.wm.traces.common.FlickerComponentName
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import java.util.regex.Pattern
+
class ImeAppAutoFocusHelper @JvmOverloads constructor(
instr: Instrumentation,
private val rotation: Int,
@@ -72,6 +78,7 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
wmHelper.waitForAppTransitionIdle()
wmHelper.waitForFullScreenApp(
ActivityOptions.DIALOG_THEMED_ACTIVITY_COMPONENT_NAME.toFlickerComponent())
+ mInstrumentation.waitForIdleSync()
}
fun dismissDialog(wmHelper: WindowManagerStateHelper) {
val dialog = uiDevice.wait(
@@ -83,4 +90,27 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
wmHelper.waitForAppTransitionIdle()
}
}
+ fun getInsetsVisibleFromDialog(type: Int): Boolean {
+ var insetsVisibilityTextView = uiDevice.wait(
+ Until.findObject(By.res("android:id/text1")), FIND_TIMEOUT)
+ if (insetsVisibilityTextView != null) {
+ var visibility = insetsVisibilityTextView.text.toString()
+ val matcher = when (type) {
+ ime() -> {
+ Pattern.compile("IME\\: (VISIBLE|INVISIBLE)").matcher(visibility)
+ }
+ statusBars() -> {
+ Pattern.compile("StatusBar\\: (VISIBLE|INVISIBLE)").matcher(visibility)
+ }
+ navigationBars() -> {
+ Pattern.compile("NavBar\\: (VISIBLE|INVISIBLE)").matcher(visibility)
+ }
+ else -> null
+ }
+ if (matcher != null && matcher.find()) {
+ return matcher.group(1).equals("VISIBLE")
+ }
+ }
+ return false
+ }
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
index 1b60403ac354..2f8f9441a7b9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -16,6 +16,10 @@
package com.android.server.wm.flicker.ime
+import android.view.WindowInsets.Type.ime
+import android.view.WindowInsets.Type.navigationBars
+import android.view.WindowInsets.Type.statusBars
+
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -35,6 +39,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
/**
* Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
@@ -56,6 +62,10 @@ class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestPar
testApp.launchViaIntent(wmHelper)
wmHelper.waitImeShown()
testApp.startDialogThemedActivity(wmHelper)
+ // Verify IME insets isn't visible on dialog since it's non-IME focusable window
+ assertFalse(testApp.getInsetsVisibleFromDialog(ime()))
+ assertTrue(testApp.getInsetsVisibleFromDialog(statusBars()))
+ assertTrue(testApp.getInsetsVisibleFromDialog(navigationBars()))
}
}
teardown {
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java
index 27606d81f9d3..20eb295d3e6b 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java
@@ -17,11 +17,16 @@
package com.android.server.wm.flicker.testapp;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.Dialog;
import android.graphics.Color;
import android.os.Bundle;
import android.view.WindowManager;
@@ -33,9 +38,12 @@ public class DialogThemedActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
+ getWindow().addFlags(FLAG_NOT_FOCUSABLE);
getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT);
TextView textView = new TextView(this);
- textView.setText("This is a test dialog");
+ // Print SystemBars' insets visibility on this window for demonstrating during the test.
+ textView.setId(android.R.id.text1);
+ textView.setText("Insets visibility\n\n");
textView.setTextColor(Color.BLACK);
LinearLayout layout = new LinearLayout(this);
layout.setBackgroundColor(Color.GREEN);
@@ -51,7 +59,17 @@ public class DialogThemedActivity extends Activity {
attrs.flags = FLAG_DIM_BEHIND | FLAG_ALT_FOCUSABLE_IM;
dialog.getWindow().getDecorView().setLayoutParams(attrs);
dialog.setCanceledOnTouchOutside(true);
+ dialog.setOnShowListener(d -> textView.setText(textView.getText()
+ + "IME: " + isInsetsVisible(dialog, ime()) + "\n"
+ + "StatusBar: " + isInsetsVisible(dialog, statusBars()) + "\n"
+ + "NavBar: " + isInsetsVisible(dialog, navigationBars()) + "\n")
+ );
dialog.show();
dialog.setOnDismissListener((d) -> finish());
}
+
+ private String isInsetsVisible(Dialog d, int type) {
+ return d.getWindow().getDecorView().getRootWindowInsets().isVisible(type) ? "VISIBLE"
+ : "INVISIBLE";
+ }
}