summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/api.go6
-rw-r--r--core/api/test-current.txt4
-rw-r--r--core/java/android/app/ActivityManager.java17
-rw-r--r--core/java/android/app/AppOpsManager.java4
-rw-r--r--core/java/android/app/IActivityManager.aidl4
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java120
-rw-r--r--core/java/android/content/pm/TEST_MAPPING35
-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.java20
-rw-r--r--core/java/android/util/PackageUtils.java40
-rw-r--r--core/java/android/view/IPinnedTaskListener.aidl16
-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/ScrollCaptureConnection.java24
-rw-r--r--core/java/android/view/SurfaceControl.java26
-rw-r--r--core/java/android/view/ViewRootImpl.java51
-rw-r--r--core/java/android/view/WindowManager.java4
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java33
-rw-r--r--core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java56
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java15
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java1
-rw-r--r--core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java24
-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/drawable/ic_no_apps.xml4
-rw-r--r--core/res/res/drawable/ic_sharing_disabled.xml4
-rw-r--r--core/res/res/drawable/ic_work_apps_off.xml4
-rw-r--r--core/res/res/layout/resolver_empty_states.xml14
-rw-r--r--core/res/res/values/dimens.xml2
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java21
-rw-r--r--graphics/java/android/graphics/drawable/RippleForeground.java4
-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.java45
-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/res/animator/tv_pip_menu_action_button_animator.xml53
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu.xml29
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-tvdpi/dimen.xml2
-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/dagger/TvPipModule.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipParamsChangedForwarder.java125
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java156
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java271
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java85
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java8
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java3
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java15
-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/src/com/android/providers/settings/SettingsProvider.java3
-rw-r--r--packages/SystemUI/res-keyguard/layout/fgs_footer.xml11
-rw-r--r--packages/SystemUI/res/layout/clipboard_overlay.xml245
-rw-r--r--packages/SystemUI/res/layout/media_output_broadcast_update_dialog.xml10
-rw-r--r--packages/SystemUI/res/layout/media_output_list_item.xml10
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml1
-rw-r--r--packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml2
-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/QuickStepContract.java12
-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/LockIconView.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java16
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt7
-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.java35
-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.java85
-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/MetadataAnimationHandler.kt26
-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/MediaOutputAdapter.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java127
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java63
-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/recents/OverviewProxyService.java36
-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/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.java2
-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/stack/NotificationStackScrollLayoutController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java4570
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java4558
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java57
-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/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/KeyguardPasswordViewControllerTest.kt114
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java35
-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.kt70
-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/MetadataAnimationHandlerTest.kt60
-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/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.java15
-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/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/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--services/core/java/com/android/server/BinaryTransparencyService.java9
-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.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java36
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java505
-rw-r--r--services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java15
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java40
-rw-r--r--services/core/java/com/android/server/media/MediaShellCommand.java1
-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/wm/ActivityClientController.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java10
-rw-r--r--services/core/java/com/android/server/wm/PinnedTaskController.java103
-rw-r--r--services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java49
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java54
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java3
-rw-r--r--services/core/jni/com_android_server_am_CachedAppOptimizer.cpp294
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java83
-rw-r--r--services/tests/mockingservicestests/jni/Android.bp1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java83
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java26
-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/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java42
-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
195 files changed, 9829 insertions, 6321 deletions
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/api/test-current.txt b/core/api/test-current.txt
index cac13743461d..4681d4943256 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -116,8 +116,8 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
method public long getTotalRam();
- method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidProcessCapabilities(int);
- method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidProcessState(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessCapabilities(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessState(int);
method public void holdLock(android.os.IBinder, int);
method public static boolean isHighEndGfx();
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 458dd5d804d1..abd60177f884 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3710,10 +3710,16 @@ public class ActivityManager {
/**
* Returns the process state of this uid.
*
+ * If the caller does not hold {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission, they can only query process state of UIDs running in the same user as the caller.
+ *
* @hide
*/
@TestApi
- @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+ @RequiresPermission(allOf = {
+ Manifest.permission.PACKAGE_USAGE_STATS,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ }, conditional = true)
public int getUidProcessState(int uid) {
try {
return getService().getUidProcessState(uid, mContext.getOpPackageName());
@@ -3725,10 +3731,17 @@ public class ActivityManager {
/**
* Returns the process capability of this uid.
*
+ * If the caller does not hold {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission, they can only query process capabilities of UIDs running in the same user
+ * as the caller.
+ *
* @hide
*/
@TestApi
- @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+ @RequiresPermission(allOf = {
+ Manifest.permission.PACKAGE_USAGE_STATS,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ }, conditional = true)
public @ProcessCapability int getUidProcessCapabilities(int uid) {
try {
return getService().getUidProcessCapabilities(uid, mContext.getOpPackageName());
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6e395be215b5..c7c654a0b071 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2537,8 +2537,8 @@ public class AppOpsManager {
* restriction} for a certain app-op.
*/
private static RestrictionBypass[] sOpAllowSystemRestrictionBypass = new RestrictionBypass[] {
- new RestrictionBypass(true, false), //COARSE_LOCATION
- new RestrictionBypass(true, false), //FINE_LOCATION
+ null, //COARSE_LOCATION
+ null, //FINE_LOCATION
null, //GPS
null, //VIBRATE
null, //READ_CONTACTS
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 49a61580ab3b..4efe9dfe7185 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -100,6 +100,8 @@ interface IActivityManager {
String callingPackage);
void unregisterUidObserver(in IUidObserver observer);
boolean isUidActive(int uid, String callingPackage);
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true)")
int getUidProcessState(int uid, in String callingPackage);
@UnsupportedAppUsage
int checkPermission(in String permission, int pid, int uid);
@@ -742,6 +744,8 @@ interface IActivityManager {
/** Called by PendingIntent.queryIntentComponents() */
ParceledListSlice queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags);
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(allOf = {android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true)")
int getUidProcessCapabilities(int uid, in String callingPackage);
/** Blocks until all broadcast queues become idle. */
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/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 9cd201a1acbc..1c1f58a19abc 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -186,5 +186,40 @@
}
]
}
+ ],
+ "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/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 731b5315c7fb..20a2bdf3b109 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9682,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
diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
index c5ab82dbf0c5..11481209284a 100644
--- a/core/java/android/util/PackageUtils.java
+++ b/core/java/android/util/PackageUtils.java
@@ -171,39 +171,53 @@ public final class PackageUtils {
}
/**
- * @see #computeSha256DigestForLargeFile(String, String)
+ * Creates a fixed size buffer based on whether the device is low ram or not. This is to be used
+ * with the {@link #computeSha256DigestForLargeFile(String, byte[])} and
+ * {@link #computeSha256DigestForLargeFile(String, byte[], String)} methods.
+ * @return a byte array of size {@link #LOW_RAM_BUFFER_SIZE_BYTES} if the device is a low RAM
+ * device, otherwise a byte array of size {@link #HIGH_RAM_BUFFER_SIZE_BYTES}
*/
- public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath) {
- return computeSha256DigestForLargeFile(filePath, null);
+ public static @NonNull byte[] createLargeFileBuffer() {
+ int bufferSize = ActivityManager.isLowRamDeviceStatic()
+ ? LOW_RAM_BUFFER_SIZE_BYTES : HIGH_RAM_BUFFER_SIZE_BYTES;
+ return new byte[bufferSize];
}
/**
- * Computes the SHA256 digest of large files.
+ * @see #computeSha256DigestForLargeFile(String, byte[], String)
+ */
+ public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath,
+ @NonNull byte[] fileBuffer) {
+ return computeSha256DigestForLargeFile(filePath, fileBuffer, null);
+ }
+
+ /**
+ * Computes the SHA256 digest of large files. This is typically useful for large APEXs.
* @param filePath The path to which the file's content is to be hashed.
+ * @param fileBuffer A buffer to read file's content into memory. It is strongly recommended to
+ * make use of the {@link #createLargeFileBuffer()} method to create this
+ * buffer.
* @param separator Separator between each pair of characters, such as colon, or null to omit.
- * @return The digest or null if an error occurs.
+ * @return The SHA256 digest or null if an error occurs.
*/
public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath,
- @Nullable String separator) {
+ @NonNull byte[] fileBuffer, @Nullable String separator) {
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA256");
messageDigest.reset();
} catch (NoSuchAlgorithmException e) {
- // this shouldn't happen!
+ // this really shouldn't happen!
return null;
}
- boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
- int bufferSize = isLowRamDevice ? LOW_RAM_BUFFER_SIZE_BYTES : HIGH_RAM_BUFFER_SIZE_BYTES;
-
File f = new File(filePath);
try {
- DigestInputStream digestStream = new DigestInputStream(new FileInputStream(f),
+ DigestInputStream digestInputStream = new DigestInputStream(new FileInputStream(f),
messageDigest);
- byte[] buffer = new byte[bufferSize];
- while (digestStream.read(buffer) != -1);
+ while (digestInputStream.read(fileBuffer) != -1);
} catch (IOException e) {
+ e.printStackTrace();
return null;
}
diff --git a/core/java/android/view/IPinnedTaskListener.aidl b/core/java/android/view/IPinnedTaskListener.aidl
index 595a846e069a..e4e2d6f30aab 100644
--- a/core/java/android/view/IPinnedTaskListener.aidl
+++ b/core/java/android/view/IPinnedTaskListener.aidl
@@ -44,26 +44,10 @@ oneway interface IPinnedTaskListener {
void onImeVisibilityChanged(boolean imeVisible, int imeHeight);
/**
- * Called when the set of actions for the current PiP activity changes, or when the listener
- * is first registered to allow the listener to synchronize its state with the controller.
- */
- void onActionsChanged(in ParceledListSlice<RemoteAction> actions, in RemoteAction closeAction);
-
- /**
* Called by the window manager to notify the listener that Activity (was or is in pinned mode)
* is hidden (either stopped or removed). This is generally used as a signal to reset saved
* reentry fraction and size.
* {@param componentName} represents the application component of PiP window.
*/
void onActivityHidden(in ComponentName componentName);
-
- /**
- * Called by the window manager when the aspect ratio is reset.
- */
- void onAspectRatioChanged(float aspectRatio);
-
- /**
- * Called by the window manager when the expanded aspect ratio is reset.
- */
- void onExpandedAspectRatioChanged(float aspectRatio);
}
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/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java
index cba0e970d389..0f27989642a9 100644
--- a/core/java/android/view/ScrollCaptureConnection.java
+++ b/core/java/android/view/ScrollCaptureConnection.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.os.Trace.TRACE_TAG_GRAPHICS;
+
import static java.util.Objects.requireNonNull;
import android.annotation.BinderThread;
@@ -27,6 +29,7 @@ import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.CloseGuard;
import android.util.Log;
@@ -48,6 +51,12 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub imple
IBinder.DeathRecipient {
private static final String TAG = "ScrollCaptureConnection";
+ private static final String TRACE_TRACK = "Scroll Capture";
+ private static final String START_CAPTURE = "startCapture";
+ private static final String REQUEST_IMAGE = "requestImage";
+
+ private static final String END_CAPTURE = "endCapture";
+ private static final String SESSION = "Session";
private final Object mLock = new Object();
private final Rect mScrollBounds;
@@ -62,6 +71,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub imple
private volatile boolean mActive;
private volatile boolean mConnected;
+ private int mTraceId;
/**
* Constructs a ScrollCaptureConnection.
@@ -86,6 +96,9 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub imple
@Override
public ICancellationSignal startCapture(@NonNull Surface surface,
@NonNull IScrollCaptureCallbacks remote) throws RemoteException {
+ mTraceId = System.identityHashCode(surface);
+ Trace.asyncTraceForTrackBegin(TRACE_TAG_GRAPHICS, TRACE_TRACK, SESSION, mTraceId);
+ Trace.asyncTraceForTrackBegin(TRACE_TAG_GRAPHICS, TRACE_TRACK, START_CAPTURE, mTraceId);
mCloseGuard.open("ScrollCaptureConnection.close");
if (!surface.isValid()) {
@@ -116,11 +129,13 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub imple
close();
}
mCancellation = null;
+ Trace.asyncTraceForTrackEnd(TRACE_TAG_GRAPHICS, TRACE_TRACK, START_CAPTURE, mTraceId);
}
@BinderThread
@Override
public ICancellationSignal requestImage(Rect requestRect) throws RemoteException {
+ Trace.asyncTraceForTrackBegin(TRACE_TAG_GRAPHICS, TRACE_TRACK, REQUEST_IMAGE, mTraceId);
checkActive();
cancelPendingAction();
ICancellationSignal cancellation = CancellationSignal.createTransport();
@@ -144,11 +159,13 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub imple
} finally {
mCancellation = null;
}
+ Trace.asyncTraceForTrackEnd(TRACE_TAG_GRAPHICS, TRACE_TRACK, REQUEST_IMAGE, mTraceId);
}
@BinderThread
@Override
public ICancellationSignal endCapture() throws RemoteException {
+ Trace.asyncTraceForTrackBegin(TRACE_TAG_GRAPHICS, TRACE_TRACK, END_CAPTURE, mTraceId);
checkActive();
cancelPendingAction();
ICancellationSignal cancellation = CancellationSignal.createTransport();
@@ -174,17 +191,22 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub imple
mCancellation = null;
close();
}
+ Trace.asyncTraceForTrackEnd(TRACE_TAG_GRAPHICS, TRACE_TRACK, END_CAPTURE, mTraceId);
+ Trace.asyncTraceForTrackEnd(TRACE_TAG_GRAPHICS, TRACE_TRACK, SESSION, mTraceId);
}
@Override
public void binderDied() {
+ Trace.instantForTrack(TRACE_TAG_GRAPHICS, TRACE_TRACK, "binderDied");
Log.e(TAG, "Controlling process just died.");
close();
+
}
@BinderThread
@Override
public void close() {
+ Trace.instantForTrack(TRACE_TAG_GRAPHICS, TRACE_TRACK, "close");
if (mActive) {
Log.w(TAG, "close(): capture session still active! Ending now.");
cancelPendingAction();
@@ -201,11 +223,13 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub imple
mRemote = null;
mLocal = null;
mCloseGuard.close();
+ Trace.endSection();
Reference.reachabilityFence(this);
}
private void cancelPendingAction() {
if (mCancellation != null) {
+ Trace.instantForTrack(TRACE_TAG_GRAPHICS, TRACE_TRACK, "CancellationSignal.cancel");
Log.w(TAG, "cancelling pending operation.");
mCancellation.cancel();
mCancellation = null;
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..6665ab62703b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -321,6 +321,11 @@ public final class ViewRootImpl implements ViewParent,
private static final int UNSET_SYNC_ID = -1;
+ /**
+ * Minimum time to wait before reporting changes to keep clear areas.
+ */
+ private static final int KEEP_CLEAR_AREA_REPORT_RATE_MILLIS = 100;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
@@ -796,6 +801,8 @@ public final class ViewRootImpl implements ViewParent,
new ViewRootRectTracker(v -> v.collectPreferKeepClearRects());
private final ViewRootRectTracker mUnrestrictedKeepClearRectsTracker =
new ViewRootRectTracker(v -> v.collectUnrestrictedPreferKeepClearRects());
+ private List<Rect> mPendingKeepClearAreas;
+ private List<Rect> mPendingUnrestrictedKeepClearAreas;
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
@@ -4824,15 +4831,42 @@ public final class ViewRootImpl implements ViewParent,
unrestrictedKeepClearRects = Collections.emptyList();
}
- try {
- mWindowSession.reportKeepClearAreasChanged(mWindow, restrictedKeepClearRects,
- unrestrictedKeepClearRects);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (mHandler.hasMessages(MSG_REPORT_KEEP_CLEAR_RECTS)) {
+ // Keep clear areas have been reported recently, wait before reporting new set
+ // of keep clear areas
+ mPendingKeepClearAreas = restrictedKeepClearRects;
+ mPendingUnrestrictedKeepClearAreas = unrestrictedKeepClearRects;
+ } else {
+ mHandler.sendEmptyMessageDelayed(MSG_REPORT_KEEP_CLEAR_RECTS,
+ KEEP_CLEAR_AREA_REPORT_RATE_MILLIS);
+ try {
+ mWindowSession.reportKeepClearAreasChanged(mWindow, restrictedKeepClearRects,
+ unrestrictedKeepClearRects);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
}
+ void reportKeepClearAreasChanged() {
+ final List<Rect> restrictedKeepClearRects = mPendingKeepClearAreas;
+ final List<Rect> unrestrictedKeepClearRects = mPendingUnrestrictedKeepClearAreas;
+ if (restrictedKeepClearRects == null && unrestrictedKeepClearRects == null) {
+ return;
+ }
+
+ mPendingKeepClearAreas = null;
+ mPendingUnrestrictedKeepClearAreas = null;
+
+ try {
+ mWindowSession.reportKeepClearAreasChanged(mWindow, restrictedKeepClearRects,
+ unrestrictedKeepClearRects);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Requests that the root render node is invalidated next time we perform a draw, such that
* {@link WindowCallbacks#onPostDraw} gets called.
@@ -5035,9 +5069,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.");
@@ -5325,6 +5356,7 @@ public final class ViewRootImpl implements ViewParent,
private static final int MSG_REQUEST_SCROLL_CAPTURE = 33;
private static final int MSG_WINDOW_TOUCH_MODE_CHANGED = 34;
private static final int MSG_KEEP_CLEAR_RECTS_CHANGED = 35;
+ private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36;
final class ViewRootHandler extends Handler {
@@ -5601,6 +5633,9 @@ public final class ViewRootImpl implements ViewParent,
case MSG_KEEP_CLEAR_RECTS_CHANGED: {
keepClearRectsChanged();
} break;
+ case MSG_REPORT_KEEP_CLEAR_RECTS: {
+ reportKeepClearAreasChanged();
+ } break;
case MSG_REQUEST_SCROLL_CAPTURE:
handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
break;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 3774dacfa5ff..5bc340b76f56 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -725,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/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/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index 4ad232ad25eb..8bd0f7b2fb57 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -15,10 +15,8 @@
*/
package com.android.internal.app;
-import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.Nullable;
-import android.annotation.StringRes;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyEventLogger;
import android.content.ContentResolver;
@@ -33,7 +31,6 @@ import android.stats.devicepolicy.DevicePolicyEnums;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
-import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.R;
@@ -348,30 +345,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
ResolverListAdapter activeListAdapter);
/**
- * Updates padding and visibilities as a result of an orientation change.
- * <p>They are not updated automatically, because the view is cached when created.
- * <p>When overridden, make sure to always call the super method.
- */
- void updateAfterConfigChange() {
- for (int i = 0; i < getItemCount(); i++) {
- ViewGroup emptyStateView = getItem(i).getEmptyStateView();
- ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
- updateIconVisibility(icon, emptyStateView);
- }
- }
-
- private void updateIconVisibility(ImageView icon, ViewGroup emptyStateView) {
- if (isSpinnerShowing(emptyStateView)) {
- icon.setVisibility(View.INVISIBLE);
- } else if (mWorkProfileUserHandle != null
- && !getContext().getResources().getBoolean(R.bool.resolver_landscape_phone)) {
- icon.setVisibility(View.VISIBLE);
- } else {
- icon.setVisibility(View.GONE);
- }
- }
-
- /**
* The empty state screens are shown according to their priority:
* <ol>
* <li>(highest priority) cross-profile disabled by policy (handled in
@@ -461,27 +434,13 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
}
}
- protected void showEmptyState(ResolverListAdapter activeListAdapter,
- @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes) {
- showEmptyState(activeListAdapter, iconRes, titleRes, subtitleRes, /* buttonOnClick */ null);
- }
-
- protected void showEmptyState(ResolverListAdapter activeListAdapter,
- @DrawableRes int iconRes, String title, String subtitle) {
- showEmptyState(activeListAdapter, iconRes, title, subtitle, /* buttonOnClick */ null);
+ protected void showEmptyState(ResolverListAdapter activeListAdapter, String title,
+ String subtitle) {
+ showEmptyState(activeListAdapter, title, subtitle, /* buttonOnClick */ null);
}
protected void showEmptyState(ResolverListAdapter activeListAdapter,
- @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes,
- View.OnClickListener buttonOnClick) {
- String title = titleRes == 0 ? null : mContext.getString(titleRes);
- String subtitle = subtitleRes == 0 ? null : mContext.getString(subtitleRes);
- showEmptyState(activeListAdapter, iconRes, title, subtitle, buttonOnClick);
- }
-
- protected void showEmptyState(ResolverListAdapter activeListAdapter,
- @DrawableRes int iconRes, String title, String subtitle,
- View.OnClickListener buttonOnClick) {
+ String title, String subtitle, View.OnClickListener buttonOnClick) {
ProfileDescriptor descriptor = getItem(
userHandleToPageIndex(activeListAdapter.getUserHandle()));
descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
@@ -507,10 +466,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
button.setOnClickListener(buttonOnClick);
- ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
- icon.setImageResource(iconRes);
- updateIconVisibility(icon, emptyStateView);
-
activeListAdapter.markTabLoaded();
}
@@ -537,7 +492,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
}
private void showSpinner(View emptyStateView) {
- emptyStateView.findViewById(R.id.resolver_empty_state_icon).setVisibility(View.INVISIBLE);
emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.INVISIBLE);
emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.INVISIBLE);
emptyStateView.findViewById(R.id.resolver_empty_state_progress).setVisibility(View.VISIBLE);
@@ -545,7 +499,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
}
private void resetViewVisibilitiesForWorkProfileEmptyState(View emptyStateView) {
- emptyStateView.findViewById(R.id.resolver_empty_state_icon).setVisibility(View.VISIBLE);
emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.VISIBLE);
emptyStateView.findViewById(R.id.resolver_empty_state_subtitle).setVisibility(View.VISIBLE);
emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.INVISIBLE);
@@ -554,7 +507,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
}
private void resetViewVisibilitiesForConsumerUserEmptyState(View emptyStateView) {
- emptyStateView.findViewById(R.id.resolver_empty_state_icon).setVisibility(View.GONE);
emptyStateView.findViewById(R.id.resolver_empty_state_title).setVisibility(View.GONE);
emptyStateView.findViewById(R.id.resolver_empty_state_subtitle).setVisibility(View.GONE);
emptyStateView.findViewById(R.id.resolver_empty_state_button).setVisibility(View.GONE);
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 916408984674..d35d5ca6fca7 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -193,7 +193,6 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter,
View.OnClickListener listener) {
showEmptyState(activeListAdapter,
- R.drawable.ic_work_apps_off,
getWorkAppPausedTitle(),
/* subtitle = */ null,
listener);
@@ -203,12 +202,10 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) {
if (mIsSendAction) {
showEmptyState(activeListAdapter,
- R.drawable.ic_sharing_disabled,
getCrossProfileBlockedTitle(),
getCantShareWithWorkMessage());
} else {
showEmptyState(activeListAdapter,
- R.drawable.ic_sharing_disabled,
getCrossProfileBlockedTitle(),
getCantAccessWorkMessage());
}
@@ -218,12 +215,10 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) {
if (mIsSendAction) {
showEmptyState(activeListAdapter,
- R.drawable.ic_sharing_disabled,
getCrossProfileBlockedTitle(),
getCantShareWithPersonalMessage());
} else {
showEmptyState(activeListAdapter,
- R.drawable.ic_sharing_disabled,
getCrossProfileBlockedTitle(),
getCantAccessPersonalMessage());
}
@@ -231,19 +226,13 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
@Override
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- showEmptyState(listAdapter,
- R.drawable.ic_no_apps,
- getNoPersonalAppsAvailableMessage(),
- /* subtitle= */ null);
+ showEmptyState(listAdapter, getNoPersonalAppsAvailableMessage(), /* subtitle= */ null);
}
@Override
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- showEmptyState(listAdapter,
- R.drawable.ic_no_apps,
- getNoWorkAppsAvailableMessage(),
- /* subtitle = */ null);
+ showEmptyState(listAdapter, getNoWorkAppsAvailableMessage(), /* subtitle = */ null);
}
private String getWorkAppPausedTitle() {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 17c2dc09140e..3d93b2a231c2 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -673,7 +673,6 @@ public class ResolverActivity extends Activity implements
getResources().getDimensionPixelSize(R.dimen.resolver_button_bar_spacing),
buttonBar.getPaddingRight(),
getResources().getDimensionPixelSize(R.dimen.resolver_button_bar_spacing));
- mMultiProfilePagerAdapter.updateAfterConfigChange();
}
@Override // ResolverListCommunicator
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index f4e568b6676a..0b33501fd875 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -26,7 +26,6 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.res.Resources;
import android.os.UserHandle;
import android.view.LayoutInflater;
import android.view.View;
@@ -74,24 +73,6 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
mShouldShowNoCrossProfileIntentsEmptyState = shouldShowNoCrossProfileIntentsEmptyState;
}
- @Override
- void updateAfterConfigChange() {
- super.updateAfterConfigChange();
- for (ResolverProfileDescriptor descriptor : mItems) {
- View emptyStateCont =
- descriptor.rootView.findViewById(R.id.resolver_empty_state_container);
- Resources resources = getContext().getResources();
- emptyStateCont.setPadding(
- emptyStateCont.getPaddingLeft(),
- resources.getDimensionPixelSize(
- R.dimen.resolver_empty_state_container_padding_top),
- emptyStateCont.getPaddingRight(),
- resources.getDimensionPixelSize(
- R.dimen.resolver_empty_state_container_padding_bottom));
-
- }
- }
-
private ResolverProfileDescriptor createProfileDescriptor(
ResolverListAdapter adapter) {
final LayoutInflater inflater = LayoutInflater.from(getContext());
@@ -203,7 +184,6 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter,
View.OnClickListener listener) {
showEmptyState(activeListAdapter,
- R.drawable.ic_work_apps_off,
getWorkAppPausedTitle(),
/* subtitle = */ null,
listener);
@@ -212,7 +192,6 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
@Override
protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) {
showEmptyState(activeListAdapter,
- R.drawable.ic_sharing_disabled,
getCrossProfileBlockedTitle(),
getCantAccessWorkMessage());
}
@@ -220,7 +199,6 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
@Override
protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) {
showEmptyState(activeListAdapter,
- R.drawable.ic_sharing_disabled,
getCrossProfileBlockedTitle(),
getCantAccessPersonalMessage());
}
@@ -228,7 +206,6 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
@Override
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
- R.drawable.ic_no_apps,
getNoPersonalAppsAvailableMessage(),
/* subtitle = */ null);
}
@@ -236,7 +213,6 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
@Override
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
- R.drawable.ic_no_apps,
getNoWorkAppsAvailableMessage(),
/* subtitle= */ null);
}
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/drawable/ic_no_apps.xml b/core/res/res/drawable/ic_no_apps.xml
deleted file mode 100644
index 4d296bddd813..000000000000
--- a/core/res/res/drawable/ic_no_apps.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<vector android:height="32dp" android:viewportHeight="24"
- android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@color/resolver_empty_state_icon" android:fillType="evenOdd" android:pathData="M18.8123,20.0145L21.3999,22.6021L22.602,21.4L2.602,1.4L1.3999,2.6021L3.9873,5.1895C3.6248,5.552 3.3998,6.052 3.3998,6.602C3.3998,7.702 4.2998,8.602 5.3998,8.602C5.9498,8.602 6.4498,8.377 6.8123,8.0145L9.9873,11.1895C9.6248,11.552 9.3998,12.052 9.3998,12.602C9.3998,13.702 10.2998,14.602 11.3998,14.602C11.9498,14.602 12.4498,14.377 12.8123,14.0145L15.9873,17.1895C15.6248,17.552 15.3998,18.052 15.3998,18.602C15.3998,19.702 16.2998,20.602 17.3998,20.602C17.9498,20.602 18.4498,20.377 18.8123,20.0145ZM17.3998,8.602C16.2998,8.602 15.3998,7.7021 15.3998,6.602C15.3998,5.502 16.2998,4.602 17.3998,4.602C18.4998,4.602 19.3998,5.502 19.3998,6.602C19.3998,7.7021 18.4998,8.602 17.3998,8.602ZM5.3998,14.6021C6.4998,14.6021 7.3998,13.7021 7.3998,12.6021C7.3998,11.5021 6.4998,10.6021 5.3998,10.6021C4.2998,10.6021 3.3998,11.5021 3.3998,12.6021C3.3998,13.7021 4.2998,14.6021 5.3998,14.6021ZM7.3998,18.6021C7.3998,19.7021 6.4998,20.6021 5.3998,20.6021C4.2998,20.6021 3.3998,19.7021 3.3998,18.6021C3.3998,17.5021 4.2998,16.6021 5.3998,16.6021C6.4998,16.6021 7.3998,17.5021 7.3998,18.6021ZM13.3998,18.6021C13.3998,19.7021 12.4998,20.6021 11.3998,20.6021C10.2998,20.6021 9.3998,19.7021 9.3998,18.6021C9.3998,17.5021 10.2998,16.6021 11.3998,16.6021C12.4998,16.6021 13.3998,17.5021 13.3998,18.6021ZM13.3999,6.602C13.3999,7.547 12.7357,8.3444 11.8511,8.5504L9.4516,6.1509C9.6576,5.2663 10.4549,4.602 11.3999,4.602C12.4999,4.602 13.3999,5.502 13.3999,6.602ZM17.8511,14.5504C18.7357,14.3444 19.3999,13.547 19.3999,12.6021C19.3999,11.5021 18.4999,10.6021 17.3999,10.6021C16.4549,10.6021 15.6576,11.2663 15.4516,12.1509L17.8511,14.5504Z"/>
-</vector>
diff --git a/core/res/res/drawable/ic_sharing_disabled.xml b/core/res/res/drawable/ic_sharing_disabled.xml
deleted file mode 100644
index d488cdbb8b7b..000000000000
--- a/core/res/res/drawable/ic_sharing_disabled.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<vector android:height="32dp" android:viewportHeight="24"
- android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@color/resolver_empty_state_icon" android:fillType="evenOdd" android:pathData="M19.7225,20.9245L21.2011,22.4031L22.4032,21.201L2.8022,1.6L1.6001,2.8021L8.1265,9.3284L7.64,9.612C7.1,9.112 6.39,8.802 5.6,8.802C3.94,8.802 2.6,10.142 2.6,11.802C2.6,13.462 3.94,14.802 5.6,14.802C6.39,14.802 7.1,14.492 7.64,13.992L14.69,18.112C14.64,18.332 14.6,18.562 14.6,18.802C14.6,20.462 15.94,21.802 17.6,21.802C18.43,21.802 19.18,21.467 19.7225,20.9245ZM16.8938,18.0958L18.3063,19.5083C18.125,19.6895 17.875,19.802 17.6,19.802C17.05,19.802 16.6,19.352 16.6,18.802C16.6,18.527 16.7125,18.277 16.8938,18.0958ZM15.1871,16.3891L9.3881,10.5901L8.51,11.102C8.56,11.332 8.6,11.562 8.6,11.802C8.6,12.042 8.56,12.272 8.51,12.502L15.1871,16.3891ZM15.56,6.992L12.4382,8.8119L11.1766,7.5503L14.69,5.502C14.64,5.282 14.6,5.042 14.6,4.802C14.6,3.142 15.94,1.802 17.6,1.802C19.26,1.802 20.6,3.142 20.6,4.802C20.6,6.462 19.26,7.802 17.6,7.802C16.81,7.802 16.09,7.492 15.56,6.992ZM18.6,4.802C18.6,4.252 18.15,3.802 17.6,3.802C17.05,3.802 16.6,4.252 16.6,4.802C16.6,5.352 17.05,5.802 17.6,5.802C18.15,5.802 18.6,5.352 18.6,4.802ZM5.6,12.802C5.05,12.802 4.6,12.352 4.6,11.802C4.6,11.252 5.05,10.802 5.6,10.802C6.15,10.802 6.6,11.252 6.6,11.802C6.6,12.352 6.15,12.802 5.6,12.802Z"/>
-</vector>
diff --git a/core/res/res/drawable/ic_work_apps_off.xml b/core/res/res/drawable/ic_work_apps_off.xml
deleted file mode 100644
index f62eb2769bef..000000000000
--- a/core/res/res/drawable/ic_work_apps_off.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<vector android:height="32dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@color/resolver_empty_state_icon" android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v1.17L10.83,8L20,8v9.17l1.98,1.98c0,-0.05 0.02,-0.1 0.02,-0.16L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2zM19,19L8,8 6,6 2.81,2.81 1.39,4.22 3.3,6.13C2.54,6.41 2.01,7.14 2.01,8L2,19c0,1.11 0.89,2 2,2h14.17l1.61,1.61 1.41,-1.41 -0.37,-0.37L19,19zM4,19L4,8h1.17l11,11L4,19z"/>
-</vector> \ No newline at end of file
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index 8594c33c3082..d5174f8b4cd9 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -30,36 +30,30 @@
android:paddingTop="@dimen/resolver_empty_state_container_padding_top"
android:paddingBottom="@dimen/resolver_empty_state_container_padding_bottom"
android:gravity="center_horizontal">
- <ImageView
- android:id="@+id/resolver_empty_state_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_centerHorizontal="true" />
<TextView
android:id="@+id/resolver_empty_state_title"
android:layout_below="@+id/resolver_empty_state_icon"
- android:layout_marginTop="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textColor="@color/resolver_empty_state_text"
- android:textSize="14sp"
+ android:textSize="18sp"
android:gravity="center_horizontal"
android:layout_centerHorizontal="true" />
<TextView
android:id="@+id/resolver_empty_state_subtitle"
android:layout_below="@+id/resolver_empty_state_title"
- android:layout_marginTop="4dp"
+ android:layout_marginTop="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/resolver_empty_state_text"
- android:textSize="12sp"
+ android:textSize="14sp"
android:gravity="center_horizontal"
android:layout_centerHorizontal="true" />
<Button
android:id="@+id/resolver_empty_state_button"
android:layout_below="@+id/resolver_empty_state_subtitle"
- android:layout_marginTop="8dp"
+ android:layout_marginTop="16dp"
android:text="@string/resolver_switch_on_work"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index d9d1a082ed4a..b73f96e3696c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -959,7 +959,7 @@
<dimen name="resolver_max_collapsed_height_with_default_with_tabs">300dp</dimen>
<dimen name="resolver_tab_text_size">14sp</dimen>
<dimen name="resolver_title_padding_bottom">0dp</dimen>
- <dimen name="resolver_empty_state_container_padding_top">8dp</dimen>
+ <dimen name="resolver_empty_state_container_padding_top">48dp</dimen>
<dimen name="resolver_empty_state_container_padding_bottom">8dp</dimen>
<dimen name="chooser_action_button_icon_size">18dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8226ec435533..55c1cb9af013 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4303,9 +4303,6 @@
<java-symbol type="string" name="resolver_no_work_apps_available" />
<java-symbol type="string" name="resolver_no_personal_apps_available" />
<java-symbol type="string" name="resolver_switch_on_work" />
- <java-symbol type="drawable" name="ic_work_apps_off" />
- <java-symbol type="drawable" name="ic_sharing_disabled" />
- <java-symbol type="drawable" name="ic_no_apps" />
<java-symbol type="drawable" name="ic_screenshot_edit" />
<java-symbol type="dimen" name="resolver_empty_state_height" />
<java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" />
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index bfa6ce5d36c5..74cad1aaa057 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -881,7 +881,7 @@ public class RippleDrawable extends LayerDrawable {
mAddRipple = false;
if (mRunningAnimations.size() > 0 && !addRipple) {
// update paint when view is invalidated
- getRipplePaint();
+ updateRipplePaint();
}
drawContent(canvas);
drawPatternedBackground(canvas, cx, cy);
@@ -940,7 +940,7 @@ public class RippleDrawable extends LayerDrawable {
startBackgroundAnimation();
}
if (mBackgroundOpacity == 0) return;
- Paint p = getRipplePaint();
+ Paint p = updateRipplePaint();
float newOpacity = mBackgroundOpacity;
final int origAlpha = p.getAlpha();
final int alpha = Math.min((int) (origAlpha * newOpacity + 0.5f), 255);
@@ -968,7 +968,7 @@ public class RippleDrawable extends LayerDrawable {
@NonNull
private RippleAnimationSession.AnimationProperties<Float, Paint> createAnimationProperties(
float x, float y, float cx, float cy, float w, float h) {
- Paint p = new Paint(getRipplePaint());
+ Paint p = new Paint(updateRipplePaint());
float radius = getComputedRadius();
RippleAnimationSession.AnimationProperties<Float, Paint> properties;
RippleShader shader = new RippleShader();
@@ -1108,11 +1108,6 @@ public class RippleDrawable extends LayerDrawable {
drawContent(mMaskCanvas);
}
mMaskCanvas.restoreToCount(saveCount);
- if (mState.mRippleStyle == STYLE_PATTERNED) {
- for (int i = 0; i < mRunningAnimations.size(); i++) {
- mRunningAnimations.get(i).getProperties().getShader().setShader(mMaskShader);
- }
- }
}
private int getMaskType() {
@@ -1169,7 +1164,7 @@ public class RippleDrawable extends LayerDrawable {
final float y = mHotspotBounds.exactCenterY();
canvas.translate(x, y);
- final Paint p = getRipplePaint();
+ final Paint p = updateRipplePaint();
if (background != null && background.isVisible()) {
background.draw(canvas, p);
@@ -1194,7 +1189,7 @@ public class RippleDrawable extends LayerDrawable {
}
@UnsupportedAppUsage
- Paint getRipplePaint() {
+ Paint updateRipplePaint() {
if (mRipplePaint == null) {
mRipplePaint = new Paint();
mRipplePaint.setAntiAlias(true);
@@ -1215,6 +1210,12 @@ public class RippleDrawable extends LayerDrawable {
mMaskMatrix.setTranslate(bounds.left - x, bounds.top - y);
}
mMaskShader.setLocalMatrix(mMaskMatrix);
+
+ if (mState.mRippleStyle == STYLE_PATTERNED) {
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ mRunningAnimations.get(i).getProperties().getShader().setShader(mMaskShader);
+ }
+ }
}
// Grab the color for the current state and cut the alpha channel in
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 0f376957c8ff..1655fba93878 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -252,7 +252,7 @@ class RippleForeground extends RippleComponent {
mPropX = CanvasProperty.createFloat(getCurrentX());
mPropY = CanvasProperty.createFloat(getCurrentY());
mPropRadius = CanvasProperty.createFloat(getCurrentRadius());
- final Paint paint = mOwner.getRipplePaint();
+ final Paint paint = mOwner.updateRipplePaint();
mPropPaint = CanvasProperty.createPaint(paint);
final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mTargetRadius);
@@ -290,7 +290,7 @@ class RippleForeground extends RippleComponent {
opacity.setInterpolator(LINEAR_INTERPOLATOR);
opacity.addListener(mAnimationListener);
opacity.setStartDelay(computeFadeOutDelay());
- opacity.setStartValue(mOwner.getRipplePaint().getAlpha());
+ opacity.setStartValue(mOwner.updateRipplePaint().getAlpha());
mPendingHwAnimators.add(opacity);
invalidateSelf();
}
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 e50b9a1cd469..81caf7786cf5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -16,7 +16,6 @@
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;
@@ -49,7 +48,8 @@ import java.util.concurrent.Executor;
class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
/** Mapping from the client assigned unique token to the {@link TaskFragmentInfo}. */
- private final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
+ @VisibleForTesting
+ final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
/**
* Mapping from the client assigned unique token to the TaskFragment parent
@@ -120,25 +120,29 @@ 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) {
+ @Nullable Bundle activityOptions, @NonNull SplitRule rule,
+ @WindowingMode int windowingMode) {
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, WINDOWING_MODE_MULTI_WINDOW, launchingActivity);
+ launchingFragmentBounds, windowingMode, launchingActivity);
}
// Create a TaskFragment for the secondary activity.
createTaskFragmentAndStartActivity(wct, secondaryFragmentToken, ownerToken,
- secondaryFragmentBounds, WINDOWING_MODE_MULTI_WINDOW, activityIntent,
+ secondaryFragmentBounds, windowingMode, activityIntent,
activityOptions);
// Set adjacent to each other so that the containers below will be invisible.
@@ -153,6 +157,7 @@ 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);
}
/**
@@ -255,6 +260,15 @@ 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 2328f76a7130..b370e59ac7c8 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 = isInPictureInPicture(taskContainer.getConfiguration());
+ final boolean wasInPip = taskContainer.isInPictureInPicture();
final boolean isInPIp = isInPictureInPicture(config);
- taskContainer.setConfiguration(config);
+ taskContainer.setWindowingMode(config.windowConfiguration.getWindowingMode());
// We need to check the animation override when enter/exit PIP or has bounds changed.
boolean shouldUpdateAnimationOverride = wasInPip != isInPIp;
@@ -278,8 +278,9 @@ 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()) {
- // We don't know about the Task bounds yet.
+ if (!taskContainer.isTaskBoundsInitialized()
+ || !taskContainer.isWindowingModeInitialized()) {
+ // We don't know about the Task bounds/windowingMode yet.
return;
}
@@ -293,7 +294,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private boolean supportSplit(@NonNull TaskContainer taskContainer) {
// No split inside PIP.
- if (isInPictureInPicture(taskContainer.getConfiguration())) {
+ if (taskContainer.isInPictureInPicture()) {
return false;
}
// Check if the parent container bounds can support any split rule.
@@ -461,8 +462,12 @@ 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 ee5a322eed4f..d8423499730f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -16,10 +16,11 @@
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;
import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
@@ -111,13 +112,16 @@ 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, primaryContainer.getTaskId());
+ null /* activity */, primaryActivity, taskId);
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,
- WINDOWING_MODE_MULTI_WINDOW);
+ windowingMode);
secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
// Set adjacent to each other so that the containers below will be invisible.
@@ -173,7 +177,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final WindowContainerTransaction wct = new WindowContainerTransaction();
createTaskFragment(wct, newContainer.getTaskFragmentToken(),
- launchingActivity.getActivityToken(), new Rect(), WINDOWING_MODE_MULTI_WINDOW);
+ launchingActivity.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED);
applyTransaction(wct);
return newContainer;
@@ -189,15 +193,17 @@ 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();
if (container == null || container == containerToAvoid) {
- container = mController.newContainer(activity, activity.getTaskId());
-
+ container = mController.newContainer(activity, taskId);
+ final int windowingMode = mController.getTaskContainer(taskId)
+ .getWindowingModeForSplitTaskFragment(bounds);
final TaskFragmentCreationParams fragmentOptions =
createFragmentOptions(
container.getTaskFragmentToken(),
activity.getActivityToken(),
bounds,
- WINDOWING_MODE_MULTI_WINDOW);
+ windowingMode);
wct.createTaskFragment(fragmentOptions);
wct.reparentActivityToTaskFragment(container.getTaskFragmentToken(),
@@ -206,6 +212,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
container.setLastRequestedBounds(bounds);
} else {
resizeTaskFragmentIfRegistered(wct, container, bounds);
+ final int windowingMode = mController.getTaskContainer(taskId)
+ .getWindowingModeForSplitTaskFragment(bounds);
+ updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
}
return container;
@@ -237,14 +246,17 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
launchingActivity.getTaskId());
}
+ final int taskId = primaryContainer.getTaskId();
TaskFragmentContainer secondaryContainer = mController.newContainer(null /* activity */,
- launchingActivity, primaryContainer.getTaskId());
+ launchingActivity, taskId);
+ final int windowingMode = mController.getTaskContainer(taskId)
+ .getWindowingModeForSplitTaskFragment(primaryRectBounds);
final WindowContainerTransaction wct = new WindowContainerTransaction();
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
rule);
startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
- activityIntent, activityOptions, rule);
+ activityIntent, activityOptions, rule, windowingMode);
if (isPlaceholder) {
// When placeholder is launched in split, we should keep the focus on the primary.
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
@@ -292,6 +304,12 @@ 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,
@@ -323,6 +341,15 @@ 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 be793018d969..3c0762d81494 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -16,9 +16,14 @@
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.content.res.Configuration;
+import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArraySet;
@@ -37,9 +42,9 @@ class TaskContainer {
/** Available window bounds of this Task. */
private final Rect mTaskBounds = new Rect();
- /** Configuration of the Task. */
- @Nullable
- private Configuration mConfiguration;
+ /** Windowing mode of this Task. */
+ @WindowingMode
+ private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
/** Active TaskFragments in this Task. */
final List<TaskFragmentContainer> mContainers = new ArrayList<>();
@@ -81,13 +86,42 @@ class TaskContainer {
return !mTaskBounds.isEmpty();
}
- @Nullable
- Configuration getConfiguration() {
- return mConfiguration;
+ 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;
}
- void setConfiguration(@Nullable Configuration configuration) {
- mConfiguration = configuration;
+ boolean isInPictureInPicture() {
+ return mWindowingMode == WINDOWING_MODE_PINNED;
}
/** 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 b06ce4c19d5c..1f12c4484159 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,15 +16,23 @@
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;
@@ -35,6 +43,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+
/**
* Test class for {@link JetpackTaskFragmentOrganizer}.
*
@@ -48,6 +58,8 @@ public class JetpackTaskFragmentOrganizerTest {
private static final int TASK_ID = 10;
@Mock
+ private WindowContainerTransaction mTransaction;
+ @Mock
private JetpackTaskFragmentOrganizer.TaskFragmentCallback mCallback;
private JetpackTaskFragmentOrganizer mOrganizer;
@@ -91,4 +103,24 @@ 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 9fb08dffbab8..c7feb7e59de3 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,6 +16,13 @@
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;
@@ -64,6 +71,56 @@ 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/res/animator/tv_pip_menu_action_button_animator.xml b/libs/WindowManager/Shell/res/animator/tv_pip_menu_action_button_animator.xml
new file mode 100644
index 000000000000..7475abac4695
--- /dev/null
+++ b/libs/WindowManager/Shell/res/animator/tv_pip_menu_action_button_animator.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_focused="true">
+ <set>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleX"
+ android:valueFrom="1.0"
+ android:valueTo="1.1"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleY"
+ android:valueFrom="1.0"
+ android:valueTo="1.1"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+ <item android:state_focused="false">
+ <set>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleX"
+ android:valueFrom="1.1"
+ android:valueTo="1.0"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleY"
+ android:valueFrom="1.1"
+ android:valueTo="1.0"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index dbd5a9b370ab..7a3ee23d8cdc 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -21,10 +21,13 @@
android:layout_height="match_parent"
android:gravity="center|top">
+ <!-- Matches the PiP app content -->
<View
android:id="@+id/tv_pip"
android:layout_width="0dp"
android:layout_height="0dp"
+ android:alpha="0"
+ android:background="@color/tv_pip_menu_background"
android:layout_marginTop="@dimen/pip_menu_outer_space"
android:layout_marginStart="@dimen/pip_menu_outer_space"
android:layout_marginEnd="@dimen/pip_menu_outer_space"/>
@@ -33,7 +36,6 @@
android:id="@+id/tv_pip_menu_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center_horizontal"
android:layout_alignTop="@+id/tv_pip"
android:layout_alignStart="@+id/tv_pip"
android:layout_alignEnd="@+id/tv_pip"
@@ -49,15 +51,12 @@
android:layout_alignStart="@+id/tv_pip"
android:layout_alignEnd="@+id/tv_pip"
android:layout_alignBottom="@+id/tv_pip"
- android:gravity="center_vertical"
android:scrollbars="none">
<LinearLayout
android:id="@+id/tv_pip_menu_action_buttons"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center"
- android:layout_gravity="center"
+ android:layout_height="wrap_content"
android:orientation="horizontal"
android:alpha="0">
@@ -73,11 +72,20 @@
android:text="@string/pip_fullscreen" />
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ android:id="@+id/tv_pip_menu_close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_close_white"
+ android:text="@string/pip_close" />
+
+ <!-- More TvPipMenuActionButtons may be added here at runtime. -->
+
+ <com.android.wm.shell.pip.tv.TvPipMenuActionButton
android:id="@+id/tv_pip_menu_move_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pip_ic_move_white"
- android:text="@String/pip_move" />
+ android:text="@string/pip_move" />
<com.android.wm.shell.pip.tv.TvPipMenuActionButton
android:id="@+id/tv_pip_menu_expand_button"
@@ -87,15 +95,6 @@
android:visibility="gone"
android:text="@string/pip_collapse" />
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
- android:id="@+id/tv_pip_menu_close_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_close_white"
- android:text="@string/pip_close" />
-
- <!-- More TvPipMenuActionButtons may be added here at runtime. -->
-
<Space
android:layout_width="@dimen/pip_menu_button_wrapper_margin"
android:layout_height="@dimen/pip_menu_button_wrapper_margin"/>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
index a86a14525022..db96d8de4094 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
@@ -20,10 +20,17 @@
android:id="@+id/button"
android:layout_width="@dimen/pip_menu_button_size"
android:layout_height="@dimen/pip_menu_button_size"
- android:layout_margin="@dimen/pip_menu_button_margin"
- android:background="@drawable/tv_pip_button_bg"
+ android:padding="@dimen/pip_menu_button_margin"
+ android:stateListAnimator="@animator/tv_pip_menu_action_button_animator"
android:focusable="true">
+ <View android:id="@+id/background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:duplicateParentState="true"
+ android:background="@drawable/tv_pip_button_bg"/>
+
<ImageView android:id="@+id/icon"
android:layout_width="@dimen/pip_menu_icon_size"
android:layout_height="@dimen/pip_menu_icon_size"
diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
index 776b18ecc01b..02e726fbc3bf 100644
--- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
@@ -16,7 +16,7 @@
-->
<resources>
<!-- The dimensions to user for picture-in-picture action buttons. -->
- <dimen name="pip_menu_button_size">40dp</dimen>
+ <dimen name="pip_menu_button_size">48dp</dimen>
<dimen name="pip_menu_button_radius">20dp</dimen>
<dimen name="pip_menu_icon_size">20dp</dimen>
<dimen name="pip_menu_button_margin">4dp</dimen>
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/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 1dd5ebcd993e..72c8141c8f2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -30,6 +30,7 @@ import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -41,6 +42,7 @@ import com.android.wm.shell.pip.tv.TvPipBoundsState;
import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
+import com.android.wm.shell.pip.tv.TvPipTaskOrganizer;
import com.android.wm.shell.pip.tv.TvPipTransition;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
@@ -67,6 +69,7 @@ public abstract class TvPipModule {
PipTransitionController pipTransitionController,
TvPipNotificationController tvPipNotificationController,
TaskStackListenerImpl taskStackListener,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
WindowManagerShellWrapper windowManagerShellWrapper,
@ShellMainThread ShellExecutor mainExecutor,
@@ -82,6 +85,7 @@ public abstract class TvPipModule {
pipMediaController,
tvPipNotificationController,
taskStackListener,
+ pipParamsChangedForwarder,
displayController,
windowManagerShellWrapper,
mainExecutor,
@@ -163,15 +167,22 @@ public abstract class TvPipModule {
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenControllerOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
- return new PipTaskOrganizer(context,
+ return new TvPipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, tvPipBoundsState, tvPipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenControllerOptional,
+ pipTransitionController, pipParamsChangedForwarder, splitScreenControllerOptional,
displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
+
+ @WMSingleton
+ @Provides
+ static PipParamsChangedForwarder providePipParamsChangedForwarder() {
+ return new PipParamsChangedForwarder();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e43f4fc34adf..7513e5129ade 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -54,6 +54,7 @@ import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -216,14 +217,14 @@ public class WMShellModule {
PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<OneHandedController> oneHandedController,
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create(context, displayController,
pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState,
- pipMotionHelper,
- pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTouchHandler,
- pipTransitionController, windowManagerShellWrapper, taskStackListener,
- oneHandedController, mainExecutor));
+ pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
+ pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
+ taskStackListener, pipParamsChangedForwarder, oneHandedController, mainExecutor));
}
@WMSingleton
@@ -297,6 +298,7 @@ public class WMShellModule {
PipAnimationController pipAnimationController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipTransitionController pipTransitionController,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenControllerOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@@ -304,7 +306,7 @@ public class WMShellModule {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenControllerOptional,
+ pipTransitionController, pipParamsChangedForwarder, splitScreenControllerOptional,
displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
@@ -391,4 +393,10 @@ public class WMShellModule {
rootTaskDisplayAreaOrganizer
);
}
+
+ @WMSingleton
+ @Provides
+ static PipParamsChangedForwarder providePipParamsChangedForwarder() {
+ return new PipParamsChangedForwarder();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
index 87eca74acd0b..ce98458c0575 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
@@ -16,9 +16,7 @@
package com.android.wm.shell.pip;
-import android.app.RemoteAction;
import android.content.ComponentName;
-import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
import android.view.IPinnedTaskListener;
import android.view.WindowManagerGlobal;
@@ -72,31 +70,12 @@ public class PinnedStackListenerForwarder {
}
}
- private void onActionsChanged(ParceledListSlice<RemoteAction> actions,
- RemoteAction closeAction) {
- for (PinnedTaskListener listener : mListeners) {
- listener.onActionsChanged(actions, closeAction);
- }
- }
-
private void onActivityHidden(ComponentName componentName) {
for (PinnedTaskListener listener : mListeners) {
listener.onActivityHidden(componentName);
}
}
- private void onAspectRatioChanged(float aspectRatio) {
- for (PinnedTaskListener listener : mListeners) {
- listener.onAspectRatioChanged(aspectRatio);
- }
- }
-
- private void onExpandedAspectRatioChanged(float aspectRatio) {
- for (PinnedTaskListener listener : mListeners) {
- listener.onExpandedAspectRatioChanged(aspectRatio);
- }
- }
-
@BinderThread
private class PinnedTaskListenerImpl extends IPinnedTaskListener.Stub {
@Override
@@ -114,35 +93,11 @@ public class PinnedStackListenerForwarder {
}
@Override
- public void onActionsChanged(ParceledListSlice<RemoteAction> actions,
- RemoteAction closeAction) {
- mMainExecutor.execute(() -> {
- PinnedStackListenerForwarder.this.onActionsChanged(actions, closeAction);
- });
- }
-
- @Override
public void onActivityHidden(ComponentName componentName) {
mMainExecutor.execute(() -> {
PinnedStackListenerForwarder.this.onActivityHidden(componentName);
});
}
-
- @Override
- public void onAspectRatioChanged(float aspectRatio) {
- mMainExecutor.execute(() -> {
- PinnedStackListenerForwarder.this.onAspectRatioChanged(aspectRatio);
- });
- }
-
- @Override
- public void onExpandedAspectRatioChanged(float aspectRatio) {
- mMainExecutor.execute(() -> {
- PinnedStackListenerForwarder.this.onExpandedAspectRatioChanged(aspectRatio);
- });
- }
-
-
}
/**
@@ -154,13 +109,6 @@ public class PinnedStackListenerForwarder {
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
- public void onActionsChanged(ParceledListSlice<RemoteAction> actions,
- RemoteAction closeAction) {}
-
public void onActivityHidden(ComponentName componentName) {}
-
- public void onAspectRatioChanged(float aspectRatio) {}
-
- public void onExpandedAspectRatioChanged(float aspectRatio) {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index f6ff294b4328..16f1d1c2944c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -26,12 +26,13 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.RemoteAction;
-import android.content.pm.ParceledListSlice;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import java.util.List;
+
/**
* Interface to allow {@link com.android.wm.shell.pip.PipTaskOrganizer} to call into
* PiP menu when certain events happen (task appear/vanish, PiP move, etc.)
@@ -66,7 +67,7 @@ public interface PipMenuController {
/**
* Given a set of actions, update the menu.
*/
- void setAppActions(ParceledListSlice<RemoteAction> appActions, RemoteAction closeAction);
+ void setAppActions(List<RemoteAction> appActions, RemoteAction closeAction);
/**
* Resize the PiP menu with the given bounds. The PiP SurfaceControl is given if there is a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipParamsChangedForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipParamsChangedForwarder.java
new file mode 100644
index 000000000000..21ba85459c48
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipParamsChangedForwarder.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip;
+
+import android.app.RemoteAction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Forwards changes to the Picture-in-Picture params to all listeners.
+ */
+public class PipParamsChangedForwarder {
+
+ private final List<PipParamsChangedCallback>
+ mPipParamsChangedListeners = new ArrayList<>();
+
+ /**
+ * Add a listener that implements at least one of the callbacks.
+ */
+ public void addListener(PipParamsChangedCallback listener) {
+ if (mPipParamsChangedListeners.contains(listener)) {
+ return;
+ }
+ mPipParamsChangedListeners.add(listener);
+ }
+
+ /**
+ * Call to notify all listeners of the changed aspect ratio.
+ */
+ public void notifyAspectRatioChanged(float aspectRatio) {
+ for (PipParamsChangedCallback listener : mPipParamsChangedListeners) {
+ listener.onAspectRatioChanged(aspectRatio);
+ }
+ }
+
+ /**
+ * Call to notify all listeners of the changed expanded aspect ratio.
+ */
+ public void notifyExpandedAspectRatioChanged(float aspectRatio) {
+ for (PipParamsChangedCallback listener : mPipParamsChangedListeners) {
+ listener.onExpandedAspectRatioChanged(aspectRatio);
+ }
+ }
+
+ /**
+ * Call to notify all listeners of the changed title.
+ */
+ public void notifyTitleChanged(CharSequence title) {
+ String value = title == null ? null : title.toString();
+ for (PipParamsChangedCallback listener : mPipParamsChangedListeners) {
+ listener.onTitleChanged(value);
+ }
+ }
+
+ /**
+ * Call to notify all listeners of the changed subtitle.
+ */
+ public void notifySubtitleChanged(CharSequence subtitle) {
+ String value = subtitle == null ? null : subtitle.toString();
+ for (PipParamsChangedCallback listener : mPipParamsChangedListeners) {
+ listener.onSubtitleChanged(value);
+ }
+ }
+
+ /**
+ * Call to notify all listeners of the changed app actions or close action.
+ */
+ public void notifyActionsChanged(List<RemoteAction> actions, RemoteAction closeAction) {
+ for (PipParamsChangedCallback listener : mPipParamsChangedListeners) {
+ listener.onActionsChanged(actions, closeAction);
+ }
+ }
+
+ /**
+ * Contains callbacks for PiP params changes. Subclasses can choose which changes they want to
+ * listen to by only overriding those selectively.
+ */
+ public interface PipParamsChangedCallback {
+
+ /**
+ * Called if aspect ratio changed.
+ */
+ default void onAspectRatioChanged(float aspectRatio) {
+ }
+
+ /**
+ * Called if expanded aspect ratio changed.
+ */
+ default void onExpandedAspectRatioChanged(float aspectRatio) {
+ }
+
+ /**
+ * Called if either the actions or the close action changed.
+ */
+ default void onActionsChanged(List<RemoteAction> actions, RemoteAction closeAction) {
+ }
+
+ /**
+ * Called if the title changed.
+ */
+ default void onTitleChanged(String title) {
+ }
+
+ /**
+ * Called if the subtitle changed.
+ */
+ default void onSubtitleChanged(String subtitle) {
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index fbdf6f0b539f..4690e16bc385 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -62,7 +62,6 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.util.Rational;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -127,6 +126,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final @NonNull PipMenuController mPipMenuController;
private final PipAnimationController mPipAnimationController;
private final PipTransitionController mPipTransitionController;
+ protected final PipParamsChangedForwarder mPipParamsChangedForwarder;
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final int mEnterAnimationDuration;
private final int mExitAnimationDuration;
@@ -219,7 +219,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private long mLastOneShotAlphaAnimationTime;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
- private PictureInPictureParams mPictureInPictureParams;
+ protected PictureInPictureParams mPictureInPictureParams;
private IntConsumer mOnDisplayIdChangeCallback;
/**
* The end transaction of PiP animation for switching between PiP and fullscreen with
@@ -259,6 +259,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@NonNull PipTransitionController pipTransitionController,
+ @NonNull PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@@ -271,6 +272,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipBoundsAlgorithm = boundsHandler;
mPipMenuController = pipMenuController;
mPipTransitionController = pipTransitionController;
+ mPipParamsChangedForwarder = pipParamsChangedForwarder;
mEnterAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
mExitAnimationDuration = context.getResources()
@@ -559,6 +561,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
mTaskInfo.topActivityInfo);
+ if (mPictureInPictureParams != null) {
+ mPipParamsChangedForwarder.notifyActionsChanged(mPictureInPictureParams.getActions(),
+ mPictureInPictureParams.getCloseAction());
+ mPipParamsChangedForwarder.notifyTitleChanged(
+ mPictureInPictureParams.getTitle());
+ mPipParamsChangedForwarder.notifySubtitleChanged(
+ mPictureInPictureParams.getSubtitle());
+ }
mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
@@ -819,17 +829,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipBoundsState.setOverrideMinSize(
mPipBoundsAlgorithm.getMinimalSize(info.topActivityInfo));
final PictureInPictureParams newParams = info.pictureInPictureParams;
- if (newParams == null || !applyPictureInPictureParams(newParams)) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Ignored onTaskInfoChanged with PiP param: %s", TAG, newParams);
+
+ // mPictureInPictureParams is only null if there is no PiP
+ if (newParams == null || mPictureInPictureParams == null) {
return;
}
- // Aspect ratio changed, re-calculate bounds if valid.
- final Rect destinationBounds = mPipBoundsAlgorithm.getAdjustedDestinationBounds(
- mPipBoundsState.getBounds(), mPipBoundsState.getAspectRatio());
- Objects.requireNonNull(destinationBounds, "Missing destination bounds");
- scheduleAnimateResizePip(destinationBounds, mEnterAnimationDuration,
- null /* updateBoundsCallback */);
+ applyNewPictureInPictureParams(newParams);
+ mPictureInPictureParams = newParams;
}
@Override
@@ -1076,20 +1082,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
/**
- * @return {@code true} if the aspect ratio is changed since no other parameters within
- * {@link PictureInPictureParams} would affect the bounds.
+ * Handles all changes to the PictureInPictureParams.
*/
- private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) {
- final Rational currentAspectRatio =
- mPictureInPictureParams != null ? mPictureInPictureParams.getAspectRatio()
- : null;
- final boolean aspectRatioChanged = !Objects.equals(currentAspectRatio,
- params.getAspectRatio());
- mPictureInPictureParams = params;
- if (aspectRatioChanged) {
- mPipBoundsState.setAspectRatio(params.getAspectRatioFloat());
- }
- return aspectRatioChanged;
+ protected void applyNewPictureInPictureParams(@NonNull PictureInPictureParams params) {
+ if (PipUtils.aspectRatioChanged(params.getAspectRatioFloat(),
+ mPictureInPictureParams.getAspectRatioFloat())) {
+ mPipParamsChangedForwarder.notifyAspectRatioChanged(params.getAspectRatioFloat());
+ }
+ if (PipUtils.remoteActionsChanged(params.getActions(), mPictureInPictureParams.getActions())
+ || !PipUtils.remoteActionsMatch(params.getCloseAction(),
+ mPictureInPictureParams.getCloseAction())) {
+ mPipParamsChangedForwarder.notifyActionsChanged(params.getActions(),
+ params.getCloseAction());
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
index d7b69adf1241..c6cf8b8b0566 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
+import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
@@ -29,10 +30,16 @@ import android.util.Pair;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import java.util.List;
+import java.util.Objects;
+
/** A class that includes convenience methods. */
public class PipUtils {
private static final String TAG = "PipUtils";
+ // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
+ private static final double EPSILON = 1e-7;
+
/**
* @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
* The component name may be null if no such activity exists.
@@ -58,4 +65,45 @@ public class PipUtils {
}
return new Pair<>(null, 0);
}
+
+ /**
+ * @return true if the aspect ratios differ
+ */
+ public static boolean aspectRatioChanged(float aspectRatio1, float aspectRatio2) {
+ return Math.abs(aspectRatio1 - aspectRatio2) > EPSILON;
+ }
+
+ /**
+ * Checks whether title, description and intent match.
+ * Comparing icons would be good, but using equals causes false negatives
+ */
+ public static boolean remoteActionsMatch(RemoteAction action1, RemoteAction action2) {
+ if (action1 == action2) return true;
+ if (action1 == null || action2 == null) return false;
+ return Objects.equals(action1.getTitle(), action2.getTitle())
+ && Objects.equals(action1.getContentDescription(), action2.getContentDescription())
+ && Objects.equals(action1.getActionIntent(), action2.getActionIntent());
+ }
+
+ /**
+ * Returns true if the actions in the lists match each other according to {@link
+ * PipUtils#remoteActionsMatch(RemoteAction, RemoteAction)}, including their position.
+ */
+ public static boolean remoteActionsChanged(List<RemoteAction> list1, List<RemoteAction> list2) {
+ if (list1 == null && list2 == null) {
+ return false;
+ }
+ if (list1 == null || list2 == null) {
+ return true;
+ }
+ if (list1.size() != list2.size()) {
+ return true;
+ }
+ for (int i = 0; i < list1.size(); i++) {
+ if (!remoteActionsMatch(list1.get(i), list2.get(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index bbec4eccce3c..4942987742a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -22,7 +22,6 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.RemoteAction;
import android.content.Context;
-import android.content.pm.ParceledListSlice;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -120,9 +119,11 @@ public class PhonePipMenuController implements PipMenuController {
private final SystemWindows mSystemWindows;
private final Optional<SplitScreenController> mSplitScreenController;
private final PipUiEventLogger mPipUiEventLogger;
- private ParceledListSlice<RemoteAction> mAppActions;
+
+ private List<RemoteAction> mAppActions;
private RemoteAction mCloseAction;
- private ParceledListSlice<RemoteAction> mMediaActions;
+ private List<RemoteAction> mMediaActions;
+
private SyncRtSurfaceTransactionApplier mApplier;
private int mMenuState;
@@ -131,7 +132,7 @@ public class PhonePipMenuController implements PipMenuController {
private ActionListener mMediaActionListener = new ActionListener() {
@Override
public void onMediaActionsChanged(List<RemoteAction> mediaActions) {
- mMediaActions = new ParceledListSlice<>(mediaActions);
+ mMediaActions = new ArrayList<>(mediaActions);
updateMenuActions();
}
};
@@ -183,6 +184,9 @@ public class PhonePipMenuController implements PipMenuController {
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
setShellRootAccessibilityWindow();
+
+ // Make sure the initial actions are set
+ updateMenuActions();
}
private void detachPipMenuView() {
@@ -457,7 +461,7 @@ public class PhonePipMenuController implements PipMenuController {
* Sets the menu actions to the actions provided by the current PiP menu.
*/
@Override
- public void setAppActions(ParceledListSlice<RemoteAction> appActions,
+ public void setAppActions(List<RemoteAction> appActions,
RemoteAction closeAction) {
mAppActions = appActions;
mCloseAction = closeAction;
@@ -479,7 +483,7 @@ public class PhonePipMenuController implements PipMenuController {
/**
* @return the best set of actions to show in the PiP menu.
*/
- private ParceledListSlice<RemoteAction> resolveMenuActions() {
+ private List<RemoteAction> resolveMenuActions() {
if (isValidActions(mAppActions)) {
return mAppActions;
}
@@ -491,17 +495,16 @@ public class PhonePipMenuController implements PipMenuController {
*/
private void updateMenuActions() {
if (mPipMenuView != null) {
- final ParceledListSlice<RemoteAction> menuActions = resolveMenuActions();
mPipMenuView.setActions(mPipBoundsState.getBounds(),
- menuActions == null ? null : menuActions.getList(), mCloseAction);
+ resolveMenuActions(), mCloseAction);
}
}
/**
* Returns whether the set of actions are valid.
*/
- private static boolean isValidActions(ParceledListSlice<?> actions) {
- return actions != null && actions.getList().size() > 0;
+ private static boolean isValidActions(List<?> actions) {
+ return actions != null && actions.size() > 0;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 272331b7cd3f..2e8b5b7979d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -40,7 +40,6 @@ import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -80,6 +79,7 @@ import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -88,6 +88,8 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
+import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@@ -113,10 +115,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private PipTouchHandler mTouchHandler;
private PipTransitionController mPipTransitionController;
private TaskStackListenerImpl mTaskStackListener;
+ private PipParamsChangedForwarder mPipParamsChangedForwarder;
private Optional<OneHandedController> mOneHandedController;
protected final PipImpl mImpl;
private final Rect mTmpInsetBounds = new Rect();
+ private final int mEnterAnimationDuration;
private boolean mIsInFixedRotation;
private PipAnimationListener mPinnedStackAnimationRecentsCallback;
@@ -270,12 +274,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
@Override
- public void onActionsChanged(ParceledListSlice<RemoteAction> actions,
- RemoteAction closeAction) {
- mMenuController.setAppActions(actions, closeAction);
- }
-
- @Override
public void onActivityHidden(ComponentName componentName) {
if (componentName.equals(mPipBoundsState.getLastPipComponentName())) {
// The activity was removed, we don't want to restore to the reentry state
@@ -283,14 +281,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipBoundsState.setLastPipComponentName(null);
}
}
-
- @Override
- public void onAspectRatioChanged(float aspectRatio) {
- // TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params
- // change.
- mPipBoundsState.setAspectRatio(aspectRatio);
- mTouchHandler.onAspectRatioChanged();
- }
}
/**
@@ -305,6 +295,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<OneHandedController> oneHandedController,
ShellExecutor mainExecutor) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
@@ -315,8 +306,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController,
- phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController,
- windowManagerShellWrapper, taskStackListener, oneHandedController, mainExecutor)
+ phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController,
+ windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
+ oneHandedController, mainExecutor)
.mImpl;
}
@@ -334,6 +326,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<OneHandedController> oneHandedController,
ShellExecutor mainExecutor
) {
@@ -360,6 +353,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mOneHandedController = oneHandedController;
mPipTransitionController = pipTransitionController;
mTaskStackListener = taskStackListener;
+
+ mEnterAnimationDuration = mContext.getResources()
+ .getInteger(R.integer.config_pipEnterAnimationDuration);
+ mPipParamsChangedForwarder = pipParamsChangedForwarder;
+
//TODO: move this to ShellInit when PipController can be injected
mMainExecutor.execute(this::init);
}
@@ -457,6 +455,34 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
});
+ mPipParamsChangedForwarder.addListener(
+ new PipParamsChangedForwarder.PipParamsChangedCallback() {
+ @Override
+ public void onAspectRatioChanged(float ratio) {
+ mPipBoundsState.setAspectRatio(ratio);
+
+ final Rect destinationBounds =
+ mPipBoundsAlgorithm.getAdjustedDestinationBounds(
+ mPipBoundsState.getBounds(),
+ mPipBoundsState.getAspectRatio());
+ Objects.requireNonNull(destinationBounds, "Missing destination bounds");
+ mPipTaskOrganizer.scheduleAnimateResizePip(destinationBounds,
+ mEnterAnimationDuration,
+ null /* updateBoundsCallback */);
+
+ mTouchHandler.onAspectRatioChanged();
+ updateMovementBounds(null /* toBounds */, false /* fromRotation */,
+ false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
+ null /* windowContainerTransaction */);
+ }
+
+ @Override
+ public void onActionsChanged(List<RemoteAction> actions,
+ RemoteAction closeAction) {
+ mMenuController.setAppActions(actions, closeAction);
+ }
+ });
+
mOneHandedController.ifPresent(controller -> {
controller.asOneHanded().registerTransitionCallback(
new OneHandedTransitionCallback() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index a3048bd8fabe..ca22882187d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -77,6 +77,9 @@ public class TvPipBoundsState extends PipBoundsState {
public void setBoundsStateForEntry(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams params, PipBoundsAlgorithm pipBoundsAlgorithm) {
super.setBoundsStateForEntry(componentName, activityInfo, params, pipBoundsAlgorithm);
+ if (params == null) {
+ return;
+ }
setDesiredTvExpandedAspectRatio(params.getExpandedAspectRatioFloat(), true);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 0e1f5a24f4a7..8326588bbbad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -27,7 +27,6 @@ import android.app.RemoteAction;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -48,6 +47,7 @@ import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
@@ -55,6 +55,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
import java.util.Set;
/**
@@ -66,7 +67,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private static final String TAG = "TvPipController";
static final boolean DEBUG = false;
- private static final double EPS = 1e-7;
private static final int NONEXISTENT_TASK_ID = -1;
@Retention(RetentionPolicy.SOURCE)
@@ -127,6 +127,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
PipMediaController pipMediaController,
TvPipNotificationController pipNotificationController,
TaskStackListenerImpl taskStackListener,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
WindowManagerShellWrapper wmShell,
ShellExecutor mainExecutor,
@@ -141,6 +142,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
pipMediaController,
pipNotificationController,
taskStackListener,
+ pipParamsChangedForwarder,
displayController,
wmShell,
mainExecutor,
@@ -157,6 +159,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
PipMediaController pipMediaController,
TvPipNotificationController pipNotificationController,
TaskStackListenerImpl taskStackListener,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
WindowManagerShellWrapper wmShell,
ShellExecutor mainExecutor,
@@ -183,6 +186,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
loadConfigurations();
+ registerPipParamsChangedListener(pipParamsChangedForwarder);
registerTaskStackListenerCallback(taskStackListener);
registerWmShellPinnedStackListener(wmShell);
displayController.addDisplayWindowListener(this);
@@ -380,6 +384,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
animationDuration, rect -> {
mTvPipMenuController.updateExpansionState();
});
+ mTvPipMenuController.onPipTransitionStarted(bounds);
}
/**
@@ -540,6 +545,73 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
});
}
+ private void registerPipParamsChangedListener(PipParamsChangedForwarder provider) {
+ provider.addListener(new PipParamsChangedForwarder.PipParamsChangedCallback() {
+ @Override
+ public void onActionsChanged(List<RemoteAction> actions,
+ RemoteAction closeAction) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onActionsChanged()", TAG);
+
+ mTvPipMenuController.setAppActions(actions, closeAction);
+ mCloseAction = closeAction;
+ }
+
+ @Override
+ public void onAspectRatioChanged(float ratio) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onAspectRatioChanged: %f", TAG, ratio);
+
+ mTvPipBoundsState.setAspectRatio(ratio);
+ if (!mTvPipBoundsState.isTvPipExpanded()) {
+ updatePinnedStackBounds();
+ }
+ }
+
+ @Override
+ public void onExpandedAspectRatioChanged(float ratio) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onExpandedAspectRatioChanged: %f", TAG, ratio);
+
+ mTvPipBoundsState.setDesiredTvExpandedAspectRatio(ratio, false);
+ mTvPipMenuController.updateExpansionState();
+
+ // 1) PiP is expanded and only aspect ratio changed, but wasn't disabled
+ // --> update bounds, but don't toggle
+ if (mTvPipBoundsState.isTvPipExpanded() && ratio != 0) {
+ mTvPipBoundsAlgorithm.updateExpandedPipSize();
+ updatePinnedStackBounds();
+ }
+
+ // 2) PiP is expanded, but expanded PiP was disabled
+ // --> collapse PiP
+ if (mTvPipBoundsState.isTvPipExpanded() && ratio == 0) {
+ int saveGravity = mTvPipBoundsAlgorithm
+ .updateGravityOnExpandToggled(mPreviousGravity, false);
+ if (saveGravity != Gravity.NO_GRAVITY) {
+ mPreviousGravity = saveGravity;
+ }
+ mTvPipBoundsState.setTvPipExpanded(false);
+ updatePinnedStackBounds();
+ }
+
+ // 3) PiP not expanded and not manually collapsed and expand was enabled
+ // --> expand to new ratio
+ if (!mTvPipBoundsState.isTvPipExpanded() && ratio != 0
+ && !mTvPipBoundsState.isTvPipManuallyCollapsed()) {
+ mTvPipBoundsAlgorithm.updateExpandedPipSize();
+ int saveGravity = mTvPipBoundsAlgorithm
+ .updateGravityOnExpandToggled(mPreviousGravity, true);
+ if (saveGravity != Gravity.NO_GRAVITY) {
+ mPreviousGravity = saveGravity;
+ }
+ mTvPipBoundsState.setTvPipExpanded(true);
+ updatePinnedStackBounds();
+ }
+ }
+ });
+ }
+
private void registerWmShellPinnedStackListener(WindowManagerShellWrapper wmShell) {
try {
wmShell.addPinnedStackListener(new PinnedStackListenerForwarder.PinnedTaskListener() {
@@ -563,86 +635,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
updatePinnedStackBounds();
}
}
-
- @Override
- public void onAspectRatioChanged(float ratio) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onAspectRatioChanged: %f", TAG, ratio);
- }
-
- boolean ratioChanged = mTvPipBoundsState.getAspectRatio() != ratio;
- mTvPipBoundsState.setAspectRatio(ratio);
-
- if (!mTvPipBoundsState.isTvPipExpanded() && ratioChanged) {
- updatePinnedStackBounds();
- }
- }
-
- @Override
- public void onExpandedAspectRatioChanged(float ratio) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onExpandedAspectRatioChanged: %f", TAG, ratio);
- }
-
- // 0) No update to the ratio --> don't do anything
-
- if (Math.abs(mTvPipBoundsState.getDesiredTvExpandedAspectRatio() - ratio)
- < EPS) {
- return;
- }
-
- mTvPipBoundsState.setDesiredTvExpandedAspectRatio(ratio, false);
-
- // 1) PiP is expanded and only aspect ratio changed, but wasn't disabled
- // --> update bounds, but don't toggle
- if (mTvPipBoundsState.isTvPipExpanded() && ratio != 0) {
- mTvPipBoundsAlgorithm.updateExpandedPipSize();
- updatePinnedStackBounds();
- }
-
- // 2) PiP is expanded, but expanded PiP was disabled
- // --> collapse PiP
- if (mTvPipBoundsState.isTvPipExpanded() && ratio == 0) {
- int saveGravity = mTvPipBoundsAlgorithm
- .updateGravityOnExpandToggled(mPreviousGravity, false);
- if (saveGravity != Gravity.NO_GRAVITY) {
- mPreviousGravity = saveGravity;
- }
- mTvPipBoundsState.setTvPipExpanded(false);
- updatePinnedStackBounds();
- }
-
- // 3) PiP not expanded and not manually collapsed and expand was enabled
- // --> expand to new ratio
- if (!mTvPipBoundsState.isTvPipExpanded() && ratio != 0
- && !mTvPipBoundsState.isTvPipManuallyCollapsed()) {
- mTvPipBoundsAlgorithm.updateExpandedPipSize();
- int saveGravity = mTvPipBoundsAlgorithm
- .updateGravityOnExpandToggled(mPreviousGravity, true);
- if (saveGravity != Gravity.NO_GRAVITY) {
- mPreviousGravity = saveGravity;
- }
- mTvPipBoundsState.setTvPipExpanded(true);
- updatePinnedStackBounds();
- }
- }
-
- @Override
- public void onMovementBoundsChanged(boolean fromImeAdjustment) {}
-
- @Override
- public void onActionsChanged(ParceledListSlice<RemoteAction> actions,
- RemoteAction closeAction) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onActionsChanged()", TAG);
- }
-
- mTvPipMenuController.setAppActions(actions, closeAction);
- mCloseAction = closeAction;
- }
});
} catch (RemoteException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
index abbc614b4b4f..a09aab666a31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
@@ -33,6 +33,7 @@ import com.android.wm.shell.R;
*/
public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener {
private final ImageView mIconImageView;
+ private final View mButtonBackgroundView;
private final View mButtonView;
private OnClickListener mOnClickListener;
@@ -57,6 +58,7 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
mIconImageView = findViewById(R.id.icon);
mButtonView = findViewById(R.id.button);
+ mButtonBackgroundView = findViewById(R.id.background);
final int[] values = new int[]{android.R.attr.src, android.R.attr.text};
final TypedArray typedArray = context.obtainStyledAttributes(attrs, values, defStyleAttr,
@@ -132,9 +134,17 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
getResources().getColorStateList(
isCustomCloseAction ? R.color.tv_pip_menu_close_icon
: R.color.tv_pip_menu_icon));
- mButtonView.setBackgroundTintList(getResources()
+ mButtonBackgroundView.setBackgroundTintList(getResources()
.getColorStateList(isCustomCloseAction ? R.color.tv_pip_menu_close_icon_bg
: R.color.tv_pip_menu_icon_bg));
}
+ @Override
+ public String toString() {
+ if (mButtonView.getContentDescription() == null) {
+ return TvPipMenuActionButton.class.getSimpleName();
+ }
+ return mButtonView.getContentDescription().toString();
+ }
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 2d67254f3610..132c04481bce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -24,7 +24,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ParceledListSlice;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -169,6 +168,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
mPipMenuView.setListener(this);
setUpViewSurfaceZOrder(mPipMenuView, 1);
addPipMenuViewToSystemWindows(mPipMenuView, MENU_WINDOW_TITLE);
+ maybeUpdateMenuViewActions();
}
private void attachPipBackgroundView() {
@@ -199,6 +199,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
void notifyPipAnimating(boolean animating) {
mPipMenuView.setEduTextActive(!animating);
+ if (!animating) {
+ mPipMenuView.onPipTransitionFinished();
+ }
}
void showMovementMenuOnly() {
@@ -235,6 +238,13 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
} else {
mPipMenuView.showButtonsMenu();
}
+ mPipMenuView.updateBounds(mTvPipBoundsState.getBounds());
+ }
+
+ void onPipTransitionStarted(Rect finishBounds) {
+ if (mPipMenuView != null) {
+ mPipMenuView.onPipTransitionStarted(finishBounds);
+ }
}
private void maybeCloseEduText() {
@@ -336,12 +346,12 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
@Override
- public void setAppActions(ParceledListSlice<RemoteAction> actions, RemoteAction closeAction) {
+ public void setAppActions(List<RemoteAction> actions, RemoteAction closeAction) {
if (DEBUG) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: setAppActions()", TAG);
}
- updateAdditionalActionsList(mAppActions, actions.getList(), closeAction);
+ updateAdditionalActionsList(mAppActions, actions, closeAction);
}
private void onMediaActionsChanged(List<RemoteAction> actions) {
@@ -559,7 +569,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
menuBounds.height()));
if (mPipMenuView != null) {
- mPipMenuView.updateLayout(destinationBounds);
+ mPipMenuView.updateBounds(destinationBounds);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 5b0db8c86529..868e45655ba3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -44,8 +44,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -53,12 +55,12 @@ import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
+import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Objects;
/**
* A View that represents Pip Menu on TV. It's responsible for displaying 3 ever-present Pip Menu
@@ -69,6 +71,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private static final String TAG = "TvPipMenuView";
private static final boolean DEBUG = TvPipController.DEBUG;
+ private static final int FIRST_CUSTOM_ACTION_POSITION = 3;
+
@Nullable
private Listener mListener;
@@ -90,15 +94,21 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private final ImageView mArrowDown;
private final ImageView mArrowLeft;
- private final ViewGroup mScrollView;
- private final ViewGroup mHorizontalScrollView;
+ private final ScrollView mScrollView;
+ private final HorizontalScrollView mHorizontalScrollView;
+ private View mFocusedButton;
private Rect mCurrentPipBounds;
+ private boolean mMoveMenuIsVisible;
+ private boolean mButtonMenuIsVisible;
private final TvPipMenuActionButton mExpandButton;
private final TvPipMenuActionButton mCloseButton;
+ private boolean mSwitchingOrientation;
+
private final int mPipMenuFadeAnimationDuration;
+ private final int mResizeAnimationDuration;
public TvPipMenuView(@NonNull Context context) {
this(context, null);
@@ -146,8 +156,11 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
mEduTextView = findViewById(R.id.tv_pip_menu_edu_text);
mEduTextContainerView = findViewById(R.id.tv_pip_menu_edu_text_container);
+ mResizeAnimationDuration = context.getResources().getInteger(
+ R.integer.config_pipResizeAnimationDuration);
mPipMenuFadeAnimationDuration = context.getResources()
.getInteger(R.integer.pip_menu_fade_animation_duration);
+
mPipMenuOuterSpace = context.getResources()
.getDimensionPixelSize(R.dimen.pip_menu_outer_space);
mPipMenuBorderWidth = context.getResources()
@@ -203,43 +216,179 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
heightAnimation.start();
}
- void updateLayout(Rect updatedPipBounds) {
+ void onPipTransitionStarted(Rect finishBounds) {
+ // Fade out content by fading in view on top.
+ if (mCurrentPipBounds != null && finishBounds != null) {
+ boolean ratioChanged = PipUtils.aspectRatioChanged(
+ mCurrentPipBounds.width() / (float) mCurrentPipBounds.height(),
+ finishBounds.width() / (float) finishBounds.height());
+ if (ratioChanged) {
+ mPipView.animate()
+ .alpha(1f)
+ .setInterpolator(TvPipInterpolators.EXIT)
+ .setDuration(mResizeAnimationDuration / 2)
+ .start();
+ }
+ }
+
+ // Update buttons.
+ final boolean vertical = finishBounds.height() > finishBounds.width();
+ final boolean orientationChanged =
+ vertical != (mActionButtonsContainer.getOrientation() == LinearLayout.VERTICAL);
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: update menu layout: %s", TAG, updatedPipBounds.toShortString());
+ "%s: onPipTransitionStarted(), orientation changed %b", TAG, orientationChanged);
+ if (!orientationChanged) {
+ return;
+ }
- boolean previouslyVertical =
- mCurrentPipBounds != null && mCurrentPipBounds.height() > mCurrentPipBounds.width();
- boolean vertical = updatedPipBounds.height() > updatedPipBounds.width();
+ if (mButtonMenuIsVisible) {
+ mSwitchingOrientation = true;
+ mActionButtonsContainer.animate()
+ .alpha(0)
+ .setInterpolator(TvPipInterpolators.EXIT)
+ .setDuration(mResizeAnimationDuration / 2)
+ .withEndAction(() -> {
+ changeButtonScrollOrientation(finishBounds);
+ updateButtonGravity(finishBounds);
+ // Only make buttons visible again in onPipTransitionFinished to keep in
+ // sync with PiP content alpha animation.
+ });
+ } else {
+ changeButtonScrollOrientation(finishBounds);
+ updateButtonGravity(finishBounds);
+ }
+ }
- mCurrentPipBounds = updatedPipBounds;
+ void onPipTransitionFinished() {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: onPipTransitionFinished()", TAG);
+
+ // Fade in content by fading out view on top.
+ mPipView.animate()
+ .alpha(0f)
+ .setDuration(mResizeAnimationDuration / 2)
+ .setInterpolator(TvPipInterpolators.ENTER)
+ .start();
+
+ // Update buttons.
+ if (mSwitchingOrientation) {
+ mActionButtonsContainer.animate()
+ .alpha(1)
+ .setInterpolator(TvPipInterpolators.ENTER)
+ .setDuration(mResizeAnimationDuration / 2);
+ } else {
+ refocusPreviousButton();
+ }
+ mSwitchingOrientation = false;
+ }
+
+ /**
+ * Also updates the button gravity.
+ */
+ void updateBounds(Rect updatedBounds) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: updateLayout, width: %s, height: %s", TAG, updatedBounds.width(),
+ updatedBounds.height());
+ mCurrentPipBounds = updatedBounds;
+ if (!mSwitchingOrientation) {
+ updateButtonGravity(mCurrentPipBounds);
+ }
updatePipFrameBounds();
+ }
+
+ private void changeButtonScrollOrientation(Rect bounds) {
+ final boolean vertical = bounds.height() > bounds.width();
- if (previouslyVertical == vertical) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: no update for menu layout", TAG);
+ final ViewGroup oldScrollView = vertical ? mHorizontalScrollView : mScrollView;
+ final ViewGroup newScrollView = vertical ? mScrollView : mHorizontalScrollView;
+
+ if (oldScrollView.getChildCount() == 1) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: orientation changed", TAG);
+ oldScrollView.removeView(mActionButtonsContainer);
+ oldScrollView.setVisibility(GONE);
+ mActionButtonsContainer.setOrientation(vertical ? LinearLayout.VERTICAL
+ : LinearLayout.HORIZONTAL);
+ newScrollView.addView(mActionButtonsContainer);
+ newScrollView.setVisibility(VISIBLE);
+ if (mFocusedButton != null) {
+ mFocusedButton.requestFocus();
}
+ }
+ }
+
+ /**
+ * Change button gravity based on new dimensions
+ */
+ private void updateButtonGravity(Rect bounds) {
+ final boolean vertical = bounds.height() > bounds.width();
+ // Use Math.max since the possible orientation change might not have been applied yet.
+ final int buttonsSize = Math.max(mActionButtonsContainer.getHeight(),
+ mActionButtonsContainer.getWidth());
+
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: buttons container width: %s, height: %s", TAG,
+ mActionButtonsContainer.getWidth(), mActionButtonsContainer.getHeight());
+
+ final boolean buttonsFit =
+ vertical ? buttonsSize < bounds.height()
+ : buttonsSize < bounds.width();
+ final int buttonGravity = buttonsFit ? Gravity.CENTER
+ : (vertical ? Gravity.CENTER_HORIZONTAL : Gravity.CENTER_VERTICAL);
+
+ final LayoutParams params = (LayoutParams) mActionButtonsContainer.getLayoutParams();
+ params.gravity = buttonGravity;
+ mActionButtonsContainer.setLayoutParams(params);
+
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: vertical: %b, buttonsFit: %b, gravity: %s", TAG, vertical, buttonsFit,
+ Gravity.toString(buttonGravity));
+ }
+
+ private void refocusPreviousButton() {
+ if (mMoveMenuIsVisible || mCurrentPipBounds == null || mFocusedButton == null) {
return;
+ }
+ final boolean vertical = mCurrentPipBounds.height() > mCurrentPipBounds.width();
+
+ if (!mFocusedButton.hasFocus()) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: request focus from: %s", TAG, mFocusedButton);
+ mFocusedButton.requestFocus();
} else {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: change menu layout to vertical: %b", TAG, vertical);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: already focused: %s", TAG, mFocusedButton);
}
+ // Do we need to scroll?
+ final Rect buttonBounds = new Rect();
+ final Rect scrollBounds = new Rect();
if (vertical) {
- mHorizontalScrollView.removeView(mActionButtonsContainer);
- mScrollView.addView(mActionButtonsContainer);
+ mScrollView.getDrawingRect(scrollBounds);
} else {
- mScrollView.removeView(mActionButtonsContainer);
- mHorizontalScrollView.addView(mActionButtonsContainer);
+ mHorizontalScrollView.getDrawingRect(scrollBounds);
}
- mActionButtonsContainer.setOrientation(vertical ? LinearLayout.VERTICAL
- : LinearLayout.HORIZONTAL);
+ mFocusedButton.getHitRect(buttonBounds);
- mScrollView.setVisibility(vertical ? VISIBLE : GONE);
- mHorizontalScrollView.setVisibility(vertical ? GONE : VISIBLE);
+ if (scrollBounds.contains(buttonBounds)) {
+ // Button is already completely visible, don't scroll
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: not scrolling", TAG);
+ return;
+ }
+
+ // Scrolling so the button is visible to the user.
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: scrolling to focused button", TAG);
+
+ if (vertical) {
+ mScrollView.smoothScrollTo((int) mFocusedButton.getX(),
+ (int) mFocusedButton.getY());
+ } else {
+ mHorizontalScrollView.smoothScrollTo((int) mFocusedButton.getX(),
+ (int) mFocusedButton.getY());
+ }
}
Rect getPipMenuContainerBounds(Rect pipBounds) {
@@ -300,6 +449,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
if (DEBUG) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMoveMenu()", TAG);
}
+ mButtonMenuIsVisible = false;
+ mMoveMenuIsVisible = true;
showButtonsMenu(false);
showMovementHints(gravity);
setFrameHighlighted(true);
@@ -310,19 +461,34 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: showButtonsMenu()", TAG);
}
+
+ mButtonMenuIsVisible = true;
+ mMoveMenuIsVisible = false;
showButtonsMenu(true);
hideMovementHints();
setFrameHighlighted(true);
+
+ // Always focus on the first button when opening the menu, except directly after moving.
+ if (mFocusedButton == null) {
+ // Focus on first button (there is a Space at position 0)
+ mFocusedButton = mActionButtonsContainer.getChildAt(1);
+ // Reset scroll position.
+ mScrollView.scrollTo(0, 0);
+ mHorizontalScrollView.scrollTo(
+ isLayoutRtl() ? mActionButtonsContainer.getWidth() : 0, 0);
+ }
+ refocusPreviousButton();
}
/**
* Hides all menu views, including the menu frame.
*/
void hideAllUserControls() {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: hideAllUserControls()", TAG);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: hideAllUserControls()", TAG);
+ mFocusedButton = null;
+ mButtonMenuIsVisible = false;
+ mMoveMenuIsVisible = false;
showButtonsMenu(false);
hideMovementHints();
setFrameHighlighted(false);
@@ -348,6 +514,13 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
});
}
+ /**
+ * Button order:
+ * - Fullscreen
+ * - Close
+ * - Custom actions (app or media actions)
+ * - System actions
+ */
void setAdditionalActions(List<RemoteAction> actions, RemoteAction closeAction,
Handler mainHandler) {
if (DEBUG) {
@@ -370,13 +543,13 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
final int actionsNumber = actions.size();
int buttonsNumber = mAdditionalButtons.size();
if (actionsNumber > buttonsNumber) {
- // Add buttons until we have enough to display all of the actions.
+ // Add buttons until we have enough to display all the actions.
while (actionsNumber > buttonsNumber) {
TvPipMenuActionButton button = new TvPipMenuActionButton(mContext);
button.setOnClickListener(this);
mActionButtonsContainer.addView(button,
- mActionButtonsContainer.getChildCount() - 1);
+ FIRST_CUSTOM_ACTION_POSITION + buttonsNumber);
mAdditionalButtons.add(button);
buttonsNumber++;
@@ -398,30 +571,27 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
final TvPipMenuActionButton button = mAdditionalButtons.get(index);
// Remove action if it matches the custom close action.
- if (actionsMatch(action, closeAction)) {
+ if (PipUtils.remoteActionsMatch(action, closeAction)) {
button.setVisibility(GONE);
continue;
}
setActionForButton(action, button, mainHandler);
}
- }
- /**
- * Checks whether title, description and intent match.
- * Comparing icons would be good, but using equals causes false negatives
- */
- private boolean actionsMatch(RemoteAction action1, RemoteAction action2) {
- if (action1 == action2) return true;
- if (action1 == null || action2 == null) return false;
- return Objects.equals(action1.getTitle(), action2.getTitle())
- && Objects.equals(action1.getContentDescription(), action2.getContentDescription())
- && Objects.equals(action1.getActionIntent(), action2.getActionIntent());
+ if (mCurrentPipBounds != null) {
+ updateButtonGravity(mCurrentPipBounds);
+ refocusPreviousButton();
+ }
}
private void setActionForButton(RemoteAction action, TvPipMenuActionButton button,
Handler mainHandler) {
button.setVisibility(View.VISIBLE); // Ensure the button is visible.
- button.setTextAndDescription(action.getContentDescription());
+ if (action.getContentDescription().length() > 0) {
+ button.setTextAndDescription(action.getContentDescription());
+ } else {
+ button.setTextAndDescription(action.getTitle());
+ }
button.setEnabled(action.isEnabled());
button.setTag(action);
action.getIcon().loadDrawableAsync(mContext, button::setImageDrawable, mainHandler);
@@ -472,12 +642,11 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: dispatchKeyEvent, action: %d, keycode: %d",
- TAG, event.getAction(), event.getKeyCode());
- }
if (mListener != null && event.getAction() == ACTION_UP) {
+ if (!mMoveMenuIsVisible) {
+ mFocusedButton = mActionButtonsContainer.getFocusedChild();
+ }
+
switch (event.getKeyCode()) {
case KEYCODE_BACK:
mListener.onBackPress();
@@ -539,6 +708,10 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: showUserActions: %b", TAG, show);
}
+ if (show) {
+ mActionButtonsContainer.setVisibility(VISIBLE);
+ refocusPreviousButton();
+ }
animateAlphaTo(show ? 1 : 0, mActionButtonsContainer);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
new file mode 100644
index 000000000000..42fd1aab44f8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.tv;
+
+import android.app.PictureInPictureParams;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
+import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
+import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
+import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * TV specific changes to the PipTaskOrganizer.
+ */
+public class TvPipTaskOrganizer extends PipTaskOrganizer {
+
+ public TvPipTaskOrganizer(Context context,
+ @NonNull SyncTransactionQueue syncTransactionQueue,
+ @NonNull PipTransitionState pipTransitionState,
+ @NonNull PipBoundsState pipBoundsState,
+ @NonNull PipBoundsAlgorithm boundsHandler,
+ @NonNull PipMenuController pipMenuController,
+ @NonNull PipAnimationController pipAnimationController,
+ @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
+ @NonNull PipTransitionController pipTransitionController,
+ @NonNull PipParamsChangedForwarder pipParamsChangedForwarder,
+ Optional<SplitScreenController> splitScreenOptional,
+ @NonNull DisplayController displayController,
+ @NonNull PipUiEventLogger pipUiEventLogger,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ ShellExecutor mainExecutor) {
+ super(context, syncTransactionQueue, pipTransitionState, pipBoundsState, boundsHandler,
+ pipMenuController, pipAnimationController, surfaceTransactionHelper,
+ pipTransitionController, pipParamsChangedForwarder, splitScreenOptional,
+ displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+ }
+
+ @Override
+ protected void applyNewPictureInPictureParams(@NonNull PictureInPictureParams params) {
+ super.applyNewPictureInPictureParams(params);
+ if (PipUtils.aspectRatioChanged(params.getExpandedAspectRatioFloat(),
+ mPictureInPictureParams.getExpandedAspectRatioFloat())) {
+ mPipParamsChangedForwarder.notifyExpandedAspectRatioChanged(
+ params.getExpandedAspectRatioFloat());
+ }
+ if (!Objects.equals(params.getTitle(), mPictureInPictureParams.getTitle())) {
+ mPipParamsChangedForwarder.notifyTitleChanged(params.getTitle());
+ }
+ if (!Objects.equals(params.getSubtitle(), mPictureInPictureParams.getSubtitle())) {
+ mPipParamsChangedForwarder.notifySubtitleChanged(params.getSubtitle());
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index def9ad20a632..4b85496f2a7f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -81,6 +81,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
private PipBoundsState mPipBoundsState;
private PipTransitionState mPipTransitionState;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
+ private PipParamsChangedForwarder mPipParamsChangedForwarder;
private ComponentName mComponent1;
private ComponentName mComponent2;
@@ -97,11 +98,10 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mMainExecutor = new TestShellExecutor();
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext,
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
- mPipBoundsAlgorithm, mMockPhonePipMenuController,
- mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
- mMockPipTransitionController, mMockOptionalSplitScreen,
- mMockDisplayController, mMockPipUiEventLogger,
- mMockShellTaskOrganizer, mMainExecutor));
+ mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController,
+ mMockPipSurfaceTransactionHelper, mMockPipTransitionController,
+ mPipParamsChangedForwarder, mMockOptionalSplitScreen, mMockDisplayController,
+ mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
mMainExecutor.flushAll();
preparePipTaskOrg();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index deb955b30842..5368b7db3dc1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -48,6 +48,7 @@ import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -86,6 +87,7 @@ public class PipControllerTest extends ShellTestCase {
@Mock private TaskStackListenerImpl mMockTaskStackListener;
@Mock private ShellExecutor mMockExecutor;
@Mock private Optional<OneHandedController> mMockOneHandedController;
+ @Mock private PipParamsChangedForwarder mPipParamsChangedForwarder;
@Mock private DisplayLayout mMockDisplayLayout1;
@Mock private DisplayLayout mMockDisplayLayout2;
@@ -102,7 +104,8 @@ public class PipControllerTest extends ShellTestCase {
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
mMockPipTransitionController, mMockWindowManagerShellWrapper,
- mMockTaskStackListener, mMockOneHandedController, mMockExecutor);
+ mMockTaskStackListener, mPipParamsChangedForwarder, mMockOneHandedController,
+ mMockExecutor);
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
}
@@ -134,7 +137,8 @@ public class PipControllerTest extends ShellTestCase {
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
mMockPipTransitionController, mMockWindowManagerShellWrapper,
- mMockTaskStackListener, mMockOneHandedController, mMockExecutor));
+ mMockTaskStackListener, mPipParamsChangedForwarder, mMockOneHandedController,
+ mMockExecutor));
}
@Test
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 0e6249047980..2fe7b1668d08 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -152,8 +152,12 @@ public final class BluetoothMidiDevice {
BluetoothGattCharacteristic characteristic,
byte[] value,
int status) {
- Log.d(TAG, "onCharacteristicRead " + status);
+ Log.d(TAG, "onCharacteristicRead status:" + status);
+ StackTraceElement[] elements = Thread.currentThread().getStackTrace();
+ for (StackTraceElement element : elements) {
+ Log.i(TAG, " " + element);
+ }
// switch to receiving notifications after initial characteristic read
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
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/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index aa0ef91be46b..ee7b7d6b180f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -46,7 +46,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.testutils.shadow.ShadowRouter2Manager;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -259,7 +258,7 @@ public class InfoMediaManagerTest {
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
- when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes);
+ mShadowRouter2Manager.setTransferableRoutes(routes);
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
assertThat(mediaDevice).isNull();
@@ -732,8 +731,14 @@ public class InfoMediaManagerTest {
}
@Test
- @Ignore
- public void shouldDisableMediaOutput_infosSizeEqual1_returnsTrue() {
+ public void shouldDisableMediaOutput_infosIsEmpty_returnsTrue() {
+ mShadowRouter2Manager.setTransferableRoutes(new ArrayList<>());
+
+ assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isTrue();
+ }
+
+ @Test
+ public void shouldDisableMediaOutput_infosSizeEqual1_returnsFalse() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
final List<MediaRoute2Info> infos = new ArrayList<>();
infos.add(info);
@@ -741,7 +746,7 @@ public class InfoMediaManagerTest {
when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
- assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isTrue();
+ assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
}
@Test
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/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ecfe4850cdd1..077337cdc8c3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -818,9 +818,6 @@ public class SettingsProvider extends ContentProvider {
getContext().enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS,
"Access files from the settings of another user");
}
- if (mode.contains("w")) {
- enforceWritePermission(Manifest.permission.WRITE_SETTINGS);
- }
uri = ContentProvider.getUriWithoutUserId(uri);
final String cacheRingtoneSetting;
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/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index ccfd3a3d79f0..b230438f66fd 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -14,140 +14,131 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<FrameLayout
+<com.android.systemui.screenshot.DraggableConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/clipboard_ui"
android:theme="@style/FloatingOverlay"
android:alpha="0"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
- android:id="@+id/background_protection"
- android:layout_height="@dimen/overlay_bg_protection_height"
- android:layout_width="match_parent"
- android:layout_gravity="bottom"
- android:src="@drawable/overlay_actions_background_protection"/>
- <com.android.systemui.screenshot.DraggableConstraintLayout
- android:id="@+id/clipboard_ui"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <ImageView
- android:id="@+id/actions_container_background"
- android:visibility="gone"
- android:layout_height="0dp"
- android:layout_width="0dp"
- android:elevation="1dp"
- android:background="@drawable/action_chip_container_background"
- android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
- app:layout_constraintBottom_toBottomOf="@+id/actions_container"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/actions_container"
- app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
- <HorizontalScrollView
- android:id="@+id/actions_container"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
- android:paddingEnd="@dimen/overlay_action_container_padding_right"
- android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
- android:elevation="1dp"
- android:scrollbars="none"
- app:layout_constraintHorizontal_bias="0"
- app:layout_constraintWidth_percent="1.0"
- app:layout_constraintWidth_max="wrap"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toEndOf="@+id/preview_border"
- app:layout_constraintEnd_toEndOf="parent">
- <LinearLayout
- android:id="@+id/actions"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:animateLayoutChanges="true">
- <include layout="@layout/overlay_action_chip"
- android:id="@+id/remote_copy_chip"/>
- <include layout="@layout/overlay_action_chip"
- android:id="@+id/edit_chip"/>
- </LinearLayout>
- </HorizontalScrollView>
- <View
- android:id="@+id/preview_border"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginStart="@dimen/overlay_offset_x"
- android:layout_marginBottom="@dimen/overlay_offset_y"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toBottomOf="@id/actions_container_background"
- android:elevation="@dimen/overlay_preview_elevation"
- app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end"
- app:layout_constraintTop_toTopOf="@id/clipboard_preview_top"
- android:background="@drawable/overlay_border"/>
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/clipboard_preview_end"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:barrierMargin="@dimen/overlay_border_width"
- app:barrierDirection="end"
- app:constraint_referenced_ids="clipboard_preview"/>
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/clipboard_preview_top"
+ android:id="@+id/actions_container_background"
+ android:visibility="gone"
+ android:layout_height="0dp"
+ android:layout_width="0dp"
+ android:elevation="4dp"
+ android:background="@drawable/action_chip_container_background"
+ android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+ app:layout_constraintBottom_toBottomOf="@+id/actions_container"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/actions_container"
+ app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
+ <HorizontalScrollView
+ android:id="@+id/actions_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
+ android:paddingEnd="@dimen/overlay_action_container_padding_right"
+ android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
+ android:elevation="4dp"
+ android:scrollbars="none"
+ android:layout_marginBottom="4dp"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintWidth_percent="1.0"
+ app:layout_constraintWidth_max="wrap"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/preview_border"
+ app:layout_constraintEnd_toEndOf="parent">
+ <LinearLayout
+ android:id="@+id/actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- app:barrierDirection="top"
- app:barrierMargin="@dimen/overlay_border_width_neg"
- app:constraint_referenced_ids="clipboard_preview"/>
- <FrameLayout
- android:id="@+id/clipboard_preview"
- android:elevation="@dimen/overlay_preview_elevation"
- android:background="@drawable/overlay_preview_background"
- android:clipChildren="true"
- android:clipToOutline="true"
- android:clipToPadding="true"
- android:layout_width="@dimen/clipboard_preview_size"
- android:layout_margin="@dimen/overlay_border_width"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- app:layout_constraintBottom_toBottomOf="@id/preview_border"
- app:layout_constraintStart_toStartOf="@id/preview_border"
- app:layout_constraintEnd_toEndOf="@id/preview_border"
- app:layout_constraintTop_toTopOf="@id/preview_border">
- <TextView android:id="@+id/text_preview"
- android:textFontWeight="500"
- android:padding="8dp"
- android:gravity="center|start"
- android:ellipsize="end"
- android:autoSizeTextType="uniform"
- android:autoSizeMinTextSize="10sp"
- android:autoSizeMaxTextSize="200sp"
- android:textColor="?attr/overlayButtonTextColor"
- android:background="?androidprv:attr/colorAccentSecondary"
- android:layout_width="@dimen/clipboard_preview_size"
- android:layout_height="@dimen/clipboard_preview_size"/>
- <ImageView
- android:id="@+id/image_preview"
- android:scaleType="fitCenter"
- android:adjustViewBounds="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </FrameLayout>
- <FrameLayout
- android:id="@+id/dismiss_button"
- android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
- android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
- android:elevation="@dimen/overlay_dismiss_button_elevation"
- android:visibility="gone"
- android:alpha="0"
- app:layout_constraintStart_toEndOf="@id/clipboard_preview"
- app:layout_constraintEnd_toEndOf="@id/clipboard_preview"
- app:layout_constraintTop_toTopOf="@id/clipboard_preview"
- app:layout_constraintBottom_toTopOf="@id/clipboard_preview"
- android:contentDescription="@string/clipboard_dismiss_description">
- <ImageView
- android:id="@+id/dismiss_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_margin="@dimen/overlay_dismiss_button_margin"
- android:src="@drawable/overlay_cancel"/>
- </FrameLayout>
- </com.android.systemui.screenshot.DraggableConstraintLayout>
-</FrameLayout> \ No newline at end of file
+ android:animateLayoutChanges="true">
+ <include layout="@layout/overlay_action_chip"
+ android:id="@+id/remote_copy_chip"/>
+ <include layout="@layout/overlay_action_chip"
+ android:id="@+id/edit_chip"/>
+ </LinearLayout>
+ </HorizontalScrollView>
+ <View
+ android:id="@+id/preview_border"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="@dimen/overlay_offset_x"
+ android:layout_marginBottom="@dimen/overlay_offset_y"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="@id/actions_container_background"
+ android:elevation="7dp"
+ app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end"
+ app:layout_constraintTop_toTopOf="@id/clipboard_preview_top"
+ android:background="@drawable/overlay_border"/>
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/clipboard_preview_end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierMargin="@dimen/overlay_border_width"
+ app:barrierDirection="end"
+ app:constraint_referenced_ids="clipboard_preview"/>
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/clipboard_preview_top"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierDirection="top"
+ app:barrierMargin="@dimen/overlay_border_width_neg"
+ app:constraint_referenced_ids="clipboard_preview"/>
+ <FrameLayout
+ android:id="@+id/clipboard_preview"
+ android:elevation="7dp"
+ android:background="@drawable/overlay_preview_background"
+ android:clipChildren="true"
+ android:clipToOutline="true"
+ android:clipToPadding="true"
+ android:layout_width="@dimen/clipboard_preview_size"
+ android:layout_margin="@dimen/overlay_border_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ app:layout_constraintBottom_toBottomOf="@id/preview_border"
+ app:layout_constraintStart_toStartOf="@id/preview_border"
+ app:layout_constraintEnd_toEndOf="@id/preview_border"
+ app:layout_constraintTop_toTopOf="@id/preview_border">
+ <TextView android:id="@+id/text_preview"
+ android:textFontWeight="500"
+ android:padding="8dp"
+ android:gravity="center|start"
+ android:ellipsize="end"
+ android:autoSizeTextType="uniform"
+ android:autoSizeMinTextSize="10sp"
+ android:autoSizeMaxTextSize="200sp"
+ android:textColor="?attr/overlayButtonTextColor"
+ android:background="?androidprv:attr/colorAccentSecondary"
+ android:layout_width="@dimen/clipboard_preview_size"
+ android:layout_height="@dimen/clipboard_preview_size"/>
+ <ImageView
+ android:id="@+id/image_preview"
+ android:scaleType="fitCenter"
+ android:adjustViewBounds="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+ <FrameLayout
+ android:id="@+id/dismiss_button"
+ android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
+ android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
+ android:elevation="10dp"
+ android:visibility="gone"
+ android:alpha="0"
+ app:layout_constraintStart_toEndOf="@id/clipboard_preview"
+ app:layout_constraintEnd_toEndOf="@id/clipboard_preview"
+ app:layout_constraintTop_toTopOf="@id/clipboard_preview"
+ app:layout_constraintBottom_toTopOf="@id/clipboard_preview"
+ android:contentDescription="@string/clipboard_dismiss_description">
+ <ImageView
+ android:id="@+id/dismiss_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/overlay_dismiss_button_margin"
+ android:src="@drawable/overlay_cancel"/>
+ </FrameLayout>
+</com.android.systemui.screenshot.DraggableConstraintLayout>
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..dc86afaf6497 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,16 @@
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:focusable="false"
+ android:importantForAccessibility="no"
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/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index ef030baa967b..4d5bf53eb64a 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -62,7 +62,6 @@
android:id="@+id/lock_icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:padding="@dimen/lock_icon_padding"
android:layout_gravity="center"
android:scaleType="centerCrop"/>
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml b/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml
index 6d52a30be7b4..18386637e8f8 100644
--- a/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml
@@ -35,7 +35,6 @@
android:id="@+id/udfps_aod_fp"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:padding="@dimen/lock_icon_padding"
android:layout_gravity="center"
android:scaleType="centerCrop"
app:lottie_autoPlay="false"
@@ -47,7 +46,6 @@
android:id="@+id/udfps_lockscreen_fp"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:padding="@dimen/lock_icon_padding"
android:layout_gravity="center"
android:scaleType="centerCrop"
app:lottie_autoPlay="false"
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/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 2b1c47f60070..630fb360cc14 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -123,6 +123,8 @@ public class QuickStepContract {
public static final int SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED = 1 << 23;
// The current app is in immersive mode
public static final int SYSUI_STATE_IMMERSIVE_MODE = 1 << 24;
+ // The voice interaction session window is showing
+ public static final int SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 25;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -149,7 +151,8 @@ public class QuickStepContract {
SYSUI_STATE_DEVICE_DOZING,
SYSUI_STATE_BACK_DISABLED,
SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED,
- SYSUI_STATE_IMMERSIVE_MODE
+ SYSUI_STATE_IMMERSIVE_MODE,
+ SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING
})
public @interface SystemUiStateFlags {}
@@ -184,6 +187,7 @@ public class QuickStepContract {
str.add((flags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0
? "bubbles_mange_menu_expanded" : "");
str.add((flags & SYSUI_STATE_IMMERSIVE_MODE) != 0 ? "immersive_mode" : "");
+ str.add((flags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0 ? "vis_win_showing" : "");
return str.toString();
}
@@ -254,9 +258,11 @@ public class QuickStepContract {
* disabled.
*/
public static boolean isBackGestureDisabled(int sysuiStateFlags) {
- // Always allow when the bouncer/global actions is showing (even on top of the keyguard)
+ // Always allow when the bouncer/global actions/voice session is showing (even on top of
+ // the keyguard)
if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0
- || (sysuiStateFlags & SYSUI_STATE_DIALOG_SHOWING) != 0) {
+ || (sysuiStateFlags & SYSUI_STATE_DIALOG_SHOWING) != 0
+ || (sysuiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0) {
return false;
}
if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
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/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index e63e675bfd14..e1913657b7cc 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -56,6 +56,7 @@ public class LockIconView extends FrameLayout implements Dumpable {
@NonNull private final RectF mSensorRect;
@NonNull private PointF mLockIconCenter = new PointF(0f, 0f);
private int mRadius;
+ private int mLockIconPadding;
private ImageView mLockIcon;
private ImageView mBgView;
@@ -125,9 +126,13 @@ public class LockIconView extends FrameLayout implements Dumpable {
* Set the location of the lock icon.
*/
@VisibleForTesting
- public void setCenterLocation(@NonNull PointF center, int radius) {
+ public void setCenterLocation(@NonNull PointF center, int radius, int drawablePadding) {
mLockIconCenter = center;
mRadius = radius;
+ mLockIconPadding = drawablePadding;
+
+ mLockIcon.setPadding(mLockIconPadding, mLockIconPadding, mLockIconPadding,
+ mLockIconPadding);
// mSensorProps coordinates assume portrait mode which is OK b/c the keyguard is always in
// portrait.
@@ -221,6 +226,7 @@ public class LockIconView extends FrameLayout implements Dumpable {
pw.println(" Center in px (x, y)= ("
+ mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
pw.println(" Radius in pixels: " + mRadius);
+ pw.println(" Drawable padding: " + mLockIconPadding);
pw.println(" mIconType=" + typeToString(mIconType));
pw.println(" mAod=" + mAod);
pw.println("Lock Icon View actual measurements:");
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 239730d18934..2b217f02e834 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -31,8 +31,6 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.AnimatedStateListDrawable;
import android.hardware.biometrics.BiometricSourceType;
-import android.hardware.biometrics.SensorLocationInternal;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Process;
import android.os.VibrationAttributes;
import android.util.DisplayMetrics;
@@ -125,6 +123,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private float mHeightPixels;
private float mWidthPixels;
private int mBottomPaddingPx;
+ private int mScaledPaddingPx;
private boolean mShowUnlockIcon;
private boolean mShowLockIcon;
@@ -341,6 +340,10 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mHeightPixels = bounds.bottom;
mBottomPaddingPx = getResources().getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
+ final int defaultPaddingPx =
+ getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
+ mScaledPaddingPx = (int) (defaultPaddingPx * mAuthController.getScaleFactor());
+
mUnlockedLabel = mView.getContext().getResources().getString(
R.string.accessibility_unlock_button);
mLockedLabel = mView.getContext()
@@ -351,15 +354,13 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private void updateLockIconLocation() {
if (mUdfpsSupported) {
- FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
- final SensorLocationInternal location = props.getLocation();
- mView.setCenterLocation(new PointF(location.sensorLocationX, location.sensorLocationY),
- location.sensorRadius);
+ mView.setCenterLocation(mAuthController.getUdfpsLocation(),
+ mAuthController.getUdfpsRadius(), mScaledPaddingPx);
} else {
mView.setCenterLocation(
new PointF(mWidthPixels / 2,
mHeightPixels - mBottomPaddingPx - sLockIconRadiusPx),
- sLockIconRadiusPx);
+ sLockIconRadiusPx, mScaledPaddingPx);
}
mView.getHitRect(mSensorTouchLocation);
@@ -668,7 +669,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/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index d4dad73a49f2..b6b9065f335e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -444,6 +444,27 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
/**
+ * @return the radius of UDFPS on the screen in pixels
+ */
+ public int getUdfpsRadius() {
+ if (mUdfpsController == null || mUdfpsBounds == null) {
+ return -1;
+ }
+ return mUdfpsBounds.height() / 2;
+ }
+
+ /**
+ * @return the scale factor representing the user's current resolution / the stable
+ * (default) resolution
+ */
+ public float getScaleFactor() {
+ if (mUdfpsController == null || mUdfpsController.mOverlayParams == null) {
+ return 1f;
+ }
+ return mUdfpsController.mOverlayParams.getScaleFactor();
+ }
+
+ /**
* @return where the fingerprint sensor exists in pixels in portrait mode. devices without an
* overridden value will use the default value even if they don't have a fingerprint sensor
*/
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/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 937b81337cbb..9139699af26a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -61,6 +61,7 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
private AnimatorSet mBackgroundInAnimator = new AnimatorSet();
private int mAlpha; // 0-255
+ private float mScaleFactor = 1;
// AOD anti-burn-in offsets
private final int mMaxBurnInOffsetX;
@@ -172,6 +173,22 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
mLockScreenFp.invalidate(); // updated with a valueCallback
}
+ void setScaleFactor(float scale) {
+ mScaleFactor = scale;
+ }
+
+ void updatePadding() {
+ if (mLockScreenFp == null || mAodFp == null) {
+ return;
+ }
+
+ final int defaultPaddingPx =
+ getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
+ final int padding = (int) (defaultPaddingPx * mScaleFactor);
+ mLockScreenFp.setPadding(padding, padding, padding, padding);
+ mAodFp.setPadding(padding, padding, padding, padding);
+ }
+
/**
* @param alpha between 0 and 255
*/
@@ -257,6 +274,7 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp);
mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg);
+ updatePadding();
updateColor();
updateAlpha();
parent.addView(view);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 416b8ee60fd9..8b0f36fe1245 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -154,6 +154,8 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
updateGenericBouncerVisibility();
mConfigurationController.addCallback(mConfigurationListener);
getPanelExpansionStateManager().addExpansionListener(mPanelExpansionListener);
+ updateScaleFactor();
+ mView.updatePadding();
updateAlpha();
updatePauseAuth();
@@ -367,6 +369,15 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
}
}
+ /**
+ * Update the scale factor based on the device's resolution.
+ */
+ private void updateScaleFactor() {
+ if (mUdfpsController != null && mUdfpsController.mOverlayParams != null) {
+ mView.setScaleFactor(mUdfpsController.mOverlayParams.getScaleFactor());
+ }
+ }
+
private final StatusBarStateController.StateListener mStateListener =
new StatusBarStateController.StateListener() {
@Override
@@ -486,6 +497,8 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@Override
public void onConfigChanged(Configuration newConfig) {
+ updateScaleFactor();
+ mView.updatePadding();
mView.updateColor();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index eef9d5bebd75..bd67a7ff1fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -122,7 +122,6 @@ public class ClipboardOverlayController {
private final AccessibilityManager mAccessibilityManager;
private final TextClassifier mTextClassifier;
- private final FrameLayout mContainer;
private final DraggableConstraintLayout mView;
private final View mClipboardPreview;
private final ImageView mImagePreview;
@@ -177,9 +176,8 @@ public class ClipboardOverlayController {
setWindowFocusable(false);
- mContainer = (FrameLayout)
+ mView = (DraggableConstraintLayout)
LayoutInflater.from(mContext).inflate(R.layout.clipboard_overlay, null);
- mView = requireNonNull(mContainer.findViewById(R.id.clipboard_ui));
mActionContainerBackground =
requireNonNull(mView.findViewById(R.id.actions_container_background));
mActionContainer = requireNonNull(mView.findViewById(R.id.actions));
@@ -201,13 +199,6 @@ public class ClipboardOverlayController {
public void onSwipeDismissInitiated(Animator animator) {
mUiEventLogger.log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
mExitAnimator = animator;
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- mContainer.animate().alpha(0).setDuration(animation.getDuration()).start();
- }
- });
}
@Override
@@ -231,7 +222,7 @@ public class ClipboardOverlayController {
attachWindow();
withWindowAttached(() -> {
- mWindow.setContentView(mContainer);
+ mWindow.setContentView(mView);
updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets());
mView.requestLayout();
});
@@ -308,7 +299,7 @@ public class ClipboardOverlayController {
} else {
mRemoteCopyChip.setVisibility(View.GONE);
}
- withWindowAttached(() -> mContainer.post(this::animateIn));
+ withWindowAttached(() -> mView.post(this::animateIn));
mTimeoutHandler.resetTimeout();
}
@@ -508,7 +499,7 @@ public class ClipboardOverlayController {
rootAnim.setInterpolator(linearInterpolator);
rootAnim.setDuration(66);
rootAnim.addUpdateListener(animation -> {
- mContainer.setAlpha(animation.getAnimatedFraction());
+ mView.setAlpha(animation.getAnimatedFraction());
});
ValueAnimator scaleAnim = ValueAnimator.ofFloat(0, 1);
@@ -553,7 +544,7 @@ public class ClipboardOverlayController {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mContainer.setAlpha(1);
+ mView.setAlpha(1);
mTimeoutHandler.resetTimeout();
}
});
@@ -568,9 +559,7 @@ public class ClipboardOverlayController {
ValueAnimator rootAnim = ValueAnimator.ofFloat(0, 1);
rootAnim.setInterpolator(linearInterpolator);
rootAnim.setDuration(100);
- rootAnim.addUpdateListener(animation -> {
- mContainer.setAlpha(1 - animation.getAnimatedFraction());
- });
+ rootAnim.addUpdateListener(anim -> mView.setAlpha(1 - anim.getAnimatedFraction()));
ValueAnimator scaleAnim = ValueAnimator.ofFloat(0, 1);
scaleAnim.setInterpolator(scaleInterpolator);
@@ -647,7 +636,7 @@ public class ClipboardOverlayController {
private void reset() {
mView.setTranslationX(0);
- mContainer.setAlpha(0);
+ mView.setAlpha(0);
mActionContainerBackground.setVisibility(View.GONE);
resetActionChips();
mTimeoutHandler.cancelTimeout();
@@ -706,8 +695,9 @@ public class ClipboardOverlayController {
}
DisplayCutout cutout = insets.getDisplayCutout();
Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars());
+ Insets imeInsets = insets.getInsets(WindowInsets.Type.ime());
if (cutout == null) {
- p.setMargins(0, 0, 0, navBarInsets.bottom);
+ p.setMargins(0, 0, 0, Math.max(imeInsets.bottom, navBarInsets.bottom));
} else {
Insets waterfall = cutout.getWaterfallInsets();
if (orientation == ORIENTATION_PORTRAIT) {
@@ -715,14 +705,16 @@ public class ClipboardOverlayController {
waterfall.left,
Math.max(cutout.getSafeInsetTop(), waterfall.top),
waterfall.right,
- Math.max(cutout.getSafeInsetBottom(),
- Math.max(navBarInsets.bottom, waterfall.bottom)));
+ Math.max(imeInsets.bottom,
+ Math.max(cutout.getSafeInsetBottom(),
+ Math.max(navBarInsets.bottom, waterfall.bottom))));
} else {
p.setMargins(
- Math.max(cutout.getSafeInsetLeft(), waterfall.left),
+ waterfall.left,
waterfall.top,
- Math.max(cutout.getSafeInsetRight(), waterfall.right),
- Math.max(navBarInsets.bottom, waterfall.bottom));
+ waterfall.right,
+ Math.max(imeInsets.bottom,
+ Math.max(navBarInsets.bottom, waterfall.bottom)));
}
}
mView.setLayoutParams(p);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index b21a886b037d..fb09132684eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -38,6 +38,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.shared.recents.utilities.Utilities
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
@@ -819,6 +820,12 @@ class KeyguardUnlockAnimationController @Inject constructor(
return false
}
+ // We don't do the shared element on tablets because they're large and the smartspace has to
+ // fly across large distances, which is distracting.
+ if (Utilities.isTablet(context)) {
+ return false
+ }
+
return true
}
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/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
new file mode 100644
index 000000000000..1d7ba94af4ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+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..20417aff024c 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;
@@ -48,7 +50,6 @@ import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
import android.widget.ImageButton;
import android.widget.ImageView;
@@ -102,7 +103,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";
@@ -552,30 +552,6 @@ public class MediaControlPanel {
// refreshState is required here to resize the text views (and prevent ellipsis)
mMediaViewController.refreshState();
-
- // Use OnPreDrawListeners to enforce zero alpha on these views for a frame.
- // TransitionLayout insists on resetting the alpha of these views to 1 when onLayout
- // is called which causes the animation to look bad. These suppress that behavior.
- titleText.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- titleText.setAlpha(0);
- titleText.getViewTreeObserver().removeOnPreDrawListener(this);
- return true;
- }
- });
-
- artistText.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- artistText.setAlpha(0);
- artistText.getViewTreeObserver().removeOnPreDrawListener(this);
- return true;
- }
- });
-
return Unit.INSTANCE;
},
() -> {
@@ -949,16 +925,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 +968,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 +977,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 +1019,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 +1027,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 +1263,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 +1348,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/MetadataAnimationHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MetadataAnimationHandler.kt
index 9a1a6d35e3e3..48f4a16cc538 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MetadataAnimationHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MetadataAnimationHandler.kt
@@ -18,8 +18,6 @@ package com.android.systemui.media
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
-import android.animation.AnimatorSet
-import com.android.internal.annotations.VisibleForTesting
/**
* MetadataAnimationHandler controls the current state of the MediaControlPanel's transition motion.
@@ -33,37 +31,37 @@ internal open class MetadataAnimationHandler(
private val enterAnimator: Animator
) : AnimatorListenerAdapter() {
- private val animator: AnimatorSet
private var postExitUpdate: (() -> Unit)? = null
private var postEnterUpdate: (() -> Unit)? = null
private var targetData: Any? = null
val isRunning: Boolean
- get() = animator.isRunning
+ get() = enterAnimator.isRunning || exitAnimator.isRunning
fun setNext(targetData: Any, postExit: () -> Unit, postEnter: () -> Unit): Boolean {
if (targetData != this.targetData) {
this.targetData = targetData
postExitUpdate = postExit
postEnterUpdate = postEnter
- if (!animator.isRunning) {
- animator.start()
+ if (!isRunning) {
+ exitAnimator.start()
}
return true
}
return false
}
- override fun onAnimationEnd(animator: Animator) {
- if (animator === exitAnimator) {
+ override fun onAnimationEnd(anim: Animator) {
+ if (anim === exitAnimator) {
postExitUpdate?.let { it() }
postExitUpdate = null
+ enterAnimator.start()
}
- if (animator === enterAnimator) {
+ if (anim === enterAnimator) {
// Another new update appeared while entering
if (postExitUpdate != null) {
- this.animator.start()
+ exitAnimator.start()
} else {
postEnterUpdate?.let { it() }
postEnterUpdate = null
@@ -74,13 +72,5 @@ internal open class MetadataAnimationHandler(
init {
exitAnimator.addListener(this)
enterAnimator.addListener(this)
- animator = buildAnimatorSet(exitAnimator, enterAnimator)
- }
-
- @VisibleForTesting
- protected open fun buildAnimatorSet(exit: Animator, enter: Animator): AnimatorSet {
- val result = AnimatorSet()
- result.playSequentially(exitAnimator, enterAnimator)
- return result
}
} \ No newline at end of file
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/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index ddcba3ae65ae..07001ee138b8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -118,6 +118,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mCheckBox.setVisibility(View.GONE);
mStatusIcon.setVisibility(View.GONE);
mEndTouchArea.setVisibility(View.GONE);
+ mEndTouchArea.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
mContainerLayout.setOnClickListener(null);
mContainerLayout.setContentDescription(null);
mTitleText.setTextColor(mController.getColorItemContent());
@@ -170,7 +171,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
setSingleLineLayout(getItemTitle(device), true /* bFocused */,
true /* showSeekBar */,
false /* showProgressBar */, false /* showStatus */);
- setUpContentDescriptionForActiveDevice(device);
+ setUpContentDescriptionForView(mContainerLayout, false, device);
mCheckBox.setOnCheckedChangeListener(null);
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(true);
@@ -181,6 +182,9 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
mEndTouchArea.setVisibility(View.VISIBLE);
mEndTouchArea.setOnClickListener(null);
mEndTouchArea.setOnClickListener((v) -> mCheckBox.performClick());
+ mEndTouchArea.setImportantForAccessibility(
+ View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ setUpContentDescriptionForView(mEndTouchArea, true, device);
} else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
mStatusIcon.setImageDrawable(
mContext.getDrawable(R.drawable.media_output_status_check));
@@ -190,7 +194,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
true /* showSeekBar */,
false /* showProgressBar */, true /* showStatus */);
initSeekbar(device);
- setUpContentDescriptionForActiveDevice(device);
+ setUpContentDescriptionForView(mContainerLayout, false, device);
mCurrentActivePosition = position;
} else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
mCheckBox.setOnCheckedChangeListener(null);
@@ -258,9 +262,10 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
}
}
- private void setUpContentDescriptionForActiveDevice(MediaDevice device) {
- mContainerLayout.setClickable(false);
- mContainerLayout.setContentDescription(
+ private void setUpContentDescriptionForView(View view, boolean clickable,
+ MediaDevice device) {
+ view.setClickable(clickable);
+ view.setContentDescription(
mContext.getString(device.getDeviceType()
== MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
? R.string.accessibility_bluetooth_name
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/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/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/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index c723fbb9a6e1..9768e706764f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -41,6 +41,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_D
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import android.annotation.FloatRange;
import android.app.ActivityTaskManager;
@@ -77,6 +78,8 @@ import androidx.annotation.NonNull;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenshotHelper;
@@ -551,6 +554,30 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
= this::cleanupAfterDeath;
+ private final IVoiceInteractionSessionListener mVoiceInteractionSessionListener =
+ new IVoiceInteractionSessionListener.Stub() {
+ @Override
+ public void onVoiceSessionShown() {
+ // Do nothing
+ }
+
+ @Override
+ public void onVoiceSessionHidden() {
+ // Do nothing
+ }
+
+ @Override
+ public void onVoiceSessionWindowVisibilityChanged(boolean visible) {
+ mContext.getMainExecutor().execute(() ->
+ OverviewProxyService.this.onVoiceSessionWindowVisibilityChanged(visible));
+ }
+
+ @Override
+ public void onSetUiHints(Bundle hints) {
+ // Do nothing
+ }
+ };
+
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
public OverviewProxyService(Context context, CommandQueue commandQueue,
@@ -569,6 +596,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
ScreenLifecycle screenLifecycle,
UiEventLogger uiEventLogger,
KeyguardUnlockAnimationController sysuiUnlockAnimationController,
+ AssistUtils assistUtils,
DumpManager dumpManager) {
super(broadcastDispatcher);
mContext = context;
@@ -640,6 +668,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
startConnectionToCurrentUser();
mStartingSurface = startingSurface;
mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
+
+ // Listen for assistant changes
+ assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener);
}
@Override
@@ -648,6 +679,11 @@ public class OverviewProxyService extends CurrentUserTracker implements
internalConnectToCurrentUser();
}
+ public void onVoiceSessionWindowVisibilityChanged(boolean visible) {
+ mSysUiState.setFlag(SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, visible)
+ .commitUpdate(mContext.getDisplayId());
+ }
+
public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton,
boolean gestureSwipeLeft) {
try {
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/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 6f65131ba452..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
@@ -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.
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/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/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 62b11c59923f..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,4526 +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");
- 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..ef24d7793562
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -0,0 +1,4558 @@
+/*
+ * 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();
+
+ if (mBiometricUnlockController.isWakeAndUnlock()) {
+ // Usually doze changes are to/from lockscreen/AOD, but if we're wake and
+ // unlocking we should hide the keyguard ASAP if necessary.
+ updateIsKeyguard();
+ }
+
+ 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/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 807122b62e9f..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();
@@ -1099,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 =
@@ -1484,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,
@@ -1515,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);
}
@@ -3625,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/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/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/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 4d0feffa1d24..be923a68391c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -20,8 +20,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static com.android.keyguard.LockIconView.ICON_LOCK;
import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-import static junit.framework.Assert.assertEquals;
-
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
@@ -87,6 +85,7 @@ import java.util.List;
@TestableLooper.RunWithLooper
public class LockIconViewControllerTest extends SysuiTestCase {
private static final String UNLOCKED_LABEL = "unlocked";
+ private static final int PADDING = 10;
private MockitoSession mStaticMockSession;
@@ -149,6 +148,8 @@ public class LockIconViewControllerTest extends SysuiTestCase {
when(mWindowManager.getCurrentWindowMetrics().getBounds()).thenReturn(windowBounds);
when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL);
when(mResources.getDrawable(anyInt(), any())).thenReturn(mIconDrawable);
+ when(mResources.getDimensionPixelSize(R.dimen.lock_icon_padding)).thenReturn(PADDING);
+ when(mAuthController.getScaleFactor()).thenReturn(1f);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
@@ -181,16 +182,32 @@ public class LockIconViewControllerTest extends SysuiTestCase {
@Test
public void testUpdateFingerprintLocationOnInit() {
// GIVEN fp sensor location is available pre-attached
- Pair<Integer, PointF> udfps = setupUdfps();
+ Pair<Integer, PointF> udfps = setupUdfps(); // first = radius, second = udfps location
// WHEN lock icon view controller is initialized and attached
mLockIconViewController.init();
captureAttachListener();
mAttachListener.onViewAttachedToWindow(mLockIconView);
- // THEN lock icon view location is updated with the same coordinates as fpProps
- verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first));
- assertEquals(udfps.second, mPointCaptor.getValue());
+ // THEN lock icon view location is updated to the udfps location with UDFPS radius
+ verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
+ eq(PADDING));
+ }
+
+ @Test
+ public void testUpdatePaddingBasedOnResolutionScale() {
+ // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5
+ Pair<Integer, PointF> udfps = setupUdfps(); // first = radius, second = udfps location
+ when(mAuthController.getScaleFactor()).thenReturn(5f);
+
+ // WHEN lock icon view controller is initialized and attached
+ mLockIconViewController.init();
+ captureAttachListener();
+ mAttachListener.onViewAttachedToWindow(mLockIconView);
+
+ // THEN lock icon view location is updated with the scaled radius
+ verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
+ eq(PADDING * 5));
}
@Test
@@ -212,8 +229,8 @@ public class LockIconViewControllerTest extends SysuiTestCase {
mDelayableExecutor.runAllReady();
// THEN lock icon view location is updated with the same coordinates as fpProps
- verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first));
- assertEquals(udfps.second, mPointCaptor.getValue());
+ verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
+ eq(PADDING));
}
@Test
@@ -400,7 +417,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
List.of(new SensorLocationInternal("" /* displayId */,
(int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation);
- when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
+ when(mAuthController.getUdfpsRadius()).thenReturn(radius);
return new Pair(radius, udfpsLocation);
}
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..94254da0d7de 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)
@@ -951,7 +947,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
// Rebinding should not trigger animation
player.bindPlayer(mediaData, PACKAGE)
- verify(mockAnimator, times(1)).start()
+ verify(mockAnimator, times(2)).start()
}
@Test
@@ -973,7 +969,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
// Bind trigges new animation
player.bindPlayer(data1, PACKAGE)
- verify(mockAnimator, times(2)).start()
+ verify(mockAnimator, times(3)).start()
whenever(mockAnimator.isRunning()).thenReturn(true)
// Rebind before animation end binds corrct data
@@ -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/MetadataAnimationHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MetadataAnimationHandlerTest.kt
index 52cb902a4f38..311aa9649911 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MetadataAnimationHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MetadataAnimationHandlerTest.kt
@@ -18,7 +18,6 @@ package com.android.systemui.media
import org.mockito.Mockito.`when` as whenever
import android.animation.Animator
-import android.animation.AnimatorSet
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -44,7 +43,6 @@ class MetadataAnimationHandlerTest : SysuiTestCase() {
private interface Callback : () -> Unit
private lateinit var handler: MetadataAnimationHandler
- @Mock private lateinit var animatorSet: AnimatorSet
@Mock private lateinit var enterAnimator: Animator
@Mock private lateinit var exitAnimator: Animator
@Mock private lateinit var postExitCB: Callback
@@ -54,11 +52,7 @@ class MetadataAnimationHandlerTest : SysuiTestCase() {
@Before
fun setUp() {
- handler = object : MetadataAnimationHandler(exitAnimator, enterAnimator) {
- override fun buildAnimatorSet(exit: Animator, enter: Animator): AnimatorSet {
- return animatorSet
- }
- }
+ handler = MetadataAnimationHandler(exitAnimator, enterAnimator)
}
@After
@@ -69,22 +63,31 @@ class MetadataAnimationHandlerTest : SysuiTestCase() {
val cb = { fail("Unexpected callback") }
handler.setNext("data-1", cb, cb)
- verify(animatorSet).start()
+ verify(exitAnimator).start()
}
@Test
fun executeAnimationEnd_runsCallacks() {
+ // We expect this first call to only start the exit animator
handler.setNext("data-1", postExitCB, postEnterCB)
- verify(animatorSet, times(1)).start()
+ verify(exitAnimator, times(1)).start()
+ verify(enterAnimator, never()).start()
verify(postExitCB, never()).invoke()
+ verify(postEnterCB, never()).invoke()
+ // After the exit animator completes,
+ // the exit cb should run, and enter animation should start
handler.onAnimationEnd(exitAnimator)
- verify(animatorSet, times(1)).start()
+ verify(exitAnimator, times(1)).start()
+ verify(enterAnimator, times(1)).start()
verify(postExitCB, times(1)).invoke()
verify(postEnterCB, never()).invoke()
+ // After the exit animator completes,
+ // the enter cb should run without other state changes
handler.onAnimationEnd(enterAnimator)
- verify(animatorSet, times(1)).start()
+ verify(exitAnimator, times(1)).start()
+ verify(enterAnimator, times(1)).start()
verify(postExitCB, times(1)).invoke()
verify(postEnterCB, times(1)).invoke()
}
@@ -120,38 +123,58 @@ class MetadataAnimationHandlerTest : SysuiTestCase() {
val postExitCB2 = mock(Callback::class.java)
val postEnterCB2 = mock(Callback::class.java)
+ // We expect this first call to only start the exit animator
handler.setNext("data-1", postExitCB, postEnterCB)
- verify(animatorSet, times(1)).start()
+ verify(exitAnimator, times(1)).start()
+ verify(enterAnimator, never()).start()
verify(postExitCB, never()).invoke()
verify(postExitCB2, never()).invoke()
verify(postEnterCB, never()).invoke()
verify(postEnterCB2, never()).invoke()
- whenever(animatorSet.isRunning()).thenReturn(true)
+ // After the exit animator completes,
+ // the exit cb should run, and enter animation should start
+ whenever(exitAnimator.isRunning()).thenReturn(true)
+ whenever(enterAnimator.isRunning()).thenReturn(false)
handler.onAnimationEnd(exitAnimator)
- verify(animatorSet, times(1)).start()
+ verify(exitAnimator, times(1)).start()
+ verify(enterAnimator, times(1)).start()
verify(postExitCB, times(1)).invoke()
verify(postExitCB2, never()).invoke()
verify(postEnterCB, never()).invoke()
verify(postEnterCB2, never()).invoke()
+ // Setting new data before the enter animator completes should not trigger
+ // the exit animator an additional time (since it's already running)
+ whenever(exitAnimator.isRunning()).thenReturn(false)
+ whenever(enterAnimator.isRunning()).thenReturn(true)
handler.setNext("data-2", postExitCB2, postEnterCB2)
+ verify(exitAnimator, times(1)).start()
+
+ // Finishing the enterAnimator should cause the exitAnimator to fire again
+ // since the data change and additional time. No enterCB should be executed.
handler.onAnimationEnd(enterAnimator)
- verify(animatorSet, times(2)).start()
+ verify(exitAnimator, times(2)).start()
+ verify(enterAnimator, times(1)).start()
verify(postExitCB, times(1)).invoke()
verify(postExitCB2, never()).invoke()
verify(postEnterCB, never()).invoke()
verify(postEnterCB2, never()).invoke()
+ // Continuing the sequence, this triggers the enter animator an additional time
handler.onAnimationEnd(exitAnimator)
- verify(animatorSet, times(2)).start()
+ verify(exitAnimator, times(2)).start()
+ verify(enterAnimator, times(2)).start()
verify(postExitCB, times(1)).invoke()
verify(postExitCB2, times(1)).invoke()
verify(postEnterCB, never()).invoke()
verify(postEnterCB2, never()).invoke()
+ // And finally the enter animator completes,
+ // triggering the correct postEnterCallback to fire
handler.onAnimationEnd(enterAnimator)
- verify(animatorSet, times(2)).start()
+ verify(exitAnimator, times(2)).start()
+ verify(enterAnimator, times(2)).start()
verify(postExitCB, times(1)).invoke()
verify(postExitCB2, times(1)).invoke()
verify(postEnterCB, never()).invoke()
@@ -172,6 +195,7 @@ class MetadataAnimationHandlerTest : SysuiTestCase() {
fun enterAnimatorEndsWithoutCallback_noAnimatiorStart() {
handler.onAnimationEnd(enterAnimator)
- verify(animatorSet, never()).start()
+ verify(exitAnimator, never()).start()
+ verify(enterAnimator, never()).start()
}
}
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/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 9c12c5c7e2d6..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
);
}
@@ -2250,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;
@@ -2262,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;
@@ -2292,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/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/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/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 877ee8218ea6..fa52ac905a43 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -500,6 +500,7 @@ public class BinaryTransparencyService extends SystemService {
// ones appearing out of the blue. Thus, we're going to only go through our cache to check
// for changes, rather than freshly invoking `getInstalledPackages()` and
// `getInstalledModules()`
+ byte[] largeFileBuffer = PackageUtils.createLargeFileBuffer();
for (Map.Entry<String, Long> entry : mBinaryLastUpdateTimes.entrySet()) {
String packageName = entry.getKey();
try {
@@ -513,7 +514,7 @@ public class BinaryTransparencyService extends SystemService {
// compute the digest for the updated package
String sha256digest = PackageUtils.computeSha256DigestForLargeFile(
- packageInfo.applicationInfo.sourceDir);
+ packageInfo.applicationInfo.sourceDir, largeFileBuffer);
if (sha256digest == null) {
Slog.e(TAG, "Failed to compute SHA256sum for file at "
+ packageInfo.applicationInfo.sourceDir);
@@ -545,11 +546,13 @@ public class BinaryTransparencyService extends SystemService {
// In general, we care about all APEXs, *and* all Modules, which may include some APKs.
// First, we deal with all installed APEXs.
+ byte[] largeFileBuffer = PackageUtils.createLargeFileBuffer();
for (PackageInfo packageInfo : getInstalledApexs()) {
ApplicationInfo appInfo = packageInfo.applicationInfo;
// compute SHA256 for these APEXs
- String sha256digest = PackageUtils.computeSha256DigestForLargeFile(appInfo.sourceDir);
+ String sha256digest = PackageUtils.computeSha256DigestForLargeFile(appInfo.sourceDir,
+ largeFileBuffer);
if (sha256digest == null) {
Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
packageInfo.packageName));
@@ -585,7 +588,7 @@ public class BinaryTransparencyService extends SystemService {
// compute SHA256 digest for these modules
String sha256digest = PackageUtils.computeSha256DigestForLargeFile(
- appInfo.sourceDir);
+ appInfo.sourceDir, largeFileBuffer);
if (sha256digest == null) {
Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
packageName));
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 91f6eeb875f6..7c6ccc95664e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7166,6 +7166,11 @@ public class ActivityManagerService extends IActivityManager.Stub
enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
"getUidProcessState");
}
+ // In case the caller is requesting processState of an app in a different user,
+ // then verify the caller has INTERACT_ACROSS_USERS_FULL permission
+ mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ UserHandle.getUserId(uid), false /* allowAll */, ALLOW_FULL_ONLY,
+ "getUidProcessState", callingPackage); // Ignore return value
synchronized (mProcLock) {
if (mPendingStartActivityUids.isPendingTopUid(uid)) {
@@ -7181,6 +7186,11 @@ public class ActivityManagerService extends IActivityManager.Stub
enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
"getUidProcessState");
}
+ // In case the caller is requesting processCapabilities of an app in a different user,
+ // then verify the caller has INTERACT_ACROSS_USERS_FULL permission
+ mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ UserHandle.getUserId(uid), false /* allowAll */, ALLOW_FULL_ONLY,
+ "getUidProcessCapabilities", callingPackage); // Ignore return value
synchronized (mProcLock) {
return mProcessList.getUidProcessCapabilityLOSP(uid);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 3e5786eaa333..402491d8fe80 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -219,6 +219,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runStopService(pw);
case "broadcast":
return runSendBroadcast(pw);
+ case "compact":
+ return runCompact(pw);
case "instrument":
getOutPrintWriter().println("Error: must be invoked through 'am instrument'.");
return -1;
@@ -966,6 +968,36 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
+ @NeverCompile
+ int runCompact(PrintWriter pw) {
+ String processName = getNextArgRequired();
+ String uid = getNextArgRequired();
+ String op = getNextArgRequired();
+ ProcessRecord app;
+ synchronized (mInternal.mProcLock) {
+ app = mInternal.getProcessRecordLocked(processName, Integer.parseInt(uid));
+ }
+ pw.println("Process record found pid: " + app.mPid);
+ if (op.equals("full")) {
+ pw.println("Executing full compaction for " + app.mPid);
+ synchronized (mInternal.mProcLock) {
+ mInternal.mOomAdjuster.mCachedAppOptimizer.compactAppFull(app, true);
+ }
+ pw.println("Finished full compaction for " + app.mPid);
+ } else if (op.equals("some")) {
+ pw.println("Executing some compaction for " + app.mPid);
+ synchronized (mInternal.mProcLock) {
+ mInternal.mOomAdjuster.mCachedAppOptimizer.compactAppSome(app, true);
+ }
+ pw.println("Finished some compaction for " + app.mPid);
+ } else {
+ getErrPrintWriter().println("Error: unknown compact command '" + op + "'");
+ return -1;
+ }
+
+ return 0;
+ }
+
int runDumpHeap(PrintWriter pw) throws RemoteException {
final PrintWriter err = getErrPrintWriter();
boolean managed = true;
@@ -3446,6 +3478,10 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" --allow-background-activity-starts: The receiver may start activities");
pw.println(" even if in the background.");
pw.println(" --async: Send without waiting for the completion of the receiver.");
+ pw.println(" compact <process_name> <Package UID> [some|full]");
+ pw.println(" Force process compaction.");
+ pw.println(" some: execute file compaction.");
+ pw.println(" full: execute anon + file compaction.");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
pw.println(" [--user <USER_ID> | current]");
pw.println(" [--no-hidden-api-checks [--no-test-api-access]]");
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index ff569a681a4e..a172018ab291 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -88,6 +88,12 @@ public final class CachedAppOptimizer {
@VisibleForTesting static final String KEY_FREEZER_DEBOUNCE_TIMEOUT =
"freeze_debounce_timeout";
+ // RSS Indices
+ private static final int RSS_TOTAL_INDEX = 0;
+ private static final int RSS_FILE_INDEX = 1;
+ private static final int RSS_ANON_INDEX = 2;
+ private static final int RSS_SWAP_INDEX = 3;
+
// Phenotype sends int configurations and we map them to the strings we'll use on device,
// preventing a weird string value entering the kernel.
private static final int COMPACT_ACTION_NONE = 0;
@@ -101,11 +107,13 @@ public final class CachedAppOptimizer {
private static final int COMPACT_ACTION_FILE_FLAG = 1;
private static final int COMPACT_ACTION_ANON_FLAG = 2;
+ private static final String ATRACE_COMPACTION_TRACK = "Compaction";
+
// Defaults for phenotype flags.
@VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
@VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
- @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
@VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL;
+ @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
@@ -152,6 +160,11 @@ public final class CachedAppOptimizer {
static final int SET_FROZEN_PROCESS_MSG = 3;
static final int REPORT_UNFREEZE_MSG = 4;
+ // When free swap falls below this percentage threshold any full (file + anon)
+ // compactions will be downgraded to file only compactions to reduce pressure
+ // on swap resources as file.
+ static final double COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD = 0.2;
+
static final int DO_FREEZE = 1;
static final int REPORT_UNFREEZE = 2;
@@ -440,6 +453,16 @@ public final class CachedAppOptimizer {
pw.println(" " + app.mOptRecord.getFreezeUnfreezeTime()
+ ": " + app.getPid() + " " + app.processName);
}
+
+ if (!mPendingCompactionProcesses.isEmpty()) {
+ pw.println(" Pending compactions:");
+ size = mPendingCompactionProcesses.size();
+ for (int i = 0; i < size; i++) {
+ ProcessRecord app = mPendingCompactionProcesses.get(i);
+ pw.println(" pid: " + app.getPid() + ". name: " + app.processName
+ + ". hasPendingCompact: " + app.mOptRecord.hasPendingCompact());
+ }
+ }
}
if (DEBUG_COMPACTION) {
for (Map.Entry<Integer, LastCompactionStats> entry
@@ -454,10 +477,16 @@ public final class CachedAppOptimizer {
}
@GuardedBy("mProcLock")
- void compactAppSome(ProcessRecord app) {
+ void compactAppSome(ProcessRecord app, boolean force) {
app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_SOME);
- if (!app.mOptRecord.hasPendingCompact()) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM, " compactAppSome requested for " + app.processName + " force: " + force);
+ }
+ if (force || !app.mOptRecord.hasPendingCompact()) {
+ Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_COMPACTION_TRACK,
+ "compactAppSome " + app.processName != null ? app.processName : "");
app.mOptRecord.setHasPendingCompact(true);
+ app.mOptRecord.setForceCompact(force);
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
mCompactionHandler.obtainMessage(
@@ -466,19 +495,31 @@ public final class CachedAppOptimizer {
}
@GuardedBy("mProcLock")
- void compactAppFull(ProcessRecord app) {
- // Apply OOM adj score throttle for Full App Compaction.
- if ((app.mState.getSetAdj() < mCompactThrottleMinOomAdj
- || app.mState.getSetAdj() > mCompactThrottleMaxOomAdj)
+ void compactAppFull(ProcessRecord app, boolean force) {
+ boolean oomAdjEnteredCached = (app.mState.getSetAdj() < mCompactThrottleMinOomAdj
+ || app.mState.getSetAdj() > mCompactThrottleMaxOomAdj)
&& app.mState.getCurAdj() >= mCompactThrottleMinOomAdj
- && app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj) {
+ && app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj;
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ " compactAppFull requested for " + app.processName + " force: " + force
+ + " oomAdjEnteredCached: " + oomAdjEnteredCached);
+ }
+ // Apply OOM adj score throttle for Full App Compaction.
+ if (force || oomAdjEnteredCached) {
app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
if (!app.mOptRecord.hasPendingCompact()) {
+ Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_COMPACTION_TRACK,
+ "compactAppFull " + app.processName != null ? app.processName : "");
app.mOptRecord.setHasPendingCompact(true);
+ app.mOptRecord.setForceCompact(force);
mPendingCompactionProcesses.add(app);
- mCompactionHandler.sendMessage(
- mCompactionHandler.obtainMessage(
+ mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(
COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
+ } else if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ " compactAppFull Skipped for " + app.processName
+ + " since it has a pending compact");
}
} else {
if (DEBUG_COMPACTION) {
@@ -493,6 +534,8 @@ public final class CachedAppOptimizer {
void compactAppPersistent(ProcessRecord app) {
app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_PERSISTENT);
if (!app.mOptRecord.hasPendingCompact()) {
+ Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_COMPACTION_TRACK,
+ "compactAppPersistent " + app.processName != null ? app.processName : "");
app.mOptRecord.setHasPendingCompact(true);
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
@@ -511,6 +554,8 @@ public final class CachedAppOptimizer {
void compactAppBfgs(ProcessRecord app) {
app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_BFGS);
if (!app.mOptRecord.hasPendingCompact()) {
+ Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_COMPACTION_TRACK,
+ "compactAppBfgs " + app.processName != null ? app.processName : "");
app.mOptRecord.setHasPendingCompact(true);
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
@@ -527,6 +572,8 @@ public final class CachedAppOptimizer {
void compactAllSystem() {
if (useCompaction()) {
+ Trace.instantForTrack(
+ Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_COMPACTION_TRACK, "compactAllSystem");
mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(
COMPACT_SYSTEM_MSG));
}
@@ -545,6 +592,11 @@ public final class CachedAppOptimizer {
static private native void cancelCompaction();
/**
+ * Retrieves the free swap percentage.
+ */
+ static private native double getFreeSwapPercent();
+
+ /**
* Reads the flag value from DeviceConfig to determine whether app compaction
* should be enabled, and starts the freeze/compaction thread if needed.
*/
@@ -1094,11 +1146,24 @@ public final class CachedAppOptimizer {
if(wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
// Remove any pending compaction we may have scheduled to happen while screen was off
Slog.e(TAG_AM, "Cancel pending or running compactions as system is awake");
- synchronized(mProcLock) {
- mPendingCompactionProcesses.clear();
+ cancelAllCompactions();
+ }
+ }
+
+ void cancelAllCompactions() {
+ synchronized (mProcLock) {
+ int size = mPendingCompactionProcesses.size();
+ ProcessRecord record;
+ for (int i=0; i < size; ++i) {
+ record = mPendingCompactionProcesses.get(i);
+ // The process record is kept alive after compactions are cleared,
+ // so make sure to reset the compaction state to avoid skipping any future
+ // compactions due to a stale value here.
+ record.mOptRecord.setHasPendingCompact(false);
}
- cancelCompaction();
+ mPendingCompactionProcesses.clear();
}
+ cancelCompaction();
}
@GuardedBy({"mService", "mProcLock"})
@@ -1114,13 +1179,48 @@ public final class CachedAppOptimizer {
// Perform a major compaction when any app enters cached
if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
&& (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) {
- compactAppSome(app);
+ compactAppSome(app, false);
} else if (newAdj >= ProcessList.CACHED_APP_MIN_ADJ
&& newAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
- compactAppFull(app);
+ compactAppFull(app, false);
}
}
+ /**
+ * This method resolves which compaction method we should use for the proposed compaction.
+ */
+ int resolveCompactionAction(int pendingAction) {
+ int resolvedAction;
+
+ switch (pendingAction) {
+ case COMPACT_PROCESS_SOME:
+ resolvedAction = COMPACT_ACTION_FILE;
+ break;
+ // For the time being, treat these as equivalent.
+ case COMPACT_PROCESS_FULL:
+ case COMPACT_PROCESS_PERSISTENT:
+ case COMPACT_PROCESS_BFGS:
+ resolvedAction = COMPACT_ACTION_FULL;
+ break;
+ default:
+ resolvedAction = COMPACT_ACTION_NONE;
+ break;
+ }
+
+ // Downgrade compaction if facing swap memory pressure
+ if (resolvedAction == COMPACT_ACTION_FULL) {
+ double swapUsagePercent = getFreeSwapPercent();
+ if (swapUsagePercent < COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD) {
+ Slog.d(TAG_AM,
+ "Downgraded compaction to file only due to low swap."
+ + " Swap Free% " + swapUsagePercent);
+ resolvedAction = COMPACT_ACTION_FILE;
+ }
+ }
+
+ return resolvedAction;
+ }
+
@VisibleForTesting
static final class LastCompactionStats {
private final long[] mRssAfterCompaction;
@@ -1139,6 +1239,167 @@ public final class CachedAppOptimizer {
super(mCachedAppOptimizerThread.getLooper());
}
+ private boolean shouldOomAdjThrottleCompaction(ProcessRecord proc, int action) {
+ final String name = proc.processName;
+ if (mAm.mInternal.isPendingTopUid(proc.uid)) {
+ // In case the OOM Adjust has not yet been propagated we see if this is
+ // pending on becoming top app in which case we should not compact.
+ Slog.e(TAG_AM, "Skip compaction since UID is active for " + name);
+ return true;
+ }
+
+ // don't compact if the process has returned to perceptible
+ // and this is only a cached/home/prev compaction
+ if ((action == COMPACT_ACTION_FILE || action == COMPACT_ACTION_FULL)
+ && (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping compaction as process " + name + " is "
+ + "now perceptible.");
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean shouldTimeThrottleCompaction(
+ ProcessRecord proc, long start, int pendingAction) {
+ final ProcessCachedOptimizerRecord opt = proc.mOptRecord;
+ final String name = proc.processName;
+
+ int lastCompactAction = opt.getLastCompactAction();
+ long lastCompactTime = opt.getLastCompactTime();
+
+ // basic throttling
+ // use the Phenotype flag knobs to determine whether current/prevous
+ // compaction combo should be throtted or not
+
+ // Note that we explicitly don't take mPhenotypeFlagLock here as the flags
+ // should very seldom change, and taking the risk of using the wrong action is
+ // preferable to taking the lock for every single compaction action.
+ if (lastCompactTime != 0) {
+ if (pendingAction == COMPACT_PROCESS_SOME) {
+ if ((lastCompactAction == COMPACT_PROCESS_SOME
+ && (start - lastCompactTime < mCompactThrottleSomeSome))
+ || (lastCompactAction == COMPACT_PROCESS_FULL
+ && (start - lastCompactTime < mCompactThrottleSomeFull))) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping some compaction for " + name
+ + ": too soon. throttle=" + mCompactThrottleSomeSome
+ + "/" + mCompactThrottleSomeFull
+ + " last=" + (start - lastCompactTime) + "ms ago");
+ }
+ return true;
+ }
+ } else if (pendingAction == COMPACT_PROCESS_FULL) {
+ if ((lastCompactAction == COMPACT_PROCESS_SOME
+ && (start - lastCompactTime < mCompactThrottleFullSome))
+ || (lastCompactAction == COMPACT_PROCESS_FULL
+ && (start - lastCompactTime < mCompactThrottleFullFull))) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping full compaction for " + name
+ + ": too soon. throttle=" + mCompactThrottleFullSome
+ + "/" + mCompactThrottleFullFull
+ + " last=" + (start - lastCompactTime) + "ms ago");
+ }
+ return true;
+ }
+ } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) {
+ if (start - lastCompactTime < mCompactThrottlePersistent) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping persistent compaction for " + name
+ + ": too soon. throttle=" + mCompactThrottlePersistent
+ + " last=" + (start - lastCompactTime) + "ms ago");
+ }
+ return true;
+ }
+ } else if (pendingAction == COMPACT_PROCESS_BFGS) {
+ if (start - lastCompactTime < mCompactThrottleBFGS) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping bfgs compaction for " + name
+ + ": too soon. throttle=" + mCompactThrottleBFGS
+ + " last=" + (start - lastCompactTime) + "ms ago");
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private boolean shouldThrottleMiscCompaction(
+ ProcessRecord proc, int procState, int action) {
+ final String name = proc.processName;
+ if (mProcStateThrottle.contains(procState)) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping full compaction for process " + name + "; proc state is "
+ + procState);
+ }
+ return true;
+ }
+
+ if (COMPACT_ACTION_NONE == action) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping compaction for process " + name + "since action is None");
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean shouldRssThrottleCompaction(
+ int action, int pid, String name, long[] rssBefore) {
+ long anonRssBefore = rssBefore[RSS_ANON_INDEX];
+ LastCompactionStats lastCompactionStats = mLastCompactionStats.get(pid);
+
+ if (rssBefore[RSS_TOTAL_INDEX] == 0 && rssBefore[RSS_FILE_INDEX] == 0
+ && rssBefore[RSS_ANON_INDEX] == 0 && rssBefore[RSS_SWAP_INDEX] == 0) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping compaction for"
+ + "process " + pid + " with no memory usage. Dead?");
+ }
+ return true;
+ }
+
+ if (action == COMPACT_ACTION_FULL || action == COMPACT_ACTION_ANON) {
+ if (mFullAnonRssThrottleKb > 0L && anonRssBefore < mFullAnonRssThrottleKb) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping full compaction for process " + name
+ + "; anon RSS is too small: " + anonRssBefore + "KB.");
+ }
+ return true;
+ }
+
+ if (lastCompactionStats != null && mFullDeltaRssThrottleKb > 0L) {
+ long[] lastRss = lastCompactionStats.getRssAfterCompaction();
+ long absDelta = Math.abs(rssBefore[RSS_FILE_INDEX] - lastRss[RSS_FILE_INDEX])
+ + Math.abs(rssBefore[RSS_ANON_INDEX] - lastRss[RSS_ANON_INDEX])
+ + Math.abs(rssBefore[RSS_SWAP_INDEX] - lastRss[RSS_SWAP_INDEX]);
+ if (absDelta <= mFullDeltaRssThrottleKb) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping full compaction for process " + name
+ + "; abs delta is too small: " + absDelta + "KB.");
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -1149,180 +1410,65 @@ public final class CachedAppOptimizer {
int pid;
String action;
final String name;
- int pendingAction, lastCompactAction;
+ int requestedAction, lastCompactAction;
long lastCompactTime;
- LastCompactionStats lastCompactionStats;
int lastOomAdj = msg.arg1;
int procState = msg.arg2;
+ boolean forceCompaction;
synchronized (mProcLock) {
- if(mPendingCompactionProcesses.isEmpty()) {
+ if (mPendingCompactionProcesses.isEmpty()) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM, "No processes pending compaction, bail out");
+ }
return;
}
proc = mPendingCompactionProcesses.remove(0);
opt = proc.mOptRecord;
+ forceCompaction = opt.isForceCompact();
+ opt.setForceCompact(false); // since this is a one-shot operation
- pendingAction = opt.getReqCompactAction();
+ requestedAction = opt.getReqCompactAction();
pid = proc.getPid();
name = proc.processName;
opt.setHasPendingCompact(false);
-
- if (mAm.mInternal.isPendingTopUid(proc.uid)) {
- // In case the OOM Adjust has not yet been propagated we see if this is
- // pending on becoming top app in which case we should not compact.
- Slog.e(TAG_AM, "Skip compaction since UID is active for " + name);
- return;
- }
-
- // don't compact if the process has returned to perceptible
- // and this is only a cached/home/prev compaction
- if ((pendingAction == COMPACT_PROCESS_SOME
- || pendingAction == COMPACT_PROCESS_FULL)
- && (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- "Skipping compaction as process " + name + " is "
- + "now perceptible.");
- }
- return;
- }
-
lastCompactAction = opt.getLastCompactAction();
lastCompactTime = opt.getLastCompactTime();
- lastCompactionStats = mLastCompactionStats.get(pid);
}
+ int resolvedAction = resolveCompactionAction(requestedAction);
+ long[] rssBefore;
if (pid == 0) {
// not a real process, either one being launched or one being killed
- return;
- }
-
- // basic throttling
- // use the Phenotype flag knobs to determine whether current/prevous
- // compaction combo should be throtted or not
-
- // Note that we explicitly don't take mPhenotypeFlagLock here as the flags
- // should very seldom change, and taking the risk of using the wrong action is
- // preferable to taking the lock for every single compaction action.
- if (lastCompactTime != 0) {
- if (pendingAction == COMPACT_PROCESS_SOME) {
- if ((lastCompactAction == COMPACT_PROCESS_SOME
- && (start - lastCompactTime < mCompactThrottleSomeSome))
- || (lastCompactAction == COMPACT_PROCESS_FULL
- && (start - lastCompactTime
- < mCompactThrottleSomeFull))) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skipping some compaction for " + name
- + ": too soon. throttle=" + mCompactThrottleSomeSome
- + "/" + mCompactThrottleSomeFull + " last="
- + (start - lastCompactTime) + "ms ago");
- }
- return;
- }
- } else if (pendingAction == COMPACT_PROCESS_FULL) {
- if ((lastCompactAction == COMPACT_PROCESS_SOME
- && (start - lastCompactTime < mCompactThrottleFullSome))
- || (lastCompactAction == COMPACT_PROCESS_FULL
- && (start - lastCompactTime
- < mCompactThrottleFullFull))) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skipping full compaction for " + name
- + ": too soon. throttle=" + mCompactThrottleFullSome
- + "/" + mCompactThrottleFullFull + " last="
- + (start - lastCompactTime) + "ms ago");
- }
- return;
- }
- } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) {
- if (start - lastCompactTime < mCompactThrottlePersistent) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skipping persistent compaction for " + name
- + ": too soon. throttle=" + mCompactThrottlePersistent
- + " last=" + (start - lastCompactTime) + "ms ago");
- }
- return;
- }
- } else if (pendingAction == COMPACT_PROCESS_BFGS) {
- if (start - lastCompactTime < mCompactThrottleBFGS) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skipping bfgs compaction for " + name
- + ": too soon. throttle=" + mCompactThrottleBFGS
- + " last=" + (start - lastCompactTime) + "ms ago");
- }
- return;
- }
- }
- }
-
- switch (pendingAction) {
- case COMPACT_PROCESS_SOME:
- action = mCompactActionSome;
- break;
- // For the time being, treat these as equivalent.
- case COMPACT_PROCESS_FULL:
- case COMPACT_PROCESS_PERSISTENT:
- case COMPACT_PROCESS_BFGS:
- action = mCompactActionFull;
- break;
- default:
- action = COMPACT_ACTION_STRING[COMPACT_ACTION_NONE];
- break;
- }
-
- if (COMPACT_ACTION_STRING[COMPACT_ACTION_NONE].equals(action)) {
- return;
- }
-
- if (mProcStateThrottle.contains(procState)) {
if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skipping full compaction for process " + name
- + "; proc state is " + procState);
+ Slog.d(TAG_AM, "Compaction failed, pid is 0");
}
return;
}
- long[] rssBefore = mProcessDependencies.getRss(pid);
- long anonRssBefore = rssBefore[2];
-
- if (rssBefore[0] == 0 && rssBefore[1] == 0 && rssBefore[2] == 0
- && rssBefore[3] == 0) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skipping compaction for" + "process " + pid
- + " with no memory usage. Dead?");
+ if (!forceCompaction) {
+ if (shouldOomAdjThrottleCompaction(proc, resolvedAction)) {
+ return;
}
- return;
- }
-
- if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])
- || action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
- if (mFullAnonRssThrottleKb > 0L
- && anonRssBefore < mFullAnonRssThrottleKb) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skipping full compaction for process "
- + name + "; anon RSS is too small: " + anonRssBefore
- + "KB.");
- }
+ if (shouldTimeThrottleCompaction(proc, start, requestedAction)) {
return;
}
-
- if (lastCompactionStats != null && mFullDeltaRssThrottleKb > 0L) {
- long[] lastRss = lastCompactionStats.getRssAfterCompaction();
- long absDelta = Math.abs(rssBefore[1] - lastRss[1])
- + Math.abs(rssBefore[2] - lastRss[2])
- + Math.abs(rssBefore[3] - lastRss[3]);
- if (absDelta <= mFullDeltaRssThrottleKb) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skipping full compaction for process "
- + name + "; abs delta is too small: " + absDelta
- + "KB.");
- }
- return;
- }
+ if (shouldThrottleMiscCompaction(proc, procState, resolvedAction)) {
+ return;
+ }
+ rssBefore = mProcessDependencies.getRss(pid);
+ if (shouldRssThrottleCompaction(resolvedAction, pid, name, rssBefore)) {
+ return;
+ }
+ } else {
+ rssBefore = mProcessDependencies.getRss(pid);
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM, "Forcing compaction for " + name);
}
}
// Now we've passed through all the throttles and are going to compact, update
// bookkeeping.
- switch (pendingAction) {
+ switch (requestedAction) {
case COMPACT_PROCESS_SOME:
mSomeCompactionCount++;
break;
@@ -1338,45 +1484,56 @@ public final class CachedAppOptimizer {
default:
break;
}
+ action = compactActionIntToString(resolvedAction);
+
try {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact "
- + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full")
- + ": " + name);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "Compact " + action + ": " + name);
long zramFreeKbBefore = Debug.getZramFreeKb();
mProcessDependencies.performCompaction(action, pid);
long[] rssAfter = mProcessDependencies.getRss(pid);
long end = SystemClock.uptimeMillis();
long time = end - start;
long zramFreeKbAfter = Debug.getZramFreeKb();
+ long deltaTotalRss = rssAfter[RSS_TOTAL_INDEX] - rssBefore[RSS_TOTAL_INDEX];
+ long deltaFileRss = rssAfter[RSS_FILE_INDEX] - rssBefore[RSS_FILE_INDEX];
+ long deltaAnonRss = rssAfter[RSS_ANON_INDEX] - rssBefore[RSS_ANON_INDEX];
+ long deltaSwapRss = rssAfter[RSS_SWAP_INDEX] - rssBefore[RSS_SWAP_INDEX];
EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
- rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
- rssAfter[0] - rssBefore[0], rssAfter[1] - rssBefore[1],
- rssAfter[2] - rssBefore[2], rssAfter[3] - rssBefore[3], time,
- lastCompactAction, lastCompactTime, lastOomAdj, procState,
- zramFreeKbBefore, zramFreeKbAfter - zramFreeKbBefore);
+ rssBefore[RSS_TOTAL_INDEX], rssBefore[RSS_FILE_INDEX],
+ rssBefore[RSS_ANON_INDEX], rssBefore[RSS_SWAP_INDEX], deltaTotalRss,
+ deltaFileRss, deltaAnonRss, deltaSwapRss, time, lastCompactAction,
+ lastCompactTime, lastOomAdj, procState, zramFreeKbBefore,
+ zramFreeKbAfter - zramFreeKbBefore);
// Note that as above not taking mPhenoTypeFlagLock here to avoid locking
// on every single compaction for a flag that will seldom change and the
// impact of reading the wrong value here is low.
if (mRandom.nextFloat() < mCompactStatsdSampleRate) {
FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED, pid, name,
- pendingAction, rssBefore[0], rssBefore[1], rssBefore[2],
- rssBefore[3], rssAfter[0], rssAfter[1], rssAfter[2],
- rssAfter[3], time, lastCompactAction, lastCompactTime,
- lastOomAdj, ActivityManager.processStateAmToProto(procState),
+ requestedAction, rssBefore[RSS_TOTAL_INDEX],
+ rssBefore[RSS_FILE_INDEX], rssBefore[RSS_ANON_INDEX],
+ rssBefore[RSS_SWAP_INDEX], rssAfter[RSS_TOTAL_INDEX],
+ rssAfter[RSS_FILE_INDEX], rssAfter[RSS_ANON_INDEX],
+ rssAfter[RSS_SWAP_INDEX], time, lastCompactAction,
+ lastCompactTime, lastOomAdj,
+ ActivityManager.processStateAmToProto(procState),
zramFreeKbBefore, zramFreeKbAfter);
}
synchronized (mProcLock) {
opt.setLastCompactTime(end);
- opt.setLastCompactAction(pendingAction);
+ opt.setLastCompactAction(resolvedAction);
}
- if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])
- || action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
+ if (resolvedAction == COMPACT_ACTION_FULL
+ || resolvedAction == COMPACT_ACTION_ANON) {
// Remove entry and insert again to update insertion order.
mLastCompactionStats.remove(pid);
mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter));
}
} catch (Exception e) {
// nothing to do, presumably the process died
+ Slog.d(TAG_AM,
+ "Exception occurred while compacting pid: " + name
+ + ". Exception:" + e.getMessage());
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1580,7 +1737,7 @@ public final class CachedAppOptimizer {
* Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class.
*/
private static final class DefaultProcessDependencies implements ProcessDependencies {
- public static int mPidCompacting = -1;
+ public static volatile int mPidCompacting = -1;
// Get memory RSS from process.
@Override
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index a86ba016eeff..a613729b441f 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -56,6 +56,8 @@ final class ProcessCachedOptimizerRecord {
@GuardedBy("mProcLock")
private boolean mPendingCompact;
+ @GuardedBy("mProcLock") private boolean mForceCompact;
+
/**
* True when the process is frozen.
*/
@@ -133,6 +135,16 @@ final class ProcessCachedOptimizerRecord {
}
@GuardedBy("mProcLock")
+ boolean isForceCompact() {
+ return mForceCompact;
+ }
+
+ @GuardedBy("mProcLock")
+ void setForceCompact(boolean forceCompact) {
+ mForceCompact = forceCompact;
+ }
+
+ @GuardedBy("mProcLock")
boolean isFrozen() {
return mFrozen;
}
@@ -205,6 +217,9 @@ final class ProcessCachedOptimizerRecord {
void dump(PrintWriter pw, String prefix, long nowUptime) {
pw.print(prefix); pw.print("lastCompactTime="); pw.print(mLastCompactTime);
pw.print(" lastCompactAction="); pw.println(mLastCompactAction);
+ pw.print(prefix);
+ pw.print("hasPendingCompaction=");
+ pw.print(mPendingCompact);
pw.print(prefix); pw.print("isFreezeExempt="); pw.print(mFreezeExempt);
pw.print(" isPendingFreeze="); pw.print(mPendingFreeze);
pw.print(" " + IS_FROZEN + "="); pw.println(mFrozen);
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 04fcda7835fa..122a950e4fd3 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -242,15 +242,19 @@ public class SpatializerHelper {
mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i));
}
}
+ // for both transaural / binaural, we are not forcing enablement as the init() method
+ // could have been called another time after boot in case of audioserver restart
if (mTransauralSupported) {
// TODO deal with persisted values
- mSADevices.add(
- new SADeviceState(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null));
+ addCompatibleAudioDevice(
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
+ false /*forceEnable*/);
}
if (mBinauralSupported) {
// TODO deal with persisted values
- mSADevices.add(
- new SADeviceState(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, null));
+ addCompatibleAudioDevice(
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, ""),
+ false /*forceEnable*/);
}
// TODO read persisted states
} catch (RemoteException e) {
@@ -461,6 +465,20 @@ public class SpatializerHelper {
}
synchronized void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
+ addCompatibleAudioDevice(ada, true /*forceEnable*/);
+ }
+
+ /**
+ * Add the given device to the list of devices for which spatial audio will be available
+ * (== possible).
+ * @param ada the compatible device
+ * @param forceEnable if true, spatial audio is enabled for this device, regardless of whether
+ * this device was already in the list. If false, the enabled field is only
+ * set to true if the device is added to the list, otherwise, if already
+ * present, the setting is left untouched.
+ */
+ private void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada,
+ boolean forceEnable) {
loglogi("addCompatibleAudioDevice: dev=" + ada);
final int deviceType = ada.getType();
final boolean wireless = isWireless(deviceType);
@@ -471,7 +489,9 @@ public class SpatializerHelper {
&& (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
|| !wireless) {
isInList = true;
- deviceState.mEnabled = true;
+ if (forceEnable) {
+ deviceState.mEnabled = true;
+ }
break;
}
}
@@ -508,7 +528,7 @@ public class SpatializerHelper {
private synchronized Pair<Boolean, Boolean> evaluateState(AudioDeviceAttributes ada) {
// if not a wireless device, this value will be overwritten to map the type
// to TYPE_BUILTIN_SPEAKER or TYPE_WIRED_HEADPHONES
- int deviceType = ada.getType();
+ @AudioDeviceInfo.AudioDeviceType int deviceType = ada.getType();
final boolean wireless = isWireless(deviceType);
// if not a wireless device: find if media device is in the speaker, wired headphones
@@ -1468,13 +1488,13 @@ public class SpatializerHelper {
}
private static final class SADeviceState {
- final int mDeviceType;
+ final @AudioDeviceInfo.AudioDeviceType int mDeviceType;
final @Nullable String mDeviceAddress; // non-null for wireless devices
boolean mEnabled = true; // by default, SA is enabled on any device
boolean mHasHeadTracker = false;
boolean mHeadTrackerEnabled = true; // by default, if head tracker is present, use it
- SADeviceState(int deviceType, @Nullable String address) {
+ SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @Nullable String address) {
mDeviceType = deviceType;
mDeviceAddress = address;
}
@@ -1505,7 +1525,7 @@ public class SpatializerHelper {
}
}
- private static boolean isWireless(int deviceType) {
+ private static boolean isWireless(@AudioDeviceInfo.AudioDeviceType int deviceType) {
for (int type : WIRELESS_TYPES) {
if (type == deviceType) {
return true;
@@ -1514,7 +1534,7 @@ public class SpatializerHelper {
return false;
}
- private static boolean isWirelessSpeaker(int deviceType) {
+ private static boolean isWirelessSpeaker(@AudioDeviceInfo.AudioDeviceType int deviceType) {
for (int type : WIRELESS_SPEAKER_TYPES) {
if (type == deviceType) {
return true;
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/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/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 4822ddbc0ebb..f21f90631792 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -739,20 +739,7 @@ class ActivityClientController extends IActivityClientController.Stub {
synchronized (mGlobalLock) {
final ActivityRecord r = ensureValidPictureInPictureActivityParams(
"setPictureInPictureParams", token, params);
-
- // Only update the saved args from the args that are set.
r.setPictureInPictureParams(params);
- if (r.inPinnedWindowingMode()) {
- // If the activity is already in picture-in-picture, update the pinned task now
- // if it is not already expanding to fullscreen. Otherwise, the arguments will
- // be used the next time the activity enters PiP.
- final Task rootTask = r.getRootTask();
- rootTask.setPictureInPictureAspectRatio(
- r.pictureInPictureArgs.getAspectRatioFloat(),
- r.pictureInPictureArgs.getExpandedAspectRatioFloat());
- rootTask.setPictureInPictureActions(r.pictureInPictureArgs.getActions(),
- r.pictureInPictureArgs.getCloseAction());
- }
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 41b724f2596f..8f689be48c21 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -148,7 +148,6 @@ import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
import android.app.ProfilerInfo;
-import android.app.RemoteAction;
import android.app.WaitResult;
import android.app.admin.DevicePolicyCache;
import android.app.assist.AssistContent;
@@ -3471,19 +3470,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r);
return;
}
- // Only update the saved args from the args that are set
r.setPictureInPictureParams(params);
- final float aspectRatio = r.pictureInPictureArgs.getAspectRatioFloat();
- final float expandedAspectRatio =
- r.pictureInPictureArgs.getExpandedAspectRatioFloat();
- final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
- final RemoteAction closeAction = r.pictureInPictureArgs.getCloseAction();
mRootWindowContainer.moveActivityToPinnedRootTask(r,
null /* launchIntoPipHostActivity */, "enterPictureInPictureMode");
final Task task = r.getTask();
- task.setPictureInPictureAspectRatio(aspectRatio, expandedAspectRatio);
- task.setPictureInPictureActions(actions, closeAction);
-
// Continue the pausing process after entering pip.
if (task.getPausingActivity() == r) {
task.schedulePauseActivity(r, false /* userLeaving */,
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 43d077664fd5..1ddeee9f46d9 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -22,9 +22,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.PictureInPictureParams;
-import android.app.RemoteAction;
import android.content.ComponentName;
-import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Matrix;
@@ -40,8 +38,6 @@ import android.view.SurfaceControl;
import android.window.PictureInPictureSurfaceTransaction;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
/**
* Holds the common state of the pinned task between the system and SystemUI. If SystemUI ever
@@ -90,12 +86,6 @@ class PinnedTaskController {
private boolean mIsImeShowing;
private int mImeHeight;
- // The set of actions and aspect-ratio for the that are currently allowed on the PiP activity
- private ArrayList<RemoteAction> mActions = new ArrayList<>();
- private RemoteAction mCloseAction;
- private float mAspectRatio = -1f;
- private float mExpandedAspectRatio = 0f;
-
// The aspect ratio bounds of the PIP.
private float mMinAspectRatio;
private float mMaxAspectRatio;
@@ -155,7 +145,6 @@ class PinnedTaskController {
mPinnedTaskListener = listener;
notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
notifyMovementBoundsChanged(false /* fromImeAdjustment */);
- notifyActionsChanged(mActions, mCloseAction);
} catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned task listener", e);
}
@@ -370,55 +359,6 @@ class PinnedTaskController {
}
/**
- * Sets the current aspect ratio.
- */
- void setAspectRatio(float aspectRatio) {
- if (Float.compare(mAspectRatio, aspectRatio) != 0) {
- mAspectRatio = aspectRatio;
- notifyAspectRatioChanged(aspectRatio);
- notifyMovementBoundsChanged(false /* fromImeAdjustment */);
- }
- }
-
- /**
- * @return the current aspect ratio.
- */
- float getAspectRatio() {
- return mAspectRatio;
- }
-
- /**
- * Sets the current aspect ratio.
- */
- void setExpandedAspectRatio(float aspectRatio) {
- if (Float.compare(mExpandedAspectRatio, aspectRatio) != 0) {
- mExpandedAspectRatio = aspectRatio;
- notifyExpandedAspectRatioChanged(aspectRatio);
- notifyMovementBoundsChanged(false /* fromImeAdjustment */);
- }
- }
-
- /**
- * @return the current aspect ratio.
- */
- float getExpandedAspectRatio() {
- return mExpandedAspectRatio;
- }
-
-
- /**
- * Sets the current set of actions.
- */
- void setActions(List<RemoteAction> actions, RemoteAction closeAction) {
- mActions.clear();
- if (actions != null) {
- mActions.addAll(actions);
- }
- mCloseAction = closeAction;
- notifyActionsChanged(mActions, closeAction);
- }
-
- /**
* Notifies listeners that the PIP needs to be adjusted for the IME.
*/
private void notifyImeVisibilityChanged(boolean imeVisible, int imeHeight) {
@@ -431,37 +371,6 @@ class PinnedTaskController {
}
}
- private void notifyAspectRatioChanged(float aspectRatio) {
- if (mPinnedTaskListener == null) return;
- try {
- mPinnedTaskListener.onAspectRatioChanged(aspectRatio);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering aspect ratio changed event.", e);
- }
- }
-
- private void notifyExpandedAspectRatioChanged(float aspectRatio) {
- if (mPinnedTaskListener == null) return;
- try {
- mPinnedTaskListener.onExpandedAspectRatioChanged(aspectRatio);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering aspect ratio changed event.", e);
- }
- }
-
- /**
- * Notifies listeners that the PIP actions have changed.
- */
- private void notifyActionsChanged(List<RemoteAction> actions, RemoteAction closeAction) {
- if (mPinnedTaskListener != null) {
- try {
- mPinnedTaskListener.onActionsChanged(new ParceledListSlice(actions), closeAction);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering actions changed event.", e);
- }
- }
- }
-
/**
* Notifies listeners that the PIP movement bounds have changed.
*/
@@ -490,19 +399,7 @@ class PinnedTaskController {
}
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
pw.println(prefix + " mImeHeight=" + mImeHeight);
- pw.println(prefix + " mAspectRatio=" + mAspectRatio);
pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio);
pw.println(prefix + " mMaxAspectRatio=" + mMaxAspectRatio);
- if (mActions.isEmpty()) {
- pw.println(prefix + " mActions=[]");
- } else {
- pw.println(prefix + " mActions=[");
- for (int i = 0; i < mActions.size(); i++) {
- RemoteAction action = mActions.get(i);
- pw.print(prefix + " Action[" + i + "]: ");
- action.dump("", pw);
- }
- pw.println(prefix + " ]");
- }
}
}
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/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/Task.java b/services/core/java/com/android/server/wm/Task.java
index 00e61171cb68..718ce2870f10 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -149,7 +149,6 @@ import android.app.ActivityTaskManager;
import android.app.AppGlobals;
import android.app.IActivityController;
import android.app.PictureInPictureParams;
-import android.app.RemoteAction;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.content.ComponentName;
@@ -218,7 +217,6 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -6076,58 +6074,6 @@ class Task extends TaskFragment {
}
}
- /**
- * Sets the current picture-in-picture aspect ratios.
- */
- void setPictureInPictureAspectRatio(float aspectRatio, float expandedAspectRatio) {
- if (!mWmService.mAtmService.mSupportsPictureInPicture) {
- return;
- }
-
- final DisplayContent displayContent = getDisplayContent();
- if (displayContent == null) {
- return;
- }
-
- if (!inPinnedWindowingMode()) {
- return;
- }
-
- final PinnedTaskController pinnedTaskController =
- getDisplayContent().getPinnedTaskController();
-
- // Notify the pinned stack controller about aspect ratio change.
- // This would result a callback delivered from SystemUI to WM to start animation,
- // if the bounds are ought to be altered due to aspect ratio change.
- if (Float.compare(aspectRatio, pinnedTaskController.getAspectRatio()) != 0) {
- pinnedTaskController.setAspectRatio(
- pinnedTaskController.isValidPictureInPictureAspectRatio(aspectRatio)
- ? aspectRatio : -1f);
- }
-
- if (mWmService.mAtmService.mSupportsExpandedPictureInPicture && Float.compare(
- expandedAspectRatio, pinnedTaskController.getExpandedAspectRatio()) != 0) {
- pinnedTaskController.setExpandedAspectRatio(pinnedTaskController
- .isValidExpandedPictureInPictureAspectRatio(expandedAspectRatio)
- ? expandedAspectRatio : 0f);
- }
- }
-
- /**
- * Sets the current picture-in-picture actions.
- */
- void setPictureInPictureActions(List<RemoteAction> actions, RemoteAction closeAction) {
- if (!mWmService.mAtmService.mSupportsPictureInPicture) {
- return;
- }
-
- if (!inPinnedWindowingMode()) {
- return;
- }
-
- getDisplayContent().getPinnedTaskController().setActions(actions, closeAction);
- }
-
public DisplayInfo getDisplayInfo() {
return mDisplayContent.getDisplayInfo();
}
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/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 636ca4143a33..49a4021b1a84 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "CachedAppOptimizer"
//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_ACTIVITY_MANAGER
+#define ATRACE_COMPACTION_TRACK "Compaction"
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -37,8 +39,10 @@
#include <sys/pidfd.h>
#include <sys/stat.h>
#include <sys/syscall.h>
+#include <sys/sysinfo.h>
#include <sys/types.h>
#include <unistd.h>
+#include <utils/Trace.h>
#include <algorithm>
@@ -62,18 +66,197 @@ using android::base::unique_fd;
// Defines the maximum amount of VMAs we can send per process_madvise syscall.
// Currently this is set to UIO_MAXIOV which is the maximum segments allowed by
// iovec implementation used by process_madvise syscall
-#define MAX_VMAS_PER_COMPACTION UIO_MAXIOV
+#define MAX_VMAS_PER_BATCH UIO_MAXIOV
// Maximum bytes that we can send per process_madvise syscall once this limit
// is reached we split the remaining VMAs into another syscall. The MAX_RW_COUNT
// limit is imposed by iovec implementation. However, if you want to use a smaller
-// limit, it has to be a page aligned value, otherwise, compaction would fail.
-#define MAX_BYTES_PER_COMPACTION MAX_RW_COUNT
+// limit, it has to be a page aligned value.
+#define MAX_BYTES_PER_BATCH MAX_RW_COUNT
+
+// Selected a high enough number to avoid clashing with linux errno codes
+#define ERROR_COMPACTION_CANCELLED -1000
namespace android {
-static bool cancelRunningCompaction;
-static bool compactionInProgress;
+// Signal happening in separate thread that would bail out compaction
+// before starting next VMA batch
+static std::atomic<bool> cancelRunningCompaction;
+
+// A VmaBatch represents a set of VMAs that can be processed
+// as VMAs are processed by client code it is expected that the
+// VMAs get consumed which means they are discarded as they are
+// processed so that the first element always is the next element
+// to be sent
+struct VmaBatch {
+ struct iovec* vmas;
+ // total amount of VMAs to reach the end of iovec
+ int totalVmas;
+ // total amount of bytes that are remaining within iovec
+ uint64_t totalBytes;
+};
+
+// Advances the iterator by the specified amount of bytes.
+// This is used to remove already processed or no longer
+// needed parts of the batch.
+// Returns total bytes consumed
+int consumeBytes(VmaBatch& batch, uint64_t bytesToConsume) {
+ int index = 0;
+ if (CC_UNLIKELY(bytesToConsume) < 0) {
+ LOG(ERROR) << "Cannot consume negative bytes for VMA batch !";
+ return 0;
+ }
+
+ if (bytesToConsume > batch.totalBytes) {
+ // Avoid consuming more bytes than available
+ bytesToConsume = batch.totalBytes;
+ }
+
+ uint64_t bytesConsumed = 0;
+ while (bytesConsumed < bytesToConsume) {
+ if (CC_UNLIKELY(index >= batch.totalVmas)) {
+ // reach the end of the batch
+ return bytesConsumed;
+ }
+ if (CC_UNLIKELY(bytesConsumed + batch.vmas[index].iov_len > bytesToConsume)) {
+ // this is the whole VMA that will be consumed
+ break;
+ }
+ bytesConsumed += batch.vmas[index].iov_len;
+ batch.totalBytes -= batch.vmas[index].iov_len;
+ --batch.totalVmas;
+ ++index;
+ }
+
+ // Move pointer to consume all the whole VMAs
+ batch.vmas = batch.vmas + index;
+
+ // Consume the rest of the bytes partially at last VMA in batch
+ uint64_t bytesLeftToConsume = bytesToConsume - bytesConsumed;
+ bytesConsumed += bytesLeftToConsume;
+ if (batch.totalVmas > 0) {
+ batch.vmas[0].iov_base = (void*)((uint64_t)batch.vmas[0].iov_base + bytesLeftToConsume);
+ }
+
+ return bytesConsumed;
+}
+
+// given a source of vmas this class will act as a factory
+// of VmaBatch objects and it will allow generating batches
+// until there are no more left in the source vector.
+// Note: the class does not actually modify the given
+// vmas vector, instead it iterates on it until the end.
+class VmaBatchCreator {
+ const std::vector<Vma>* sourceVmas;
+ // This is the destination array where batched VMAs will be stored
+ // it gets encapsulated into a VmaBatch which is the object
+ // meant to be used by client code.
+ struct iovec* destVmas;
+
+ // Parameters to keep track of the iterator on the source vmas
+ int currentIndex_;
+ uint64_t currentOffset_;
+
+public:
+ VmaBatchCreator(const std::vector<Vma>* vmasToBatch, struct iovec* destVmasVec)
+ : sourceVmas(vmasToBatch), destVmas(destVmasVec), currentIndex_(0), currentOffset_(0) {}
+
+ int currentIndex() { return currentIndex_; }
+ uint64_t currentOffset() { return currentOffset_; }
+
+ // Generates a batch and moves the iterator on the source vmas
+ // past the last VMA in the batch.
+ // Returns true on success, false on failure
+ bool createNextBatch(VmaBatch& batch) {
+ if (currentIndex_ >= MAX_VMAS_PER_BATCH && currentIndex_ >= sourceVmas->size()) {
+ return false;
+ }
+
+ const std::vector<Vma>& vmas = *sourceVmas;
+ batch.vmas = destVmas;
+ uint64_t totalBytesInBatch = 0;
+ int indexInBatch = 0;
+
+ // Add VMAs to the batch up until we consumed all the VMAs or
+ // reached any imposed limit of VMAs per batch.
+ while (indexInBatch < MAX_VMAS_PER_BATCH && currentIndex_ < vmas.size()) {
+ uint64_t vmaStart = vmas[currentIndex_].start + currentOffset_;
+ uint64_t vmaSize = vmas[currentIndex_].end - vmaStart;
+ if (CC_UNLIKELY(vmaSize == 0)) {
+ // No more bytes to batch for this VMA, move to next one
+ // this only happens if a batch partially consumed bytes
+ // and offset landed at exactly the end of a vma
+ continue;
+ }
+ batch.vmas[indexInBatch].iov_base = (void*)vmaStart;
+ uint64_t bytesAvailableInBatch = MAX_BYTES_PER_BATCH - totalBytesInBatch;
+
+ if (vmaSize >= bytesAvailableInBatch) {
+ // VMA would exceed the max available bytes in batch
+ // clamp with available bytes and finish batch.
+ vmaSize = bytesAvailableInBatch;
+ currentOffset_ += bytesAvailableInBatch;
+ }
+
+ batch.vmas[indexInBatch].iov_len = vmaSize;
+ totalBytesInBatch += vmaSize;
+
+ ++indexInBatch;
+ if (totalBytesInBatch >= MAX_BYTES_PER_BATCH) {
+ // Reached max bytes quota so this marks
+ // the end of the batch
+ break;
+ }
+
+ // Fully finished current VMA, move to next one
+ currentOffset_ = 0;
+ ++currentIndex_;
+ }
+ // Vmas where fully filled and we are past the last filled index.
+ batch.totalVmas = indexInBatch;
+ batch.totalBytes = totalBytesInBatch;
+ return true;
+ }
+};
+
+// Madvise a set of VMAs given in a batch for a specific process
+// The total number of bytes successfully madvised will be set on
+// outBytesProcessed.
+// Returns 0 on success and standard linux -errno code returned by
+// process_madvise on failure
+int madviseVmasFromBatch(unique_fd& pidfd, VmaBatch& batch, int madviseType,
+ uint64_t* outBytesProcessed) {
+ if (batch.totalVmas == 0) {
+ // No VMAs in Batch, skip.
+ *outBytesProcessed = 0;
+ return 0;
+ }
+
+ ATRACE_BEGIN(StringPrintf("Madvise %d: %d VMAs", madviseType, batch.totalVmas).c_str());
+ uint64_t bytesProcessedInSend =
+ process_madvise(pidfd, batch.vmas, batch.totalVmas, madviseType, 0);
+ ATRACE_END();
+
+ if (CC_UNLIKELY(bytesProcessedInSend == -1)) {
+ bytesProcessedInSend = 0;
+ if (errno != EINVAL) {
+ // Forward irrecoverable errors and bail out compaction
+ *outBytesProcessed = 0;
+ return -errno;
+ }
+ }
+
+ if (bytesProcessedInSend < batch.totalBytes) {
+ // Did not process all the bytes requested
+ // skip last page which likely failed
+ bytesProcessedInSend += PAGE_SIZE;
+ }
+
+ bytesProcessedInSend = consumeBytes(batch, bytesProcessedInSend);
+
+ *outBytesProcessed = bytesProcessedInSend;
+ return 0;
+}
// Legacy method for compacting processes, any new code should
// use compactProcess instead.
@@ -88,8 +271,6 @@ static inline void compactProcessProcfs(int pid, const std::string& compactionTy
// If any VMA fails compaction due to -EINVAL it will be skipped and continue.
// However, if it fails for any other reason, it will bail out and forward the error
static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
- static struct iovec vmasToKernel[MAX_VMAS_PER_COMPACTION];
-
if (vmas.empty()) {
return 0;
}
@@ -99,73 +280,34 @@ static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseT
// Skip compaction if failed to open pidfd with any error
return -errno;
}
- compactionInProgress = true;
- cancelRunningCompaction = false;
+
+ struct iovec destVmas[MAX_VMAS_PER_BATCH];
+
+ VmaBatch batch;
+ VmaBatchCreator batcher(&vmas, destVmas);
int64_t totalBytesProcessed = 0;
+ while (batcher.createNextBatch(batch)) {
+ uint64_t bytesProcessedInSend;
- int64_t vmaOffset = 0;
- for (int iVma = 0; iVma < vmas.size();) {
- uint64_t bytesSentToCompact = 0;
- int iVec = 0;
- while (iVec < MAX_VMAS_PER_COMPACTION && iVma < vmas.size()) {
- if (CC_UNLIKELY(cancelRunningCompaction)) {
+ do {
+ if (CC_UNLIKELY(cancelRunningCompaction.load())) {
// There could be a significant delay between when a compaction
// is requested and when it is handled during this time our
// OOM adjust could have improved.
LOG(DEBUG) << "Cancelled running compaction for " << pid;
- break;
- }
-
- uint64_t vmaStart = vmas[iVma].start + vmaOffset;
- uint64_t vmaSize = vmas[iVma].end - vmaStart;
- if (vmaSize == 0) {
- goto next_vma;
- }
- vmasToKernel[iVec].iov_base = (void*)vmaStart;
- if (vmaSize > MAX_BYTES_PER_COMPACTION - bytesSentToCompact) {
- // Exceeded the max bytes that could be sent, so clamp
- // the end to avoid exceeding limit and issue compaction
- vmaSize = MAX_BYTES_PER_COMPACTION - bytesSentToCompact;
+ ATRACE_INSTANT_FOR_TRACK(ATRACE_COMPACTION_TRACK,
+ StringPrintf("Cancelled compaction for %d", pid).c_str());
+ return ERROR_COMPACTION_CANCELLED;
}
-
- vmasToKernel[iVec].iov_len = vmaSize;
- bytesSentToCompact += vmaSize;
- ++iVec;
- if (bytesSentToCompact >= MAX_BYTES_PER_COMPACTION) {
- // Ran out of bytes within iovec, dispatch compaction.
- vmaOffset += vmaSize;
- break;
+ int error = madviseVmasFromBatch(pidfd, batch, madviseType, &bytesProcessedInSend);
+ if (error < 0) {
+ // Returns standard linux errno code
+ return error;
}
-
- next_vma:
- // Finished current VMA, and have more bytes remaining
- vmaOffset = 0;
- ++iVma;
- }
-
- if (cancelRunningCompaction) {
- cancelRunningCompaction = false;
- break;
- }
-
- auto bytesProcessed = process_madvise(pidfd, vmasToKernel, iVec, madviseType, 0);
-
- if (CC_UNLIKELY(bytesProcessed == -1)) {
- if (errno == EINVAL) {
- // This error is somewhat common due to an unevictable VMA if this is
- // the case silently skip the bad VMA and continue compacting the rest.
- continue;
- } else {
- // Forward irrecoverable errors and bail out compaction
- compactionInProgress = false;
- return -errno;
- }
- }
-
- totalBytesProcessed += bytesProcessed;
+ totalBytesProcessed += bytesProcessedInSend;
+ } while (batch.totalBytes > 0);
}
- compactionInProgress = false;
return totalBytesProcessed;
}
@@ -194,9 +336,12 @@ static int getAnyPageAdvice(const Vma& vma) {
//
// Currently supported behaviors are MADV_COLD and MADV_PAGEOUT.
//
-// Returns the total number of bytes compacted or forwards an
-// process_madvise error.
+// Returns the total number of bytes compacted on success. On error
+// returns process_madvise errno code or if compaction was cancelled
+// it returns ERROR_COMPACTION_CANCELLED.
static int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
+ cancelRunningCompaction.store(false);
+
ProcMemInfo meminfo(pid);
std::vector<Vma> pageoutVmas, coldVmas;
auto vmaCollectorCb = [&coldVmas,&pageoutVmas,&vmaToAdviseFunc](const Vma& vma) {
@@ -215,12 +360,14 @@ static int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
int64_t pageoutBytes = compactMemory(pageoutVmas, pid, MADV_PAGEOUT);
if (pageoutBytes < 0) {
// Error, just forward it.
+ cancelRunningCompaction.store(false);
return pageoutBytes;
}
int64_t coldBytes = compactMemory(coldVmas, pid, MADV_COLD);
if (coldBytes < 0) {
// Error, just forward it.
+ cancelRunningCompaction.store(false);
return coldBytes;
}
@@ -300,9 +447,18 @@ static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, job
}
static void com_android_server_am_CachedAppOptimizer_cancelCompaction(JNIEnv*, jobject) {
- if (compactionInProgress) {
- cancelRunningCompaction = true;
+ cancelRunningCompaction.store(true);
+ ATRACE_INSTANT_FOR_TRACK(ATRACE_COMPACTION_TRACK, "Cancel compaction");
+}
+
+static jdouble com_android_server_am_CachedAppOptimizer_getFreeSwapPercent(JNIEnv*, jobject) {
+ struct sysinfo memoryInfo;
+ int error = sysinfo(&memoryInfo);
+ if(error == -1) {
+ LOG(ERROR) << "Could not check free swap space";
+ return 0;
}
+ return (double)memoryInfo.freeswap / (double)memoryInfo.totalswap;
}
static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
@@ -358,6 +514,8 @@ static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"cancelCompaction", "()V",
(void*)com_android_server_am_CachedAppOptimizer_cancelCompaction},
+ {"getFreeSwapPercent", "()D",
+ (void*)com_android_server_am_CachedAppOptimizer_getFreeSwapPercent},
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
{"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
{"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 90fd8edacce3..8aa9f60e922d 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -23,7 +23,7 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
+// import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -174,6 +174,7 @@ public class MidiService extends IMidiManager.Stub {
};
private final class Client implements IBinder.DeathRecipient {
+ private static final String TAG = "MidiService.Client";
// Binder token for this client
private final IBinder mToken;
// This client's UID
@@ -215,7 +216,9 @@ public class MidiService extends IMidiManager.Stub {
}
public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback) {
+ Log.d(TAG, "addDeviceConnection() device:" + device);
if (mDeviceConnections.size() >= MAX_CONNECTIONS_PER_CLIENT) {
+ Log.i(TAG, "too many MIDI connections for UID = " + mUid);
throw new SecurityException(
"too many MIDI connections for UID = " + mUid);
}
@@ -343,6 +346,7 @@ public class MidiService extends IMidiManager.Stub {
}
private final class Device implements IBinder.DeathRecipient {
+ private static final String TAG = "MidiService.Device";
private IMidiDeviceServer mServer;
private MidiDeviceInfo mDeviceInfo;
private final BluetoothDevice mBluetoothDevice;
@@ -378,6 +382,7 @@ public class MidiService extends IMidiManager.Stub {
}
private void setDeviceServer(IMidiDeviceServer server) {
+ Log.i(TAG, "setDeviceServer()");
if (server != null) {
if (mServer != null) {
Log.e(TAG, "mServer already set in setDeviceServer");
@@ -459,21 +464,28 @@ public class MidiService extends IMidiManager.Stub {
}
public void addDeviceConnection(DeviceConnection connection) {
+ Log.d(TAG, "addDeviceConnection() [A] connection:" + connection);
synchronized (mDeviceConnections) {
+ Log.d(TAG, " mServer:" + mServer);
if (mServer != null) {
+ Log.i(TAG, "++++ A");
mDeviceConnections.add(connection);
connection.notifyClient(mServer);
} else if (mServiceConnection == null &&
(mServiceInfo != null || mBluetoothDevice != null)) {
+ Log.i(TAG, "++++ B");
mDeviceConnections.add(connection);
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(TAG, "++++ onServiceConnected() mBluetoothDevice:"
+ + mBluetoothDevice);
IMidiDeviceServer server = null;
if (mBluetoothDevice != null) {
IBluetoothMidiService mBluetoothMidiService =
IBluetoothMidiService.Stub.asInterface(service);
+ Log.i(TAG, "++++ mBluetoothMidiService:" + mBluetoothMidiService);
if (mBluetoothMidiService != null) {
try {
// We need to explicitly add the device in a separate method
@@ -592,6 +604,7 @@ public class MidiService extends IMidiManager.Stub {
// Represents a connection between a client and a device
private final class DeviceConnection {
+ private static final String TAG = "MidiService.DeviceConnection";
private final IBinder mToken = new Binder();
private final Device mDevice;
private final Client mClient;
@@ -616,6 +629,8 @@ public class MidiService extends IMidiManager.Stub {
}
public void notifyClient(IMidiDeviceServer deviceServer) {
+ Log.d(TAG, "notifyClient");
+
if (mCallback != null) {
try {
mCallback.onDeviceOpened(deviceServer, (deviceServer == null ? null : mToken));
@@ -628,7 +643,9 @@ public class MidiService extends IMidiManager.Stub {
@Override
public String toString() {
- return "DeviceConnection Device ID: " + mDevice.getDeviceInfo().getId();
+// return "DeviceConnection Device ID: " + mDevice.getDeviceInfo().getId();
+ return mDevice != null && mDevice.getDeviceInfo() != null
+ ? ("" + mDevice.getDeviceInfo().getId()) : "null";
}
}
@@ -669,9 +686,10 @@ public class MidiService extends IMidiManager.Stub {
}
private void dumpUuids(BluetoothDevice btDevice) {
- Log.d(TAG, "UUIDs for " + btDevice);
-
ParcelUuid[] uuidParcels = btDevice.getUuids();
+ Log.d(TAG, "dumpUuids(" + btDevice + ") numParcels:"
+ + (uuidParcels != null ? uuidParcels.length : 0));
+
if (uuidParcels == null) {
Log.d(TAG, "No UUID Parcels");
return;
@@ -707,7 +725,8 @@ public class MidiService extends IMidiManager.Stub {
}
switch (action) {
- case BluetoothDevice.ACTION_ACL_CONNECTED: {
+ case BluetoothDevice.ACTION_ACL_CONNECTED:
+ {
Log.d(TAG, "ACTION_ACL_CONNECTED");
dumpIntentExtras(intent);
// BLE-MIDI controllers are by definition BLE, so if this device
@@ -734,7 +753,8 @@ public class MidiService extends IMidiManager.Stub {
}
break;
- case BluetoothDevice.ACTION_ACL_DISCONNECTED: {
+ case BluetoothDevice.ACTION_ACL_DISCONNECTED:
+ {
Log.d(TAG, "ACTION_ACL_DISCONNECTED");
BluetoothDevice btDevice =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
@@ -745,6 +765,35 @@ public class MidiService extends IMidiManager.Stub {
}
}
break;
+
+ case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
+// {
+// Log.d(TAG, "ACTION_BOND_STATE_CHANGED");
+// int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
+// Log.d(TAG, " bondState:" + bondState);
+// BluetoothDevice btDevice =
+// intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+// Log.d(TAG, " btDevice:" + btDevice);
+// dumpUuids(btDevice);
+// if (isBLEMIDIDevice(btDevice)) {
+// Log.d(TAG, "BT MIDI DEVICE");
+// openBluetoothDevice(btDevice);
+// }
+// }
+// break;
+
+ case BluetoothDevice.ACTION_UUID:
+ {
+ Log.d(TAG, "ACTION_UUID");
+ BluetoothDevice btDevice =
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ dumpUuids(btDevice);
+ if (isBLEMIDIDevice(btDevice)) {
+ Log.d(TAG, "BT MIDI DEVICE");
+ openBluetoothDevice(btDevice);
+ }
+ }
+ break;
}
}
};
@@ -753,11 +802,15 @@ public class MidiService extends IMidiManager.Stub {
mContext = context;
mPackageManager = context.getPackageManager();
+ // TEMPORARY - Disable BTL-MIDI
+ //FIXME - b/25689266
// Setup broadcast receivers
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
- filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
- context.registerReceiver(mBleMidiReceiver, filter);
+// IntentFilter filter = new IntentFilter();
+// filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+// filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+// filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+// filter.addAction(BluetoothDevice.ACTION_UUID);
+// context.registerReceiver(mBleMidiReceiver, filter);
mBluetoothServiceUid = -1;
@@ -771,6 +824,8 @@ public class MidiService extends IMidiManager.Stub {
mNonMidiUUIDs.add(BluetoothUuid.LE_AUDIO);
mNonMidiUUIDs.add(BluetoothUuid.HOGP);
mNonMidiUUIDs.add(BluetoothUuid.HEARING_AID);
+ // This one is coming up
+ // mNonMidiUUIDs.add(BluetoothUuid.BATTERY);
}
private void onUnlockUser() {
@@ -876,11 +931,13 @@ public class MidiService extends IMidiManager.Stub {
public void openDevice(IBinder token, MidiDeviceInfo deviceInfo,
IMidiDeviceOpenCallback callback) {
Client client = getClient(token);
+ Log.d(TAG, "openDevice() client:" + client);
if (client == null) return;
Device device;
synchronized (mDevicesByInfo) {
device = mDevicesByInfo.get(deviceInfo);
+ Log.d(TAG, " device:" + device);
if (device == null) {
throw new IllegalArgumentException("device does not exist: " + deviceInfo);
}
@@ -901,6 +958,7 @@ public class MidiService extends IMidiManager.Stub {
// clear calling identity so bindService does not fail
final long identity = Binder.clearCallingIdentity();
try {
+ Log.i(TAG, "addDeviceConnection() [B] device:" + device);
client.addDeviceConnection(device, callback);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -916,6 +974,7 @@ public class MidiService extends IMidiManager.Stub {
@Override
public void onDeviceOpened(MidiDevice device) {
synchronized (mBleMidiDeviceMap) {
+ Log.i(TAG, "onDeviceOpened() device:" + device);
mBleMidiDeviceMap.put(bluetoothDevice, device);
}
}
@@ -949,6 +1008,7 @@ public class MidiService extends IMidiManager.Stub {
// Bluetooth devices are created on demand
Device device;
+ Log.i(TAG, "alloc device...");
synchronized (mDevicesByInfo) {
device = mBluetoothDevices.get(bluetoothDevice);
if (device == null) {
@@ -956,10 +1016,11 @@ public class MidiService extends IMidiManager.Stub {
mBluetoothDevices.put(bluetoothDevice, device);
}
}
-
+ Log.i(TAG, "device: " + device);
// clear calling identity so bindService does not fail
final long identity = Binder.clearCallingIdentity();
try {
+ Log.i(TAG, "addDeviceConnection() [C] device:" + device);
client.addDeviceConnection(device, callback);
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index 89b204b9c999..f454ac7e9e4b 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -44,6 +44,7 @@ cc_library_shared {
"libnativehelper",
"libprocessgroup",
"libutils",
+ "libcutils",
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
"android.hardware.graphics.common@1.2",
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index bf46f555004c..2baa1ec6cdc2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -857,6 +857,7 @@ public final class CachedAppOptimizerTest {
.containsExactlyElementsIn(expected);
}
+ @SuppressWarnings("GuardedBy")
@Test
public void processWithDeltaRSSTooSmall_notFullCompacted() throws Exception {
// Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set RSS
@@ -892,7 +893,7 @@ public final class CachedAppOptimizerTest {
mProcessDependencies.setRss(rssBefore1);
mProcessDependencies.setRssAfterCompaction(rssAfter1); //
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
waitForHandler();
// THEN process IS compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -907,7 +908,7 @@ public final class CachedAppOptimizerTest {
processRecord.mOptRecord.setLastCompactTime(
processRecord.mOptRecord.getLastCompactTime() - 10_000);
// WHEN we try to run compaction.
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
waitForHandler();
// THEN process IS NOT compacted - values after compaction for process 1 should remain the
// same as from the last compaction.
@@ -923,7 +924,7 @@ public final class CachedAppOptimizerTest {
processRecord.mOptRecord.setLastCompactTime(
processRecord.mOptRecord.getLastCompactTime() - 10_000);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
waitForHandler();
// THEN process IS compacted - values after compaction for process 1 should be updated.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -932,6 +933,7 @@ public final class CachedAppOptimizerTest {
assertThat(valuesAfter).isEqualTo(rssAfter3);
}
+ @SuppressWarnings("GuardedBy")
@Test
public void processWithAnonRSSTooSmall_notFullCompacted() throws Exception {
// Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set RSS
@@ -963,7 +965,7 @@ public final class CachedAppOptimizerTest {
mProcessDependencies.setRss(rssBelowThreshold);
mProcessDependencies.setRssAfterCompaction(rssBelowThresholdAfter);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
waitForHandler();
// THEN process IS NOT compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
@@ -972,7 +974,7 @@ public final class CachedAppOptimizerTest {
mProcessDependencies.setRss(rssAboveThreshold);
mProcessDependencies.setRssAfterCompaction(rssAboveThresholdAfter);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
waitForHandler();
// THEN process IS compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -981,6 +983,7 @@ public final class CachedAppOptimizerTest {
assertThat(valuesAfter).isEqualTo(rssAboveThresholdAfter);
}
+ @SuppressWarnings("GuardedBy")
@Test
public void processWithOomAdjTooSmall_notFullCompacted() throws Exception {
// Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set Min and
@@ -993,10 +996,11 @@ public final class CachedAppOptimizerTest {
// Simulate RSS memory for which compaction should occur.
long[] rssBefore =
- new long[]{/*Total RSS*/ 15000, /*File RSS*/ 15000, /*Anon RSS*/ 15000,
- /*Swap*/ 10000};
+ new long[]{/*Total RSS*/ 15000, /*File RSS*/ 15000, /*Anon RSS*/ 15000,
+ /*Swap*/ 10000};
long[] rssAfter =
- new long[]{/*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/5000};
+ new long[]{/*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/
+ 5000};
// Process that passes properties.
int pid = 1;
ProcessRecord processRecord =
@@ -1010,7 +1014,7 @@ public final class CachedAppOptimizerTest {
processRecord.mState.setSetAdj(899);
processRecord.mState.setCurAdj(970);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
waitForHandler();
// THEN process IS NOT compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
@@ -1019,16 +1023,71 @@ public final class CachedAppOptimizerTest {
processRecord.mState.setSetAdj(910);
processRecord.mState.setCurAdj(930);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
waitForHandler();
// THEN process IS compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
long[] valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats
- .get(pid)
- .getRssAfterCompaction();
+ .get(pid)
+ .getRssAfterCompaction();
assertThat(valuesAfter).isEqualTo(rssAfter);
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void process_forceCompacted() throws Exception {
+ mCachedAppOptimizerUnderTest.init();
+ setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true);
+ setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, Long.toString(920), true);
+ setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, Long.toString(950), true);
+ initActivityManagerService();
+
+ long[] rssBefore = new long[] {/*Total RSS*/ 15000, /*File RSS*/ 15000, /*Anon RSS*/ 15000,
+ /*Swap*/ 10000};
+ long[] rssAfter = new long[] {
+ /*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/ 5000};
+ // Process that passes properties.
+ int pid = 1;
+ ProcessRecord processRecord = makeProcessRecord(pid, 2, 3, "p1", "app1");
+ mProcessDependencies.setRss(rssBefore);
+ mProcessDependencies.setRssAfterCompaction(rssAfter);
+
+ // Use an OOM Adjust value that usually avoids compaction
+ processRecord.mState.setSetAdj(100);
+ processRecord.mState.setCurAdj(100);
+
+ // Compact process full
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ waitForHandler();
+ // the process is not compacted
+ assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
+
+ // Compact process some
+ mCachedAppOptimizerUnderTest.compactAppSome(processRecord, false);
+ waitForHandler();
+ // the process is not compacted
+ assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
+
+ processRecord.mState.setSetAdj(100);
+ processRecord.mState.setCurAdj(100);
+
+ // We force a full compaction
+ mCachedAppOptimizerUnderTest.compactAppFull(processRecord, true);
+ waitForHandler();
+ // then process is compacted.
+ assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
+
+ mCachedAppOptimizerUnderTest.mLastCompactionStats.clear();
+
+ // We force a some compaction
+ mCachedAppOptimizerUnderTest.compactAppSome(processRecord, true);
+ waitForHandler();
+ // then process is compacted.
+ String executedCompactAction =
+ compactActionIntToString(processRecord.mOptRecord.getLastCompactAction());
+ assertThat(executedCompactAction)
+ .isEqualTo(mCachedAppOptimizerUnderTest.mCompactActionSome);
+ }
private void setFlag(String key, String value, boolean defaultValue) throws Exception {
mCountDown = new CountDownLatch(1);
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/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/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/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";
+ }
}