summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb2
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb2
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb2
-rw-r--r--.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java13
-rw-r--r--api/Android.bp40
-rw-r--r--cmds/app_process/Android.bp11
-rw-r--r--cmds/app_process/version-script.txt4
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/app/ActivityOptions.java13
-rw-r--r--core/java/android/app/PendingIntent.java1
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java10
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerLiteInternal.java41
-rw-r--r--core/java/android/content/pm/AppSearchShortcutInfo.java2
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java33
-rw-r--r--core/java/android/content/pm/ShortcutServiceInternal.java2
-rw-r--r--core/java/android/hardware/biometrics/BiometricAuthenticator.java5
-rw-r--r--core/java/android/os/PackageTagsList.java156
-rw-r--r--core/java/android/os/UserManager.java19
-rw-r--r--core/java/android/window/ITaskOrganizer.aidl5
-rw-r--r--core/java/android/window/SplashScreenView.java64
-rw-r--r--core/java/android/window/TaskOrganizer.java17
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java30
-rw-r--r--core/tests/coretests/src/android/os/PackageTagsListTest.java52
-rw-r--r--data/etc/car/com.android.car.cluster.home.xml1
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java12
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java78
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java45
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java14
-rw-r--r--location/java/android/location/LocationManagerInternal.java73
-rw-r--r--packages/CtsShim/apk/arm/CtsShim.apkbin5483 -> 5488 bytes
-rw-r--r--packages/CtsShim/apk/arm/CtsShimPriv.apkbin31624 -> 31627 bytes
-rw-r--r--packages/CtsShim/apk/x86/CtsShim.apkbin5483 -> 5488 bytes
-rw-r--r--packages/CtsShim/apk/x86/CtsShimPriv.apkbin24257 -> 24260 bytes
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java31
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java71
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java10
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java79
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java13
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt10
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java7
-rw-r--r--packages/SystemUI/res/layout/global_actions_change_panel.xml16
-rw-r--r--packages/SystemUI/res/layout/long_screenshot.xml1
-rw-r--r--packages/SystemUI/res/values-television/config.xml1
-rw-r--r--packages/SystemUI/res/values/attrs.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/CropView.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java257
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java200
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java63
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java2
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java18
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java28
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java5
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java15
-rw-r--r--services/core/java/com/android/server/appop/DiscreteRegistry.java90
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java11
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java103
-rw-r--r--services/core/java/com/android/server/location/provider/AbstractLocationProvider.java20
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java10
-rw-r--r--services/core/java/com/android/server/location/provider/MockableLocationProvider.java29
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java9
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java12
-rw-r--r--services/core/java/com/android/server/pm/ShortcutParser.java11
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java7
-rw-r--r--services/core/java/com/android/server/policy/AppOpsPolicy.java143
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java33
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java41
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java6
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java22
-rw-r--r--services/core/java/com/android/server/wm/Task.java34
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java29
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java6
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java57
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java14
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java3
147 files changed, 2569 insertions, 1032 deletions
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 80317e4634f3..748fe14cde3e 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7464984"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimPriv.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index 3605b6d0433b..15103b1f175c 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7464984"
target: "CtsShim"
source_file: "aosp_arm64/CtsShim.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 025ec3a9fdaf..2b9ed2fba339 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7464984"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimPriv.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index e19235a12f8f..d4ff3b7336b5 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7464984"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShim.apk"
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index fe0c7f718bb0..fb5129f18417 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -2006,16 +2006,12 @@ public class AlarmManagerService extends SystemService {
windowLength = INTERVAL_DAY;
} else if ((flags & FLAG_PRIORITIZE) == 0 && windowLength < minAllowedWindow) {
// Prioritized alarms are exempt from minimum window limits.
- if (CompatChanges.isChangeEnabled(
+ if (!isExemptFromMinWindowRestrictions(callingUid) && CompatChanges.isChangeEnabled(
AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, callingPackage,
UserHandle.getUserHandleForUid(callingUid))) {
Slog.w(TAG, "Window length " + windowLength + "ms too short; expanding to "
+ minAllowedWindow + "ms.");
windowLength = minAllowedWindow;
- } else {
- // TODO (b/185199076): Remove temporary log to catch breaking apps.
- Slog.wtf(TAG, "Short window " + windowLength + "ms specified by "
- + callingPackage);
}
}
maxElapsed = triggerElapsed + windowLength;
@@ -2409,6 +2405,13 @@ public class AlarmManagerService extends SystemService {
}
/**
+ * Returns true if the given uid can set window to be as small as it wants.
+ */
+ boolean isExemptFromMinWindowRestrictions(int uid) {
+ return isExemptFromExactAlarmPermission(uid);
+ }
+
+ /**
* Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact,
* allow-while-idle alarms.
*/
diff --git a/api/Android.bp b/api/Android.bp
index db1f64c57e2c..a84e6a6cb031 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -69,7 +69,10 @@ genrule {
dest: "current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/public/api",
dest: "android.txt",
},
@@ -151,7 +154,10 @@ genrule {
dest: "removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/public/api",
dest: "removed.txt",
},
@@ -187,7 +193,10 @@ genrule {
dest: "system-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system/api",
dest: "android.txt",
},
@@ -242,7 +251,10 @@ genrule {
dest: "system-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system/api",
dest: "removed.txt",
},
@@ -279,7 +291,10 @@ genrule {
dest: "module-lib-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/module-lib/api",
dest: "android.txt",
},
@@ -336,7 +351,10 @@ genrule {
dest: "module-lib-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/module-lib/api",
dest: "removed.txt",
},
@@ -377,7 +395,10 @@ genrule {
dest: "system-server-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "android.txt",
},
@@ -401,7 +422,10 @@ genrule {
dest: "system-server-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "removed.txt",
},
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index 0eff83c99282..a1575173ded6 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -29,7 +29,16 @@ cc_binary {
},
},
- ldflags: ["-Wl,--export-dynamic"],
+ // Symbols exported from the executable in .dynsym interpose symbols in every
+ // linker namespace, including an app's classloader namespace. Provide this
+ // version script to prevent unwanted interposition.
+ //
+ // By default, the static linker doesn't export most of an executable's symbols,
+ // but it will export a symbol that appears to override a symbol in a needed DSO.
+ // This commonly happens with C++ vaguely-linked entities, such as template
+ // functions or type_info variables. Hence, a version script is needed even for
+ // an executable.
+ version_script: "version-script.txt",
shared_libs: [
"libandroid_runtime",
diff --git a/cmds/app_process/version-script.txt b/cmds/app_process/version-script.txt
new file mode 100644
index 000000000000..a98066a67675
--- /dev/null
+++ b/cmds/app_process/version-script.txt
@@ -0,0 +1,4 @@
+{
+ local:
+ *;
+};
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 42708f9d2e15..80664ed0816f 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3190,6 +3190,7 @@ package android.window {
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public android.window.WindowContainerToken getImeTarget(int);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getRootTasks(int, @NonNull int[]);
+ method @BinderThread public void onAppSplashScreenViewRemoved(int);
method @BinderThread public void onBackPressedOnTaskRoot(@NonNull android.app.ActivityManager.RunningTaskInfo);
method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 1ce598b5fa18..8e1f263ebf03 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -419,7 +419,7 @@ public class ActivityOptions {
private IBinder mLaunchCookie;
private IRemoteTransition mRemoteTransition;
private boolean mOverrideTaskTransition;
- private int mSplashScreenThemeResId;
+ private String mSplashScreenThemeResName;
@SplashScreen.SplashScreenStyle
private int mSplashScreenStyle;
private boolean mRemoveWithTaskOrganizer;
@@ -1174,7 +1174,7 @@ public class ActivityOptions {
mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder(
KEY_REMOTE_TRANSITION));
mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
- mSplashScreenThemeResId = opts.getInt(KEY_SPLASH_SCREEN_THEME);
+ mSplashScreenThemeResName = opts.getString(KEY_SPLASH_SCREEN_THEME);
mRemoveWithTaskOrganizer = opts.getBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER);
mLaunchedFromBubble = opts.getBoolean(KEY_LAUNCHED_FROM_BUBBLE);
mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
@@ -1368,8 +1368,9 @@ public class ActivityOptions {
* Gets whether the activity want to be launched as other theme for the splash screen.
* @hide
*/
- public int getSplashScreenThemeResId() {
- return mSplashScreenThemeResId;
+ @Nullable
+ public String getSplashScreenThemeResName() {
+ return mSplashScreenThemeResName;
}
/**
@@ -1945,8 +1946,8 @@ public class ActivityOptions {
if (mOverrideTaskTransition) {
b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition);
}
- if (mSplashScreenThemeResId != 0) {
- b.putInt(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResId);
+ if (mSplashScreenThemeResName != null && !mSplashScreenThemeResName.isEmpty()) {
+ b.putString(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResName);
}
if (mRemoveWithTaskOrganizer) {
b.putBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER, mRemoveWithTaskOrganizer);
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index e68eb7471e42..0136a35e3975 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -365,7 +365,6 @@ public final class PendingIntent implements Parcelable {
}
if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
- && !"com.google.android.apps.gcs".equals(packageName)
&& !flagImmutableSet && !flagMutableSet) {
String msg = packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S
+ " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE"
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index a9bec98ce405..a0d2977cf09a 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -18,7 +18,6 @@ package android.app.admin;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.content.ComponentName;
import android.content.Intent;
import android.os.UserHandle;
@@ -256,13 +255,4 @@ public abstract class DevicePolicyManagerInternal {
* {@link #supportsResetOp(int)} is true.
*/
public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
-
- /**
- * Notifies the system that an unsafe operation reason has changed.
- *
- * @throws IllegalArgumentException if {@code checker} is not the same as set on
- * {@code DevicePolicyManagerService}.
- */
- public abstract void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker,
- @OperationSafetyReason int reason, boolean isSafe);
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java b/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java
new file mode 100644
index 000000000000..ccb99470d372
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
+
+/**
+ * Device policy manager local system service interface for methods that don't require the
+ * {@code device_admin} feature.
+ *
+ * Maintenance note: if you need to expose information from DPMS to lower level services such as
+ * PM/UM/AM/etc, then exposing it from DevicePolicyManagerInternal is not safe because it may cause
+ * lock order inversion. Consider using {@link DevicePolicyCache} instead.
+ *
+ * @hide Only for use within the system server.
+ */
+public interface DevicePolicyManagerLiteInternal {
+
+ /**
+ * Notifies the system that an unsafe operation reason has changed.
+ *
+ * @throws IllegalArgumentException if {@code checker} is not the same as set on
+ * {@code DevicePolicyManagerService.setDevicePolicySafetyChecker()}.
+ */
+ void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker,
+ @OperationSafetyReason int reason, boolean isSafe);
+}
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index 63f93bfa24e5..806091e2158d 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -423,7 +423,7 @@ public class AppSearchShortcutInfo extends GenericDocument {
shortLabelResName, longLabel, longLabelResId, longLabelResName, disabledMessage,
disabledMessageResId, disabledMessageResName, categoriesSet, intents, rank, extras,
getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri,
- disabledReason, persons, locusId, 0);
+ disabledReason, persons, locusId, null);
si.setImplicitRank(implicitRank);
if ((implicitRank & ShortcutInfo.RANK_CHANGED_BIT) != 0) {
si.setRankChanged();
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 76712b5ce2dc..a264bebb5d88 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -449,7 +449,7 @@ public final class ShortcutInfo implements Parcelable {
private int mDisabledReason;
- private int mStartingThemeResId;
+ @Nullable private String mStartingThemeResName;
private ShortcutInfo(Builder b) {
mUserId = b.mContext.getUserId();
@@ -478,8 +478,9 @@ public final class ShortcutInfo implements Parcelable {
mExtras = b.mExtras;
mLocusId = b.mLocusId;
+ mStartingThemeResName = b.mStartingThemeResId != 0
+ ? b.mContext.getResources().getResourceName(b.mStartingThemeResId) : null;
updateTimestamp();
- mStartingThemeResId = b.mStartingThemeResId;
}
/**
@@ -626,7 +627,7 @@ public final class ShortcutInfo implements Parcelable {
// Set this bit.
mFlags |= FLAG_KEY_FIELDS_ONLY;
}
- mStartingThemeResId = source.mStartingThemeResId;
+ mStartingThemeResName = source.mStartingThemeResName;
}
/**
@@ -950,8 +951,8 @@ public final class ShortcutInfo implements Parcelable {
if (source.mLocusId != null) {
mLocusId = source.mLocusId;
}
- if (source.mStartingThemeResId != 0) {
- mStartingThemeResId = source.mStartingThemeResId;
+ if (source.mStartingThemeResName != null && !source.mStartingThemeResName.isEmpty()) {
+ mStartingThemeResName = source.mStartingThemeResName;
}
}
@@ -1454,11 +1455,12 @@ public final class ShortcutInfo implements Parcelable {
}
/**
- * Returns the theme resource id used for the splash screen.
+ * Returns the theme resource name used for the splash screen.
* @hide
*/
- public int getStartingThemeResId() {
- return mStartingThemeResId;
+ @Nullable
+ public String getStartingThemeResName() {
+ return mStartingThemeResName;
}
/** @hide -- old signature, the internal code still uses it. */
@@ -2182,7 +2184,7 @@ public final class ShortcutInfo implements Parcelable {
mPersons = source.readParcelableArray(cl, Person.class);
mLocusId = source.readParcelable(cl);
mIconUri = source.readString8();
- mStartingThemeResId = source.readInt();
+ mStartingThemeResName = source.readString8();
}
@Override
@@ -2234,7 +2236,7 @@ public final class ShortcutInfo implements Parcelable {
dest.writeParcelableArray(mPersons, flags);
dest.writeParcelable(mLocusId, flags);
dest.writeString8(mIconUri);
- dest.writeInt(mStartingThemeResId);
+ dest.writeString8(mStartingThemeResName);
}
public static final @NonNull Creator<ShortcutInfo> CREATOR =
@@ -2391,10 +2393,10 @@ public final class ShortcutInfo implements Parcelable {
sb.append("disabledReason=");
sb.append(getDisabledReasonDebugString(mDisabledReason));
- if (mStartingThemeResId != 0) {
+ if (mStartingThemeResName != null && !mStartingThemeResName.isEmpty()) {
addIndentOrComma(sb, indent);
- sb.append("SplashScreenThemeResId=");
- sb.append(Integer.toHexString(mStartingThemeResId));
+ sb.append("SplashScreenThemeResName=");
+ sb.append(mStartingThemeResName);
}
addIndentOrComma(sb, indent);
@@ -2482,7 +2484,8 @@ public final class ShortcutInfo implements Parcelable {
Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
long lastChangedTimestamp,
int flags, int iconResId, String iconResName, String bitmapPath, String iconUri,
- int disabledReason, Person[] persons, LocusId locusId, int startingThemeResId) {
+ int disabledReason, Person[] persons, LocusId locusId,
+ @Nullable String startingThemeResName) {
mUserId = userId;
mId = id;
mPackageName = packageName;
@@ -2511,6 +2514,6 @@ public final class ShortcutInfo implements Parcelable {
mDisabledReason = disabledReason;
mPersons = persons;
mLocusId = locusId;
- mStartingThemeResId = startingThemeResId;
+ mStartingThemeResName = startingThemeResName;
}
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 233abf36131b..3ed5c6457fa5 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -74,7 +74,7 @@ public abstract class ShortcutServiceInternal {
/**
* Get the theme res ID of the starting window, it can be 0 if not specified.
*/
- public abstract int getShortcutStartingThemeResId(int launcherUserId,
+ public abstract @Nullable String getShortcutStartingThemeResName(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId,
int userId);
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index a0027077e1a3..60a365835e2e 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -63,6 +63,11 @@ public interface BiometricAuthenticator {
*/
int TYPE_FACE = 1 << 3;
+ /**
+ * @hide
+ */
+ int TYPE_ANY_BIOMETRIC = TYPE_FINGERPRINT | TYPE_IRIS | TYPE_FACE;
+
@IntDef(flag = true, value = {
TYPE_NONE,
TYPE_CREDENTIAL,
diff --git a/core/java/android/os/PackageTagsList.java b/core/java/android/os/PackageTagsList.java
index c94d3de33b6f..4a81dc6f592e 100644
--- a/core/java/android/os/PackageTagsList.java
+++ b/core/java/android/os/PackageTagsList.java
@@ -23,20 +23,26 @@ import android.annotation.TestApi;
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.annotations.Immutable;
+
import java.io.PrintWriter;
+import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
- * A list of packages and associated attribution tags that supports easy membership checks.
+ * A list of packages and associated attribution tags that supports easy membership checks. Supports
+ * "wildcard" attribution tags (ie, matching any attribution tag under a package) in additional to
+ * standard checks.
*
* @hide
*/
@TestApi
+@Immutable
public final class PackageTagsList implements Parcelable {
- // an empty set value matches any attribution tag
+ // an empty set value matches any attribution tag (ie, wildcard)
private final ArrayMap<String, ArraySet<String>> mPackageTags;
private PackageTagsList(@NonNull ArrayMap<String, ArraySet<String>> packageTags) {
@@ -51,15 +57,34 @@ public final class PackageTagsList implements Parcelable {
}
/**
- * Returns true if the given package is represented within this instance. If this returns true
- * this does not imply anything about whether any given attribution tag under the given package
- * name is present.
+ * Returns true if the given package is found within this instance. If this returns true this
+ * does not imply anything about whether any given attribution tag under the given package name
+ * is present.
*/
public boolean includes(@NonNull String packageName) {
return mPackageTags.containsKey(packageName);
}
/**
+ * Returns true if the given attribution tag is found within this instance under any package.
+ * Only returns true if the attribution tag literal is found, not if any package contains the
+ * set of all attribution tags.
+ *
+ * @hide
+ */
+ public boolean includesTag(@NonNull String attributionTag) {
+ final int size = mPackageTags.size();
+ for (int i = 0; i < size; i++) {
+ ArraySet<String> tags = mPackageTags.valueAt(i);
+ if (tags.contains(attributionTag)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Returns true if all attribution tags under the given package are contained within this
* instance.
*/
@@ -76,6 +101,7 @@ public final class PackageTagsList implements Parcelable {
if (tags == null) {
return false;
} else if (tags.isEmpty()) {
+ // our tags are the full set, so we contain any attribution tag
return true;
} else {
return tags.contains(attributionTag);
@@ -98,10 +124,12 @@ public final class PackageTagsList implements Parcelable {
return false;
}
if (tags.isEmpty()) {
+ // our tags are the full set, so we contain whatever the other tags are
continue;
}
ArraySet<String> otherTags = packageTagsList.mPackageTags.valueAt(i);
if (otherTags.isEmpty()) {
+ // other tags are the full set, so we can't contain them
return false;
}
if (!tags.containsAll(otherTags)) {
@@ -248,6 +276,31 @@ public final class PackageTagsList implements Parcelable {
}
/**
+ * Adds the specified package and set of attribution tags to the builder.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder add(@NonNull String packageName,
+ @NonNull Collection<String> attributionTags) {
+ if (attributionTags.isEmpty()) {
+ // the input is not allowed to specify a full set by passing in an empty collection
+ return this;
+ }
+
+ ArraySet<String> tags = mPackageTags.get(packageName);
+ if (tags == null) {
+ tags = new ArraySet<>(attributionTags);
+ mPackageTags.put(packageName, tags);
+ } else if (!tags.isEmpty()) {
+ // if we contain the full set, already done, otherwise add all the tags
+ tags.addAll(attributionTags);
+ }
+
+ return this;
+ }
+
+ /**
* Adds the specified {@link PackageTagsList} to the builder.
*/
@SuppressLint("MissingGetterMatchingBuilder")
@@ -267,13 +320,92 @@ public final class PackageTagsList implements Parcelable {
if (newTags.isEmpty()) {
add(entry.getKey());
} else {
- ArraySet<String> tags = mPackageTags.get(entry.getKey());
- if (tags == null) {
- tags = new ArraySet<>(newTags);
- mPackageTags.put(entry.getKey(), tags);
- } else if (!tags.isEmpty()) {
- tags.addAll(newTags);
- }
+ add(entry.getKey(), newTags);
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Removes all attribution tags under the specified package from the builder.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull String packageName) {
+ mPackageTags.remove(packageName);
+ return this;
+ }
+
+ /**
+ * Removes the specified package and attribution tag from the builder if and only if the
+ * specified attribution tag is listed explicitly under the package. If the package contains
+ * all possible attribution tags, then nothing will be removed.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull String packageName,
+ @Nullable String attributionTag) {
+ ArraySet<String> tags = mPackageTags.get(packageName);
+ if (tags != null && tags.remove(attributionTag) && tags.isEmpty()) {
+ mPackageTags.remove(packageName);
+ }
+ return this;
+ }
+
+ /**
+ * Removes the specified package and set of attribution tags from the builder if and only if
+ * the specified set of attribution tags are listed explicitly under the package. If the
+ * package contains all possible attribution tags, then nothing will be removed.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull String packageName,
+ @NonNull Collection<String> attributionTags) {
+ if (attributionTags.isEmpty()) {
+ // the input is not allowed to specify a full set by passing in an empty collection
+ return this;
+ }
+
+ ArraySet<String> tags = mPackageTags.get(packageName);
+ if (tags != null && tags.removeAll(attributionTags) && tags.isEmpty()) {
+ mPackageTags.remove(packageName);
+ }
+ return this;
+ }
+
+ /**
+ * Removes the specified {@link PackageTagsList} from the builder. If a package contains all
+ * possible attribution tags, it will only be removed if the package in the removed
+ * {@link PackageTagsList} also contains all possible attribution tags.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull PackageTagsList packageTagsList) {
+ return remove(packageTagsList.mPackageTags);
+ }
+
+ /**
+ * Removes the given map of package to attribution tags to the builder. An empty set of
+ * attribution tags is interpreted to imply all attribution tags under that package. If a
+ * package contains all possible attribution tags, it will only be removed if the package in
+ * the removed map also contains all possible attribution tags.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull Map<String, ? extends Set<String>> packageTagsMap) {
+ for (Map.Entry<String, ? extends Set<String>> entry : packageTagsMap.entrySet()) {
+ Set<String> removedTags = entry.getValue();
+ if (removedTags.isEmpty()) {
+ // if removing the full set, drop the package completely
+ remove(entry.getKey());
+ } else {
+ remove(entry.getKey(), removedTags);
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c22224dd0da2..7edd6e6597b2 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1322,6 +1322,24 @@ public class UserManager {
"disallow_camera_toggle";
/**
+ * This is really not a user restriction in the normal sense. This can't be set to a user,
+ * via UserManager nor via DevicePolicyManager. This is not even set in UserSettingsUtils.
+ * This is defined here purely for convenience within the settings app.
+ *
+ * TODO(b/191306258): Refactor the Settings app to remove the need for this field, and delete it
+ *
+ * Specifies whether biometrics are available to the user. This is used internally only,
+ * as a means of communications between biometric settings and
+ * {@link com.android.settingslib.enterprise.ActionDisabledByAdminControllerFactory}.
+ *
+ * @see {@link android.hardware.biometrics.ParentalControlsUtilsInternal}
+ * @see {@link com.android.settings.biometrics.ParentalControlsUtils}
+ *
+ * @hide
+ */
+ public static final String DISALLOW_BIOMETRIC = "disallow_biometric";
+
+ /**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
*
@@ -1415,6 +1433,7 @@ public class UserManager {
DISALLOW_MICROPHONE_TOGGLE,
DISALLOW_CAMERA_TOGGLE,
KEY_RESTRICTIONS_PENDING,
+ DISALLOW_BIOMETRIC,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserRestrictionKey {}
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 3eb35c2c5e8a..8b8dba89ea67 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -52,6 +52,11 @@ oneway interface ITaskOrganizer {
void copySplashScreenView(int taskId);
/**
+ * Called when the Task removed the splash screen.
+ */
+ void onAppSplashScreenViewRemoved(int taskId);
+
+ /**
* A callback when the Task is available for the registered organizer. The client is responsible
* for releasing the SurfaceControl in the callback. For non-root tasks, the leash may initially
* be hidden so it is up to the organizer to show this task.
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 6772afeb0270..4a3bf91645f2 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -34,8 +34,10 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteCallback;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
@@ -76,7 +78,7 @@ import java.time.Instant;
*/
public final class SplashScreenView extends FrameLayout {
private static final String TAG = SplashScreenView.class.getSimpleName();
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Build.IS_DEBUGGABLE;
private static final int LIGHT_BARS_MASK =
WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
@@ -85,6 +87,7 @@ public final class SplashScreenView extends FrameLayout {
| FLAG_TRANSLUCENT_NAVIGATION | FLAG_TRANSLUCENT_STATUS;
private boolean mNotCopyable;
+ private boolean mIsCopied;
private int mInitBackgroundColor;
private int mInitIconBackgroundColor;
private View mIconView;
@@ -103,6 +106,10 @@ public final class SplashScreenView extends FrameLayout {
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
@Nullable
private SurfaceView mSurfaceView;
+ @Nullable
+ private SurfaceControlViewHost mSurfaceHost;
+ @Nullable
+ private RemoteCallback mClientCallback;
// cache original window and status
private Window mWindow;
@@ -127,6 +134,7 @@ public final class SplashScreenView extends FrameLayout {
private Bitmap mParceledIconBitmap;
private Drawable mIconDrawable;
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+ private RemoteCallback mClientCallback;
private int mBrandingImageWidth;
private int mBrandingImageHeight;
private Drawable mBrandingDrawable;
@@ -161,6 +169,7 @@ public final class SplashScreenView extends FrameLayout {
}
mIconAnimationStart = Instant.ofEpochMilli(parcelable.mIconAnimationStartMillis);
mIconAnimationDuration = Duration.ofMillis(parcelable.mIconAnimationDurationMillis);
+ mClientCallback = parcelable.mClientCallback;
if (DEBUG) {
Log.d(TAG, String.format("Building from parcel drawable: %s", mIconDrawable));
}
@@ -228,6 +237,7 @@ public final class SplashScreenView extends FrameLayout {
view.mInitBackgroundColor = mBackgroundColor;
view.mInitIconBackgroundColor = mIconBackground;
view.setBackgroundColor(mBackgroundColor);
+ view.mClientCallback = mClientCallback;
view.mBrandingImageView = view.findViewById(R.id.splashscreen_branding_view);
@@ -285,7 +295,8 @@ public final class SplashScreenView extends FrameLayout {
if (mSurfacePackage == null) {
if (DEBUG) {
Log.d(TAG,
- "Creating Original SurfacePackage. SurfaceView: " + surfaceView);
+ "SurfaceControlViewHost created on thread "
+ + Thread.currentThread().getId());
}
SurfaceControlViewHost viewHost = new SurfaceControlViewHost(mContext,
@@ -297,6 +308,7 @@ public final class SplashScreenView extends FrameLayout {
SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage();
surfaceView.setChildSurfacePackage(surfacePackage);
view.mSurfacePackage = surfacePackage;
+ view.mSurfaceHost = viewHost;
view.mSurfacePackageCopy = new SurfaceControlViewHost.SurfacePackage(
surfacePackage);
} else {
@@ -357,6 +369,7 @@ public final class SplashScreenView extends FrameLayout {
* @hide
*/
public void onCopied() {
+ mIsCopied = true;
if (mSurfaceView == null) {
return;
}
@@ -369,6 +382,12 @@ public final class SplashScreenView extends FrameLayout {
mSurfacePackage = null;
}
+ /** @hide **/
+ @Nullable
+ public SurfaceControlViewHost getSurfaceHost() {
+ return mSurfaceHost;
+ }
+
@Override
public void setAlpha(float alpha) {
super.setAlpha(alpha);
@@ -407,8 +426,7 @@ public final class SplashScreenView extends FrameLayout {
if (DEBUG) {
mSurfacePackage.getSurfaceControl().addOnReparentListener(
(transaction, parent) -> Log.e(TAG,
- String.format("SurfacePackage'surface reparented.\n Parent: %s",
- parent), new Throwable()));
+ String.format("SurfacePackage'surface reparented to %s", parent)));
Log.d(TAG, "Transferring surface " + mSurfaceView.toString());
}
mSurfaceView.setChildSurfacePackage(mSurfacePackage);
@@ -466,9 +484,36 @@ public final class SplashScreenView extends FrameLayout {
mHasRemoved = true;
}
+ /** @hide **/
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ releaseAnimationSurfaceHost();
+ }
+
+ private void releaseAnimationSurfaceHost() {
+ if (mSurfaceHost != null && !mIsCopied) {
+ final SurfaceControlViewHost finalSurfaceHost = mSurfaceHost;
+ mSurfaceHost = null;
+ finalSurfaceHost.getView().post(() -> {
+ if (DEBUG) {
+ Log.d(TAG,
+ "Shell removed splash screen."
+ + " Releasing SurfaceControlViewHost on thread #"
+ + Thread.currentThread().getId());
+ }
+ finalSurfaceHost.release();
+ });
+ } else if (mSurfacePackage != null && mSurfaceHost == null) {
+ mSurfacePackage = null;
+ mClientCallback.sendResult(null);
+ }
+ }
+
/**
* Called when this view is attached to an activity. This also makes SystemUI colors
* transparent so the content of splash screen view can draw fully.
+ *
* @hide
*/
public void attachHostActivityAndSetSystemUIColors(Activity activity, Window window) {
@@ -585,6 +630,7 @@ public final class SplashScreenView extends FrameLayout {
private long mIconAnimationDurationMillis;
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+ private RemoteCallback mClientCallback;
public SplashScreenViewParcelable(SplashScreenView view) {
mIconSize = view.mIconView.getWidth();
@@ -641,6 +687,7 @@ public final class SplashScreenView extends FrameLayout {
mIconAnimationDurationMillis = source.readLong();
mIconBackground = source.readInt();
mSurfacePackage = source.readTypedObject(SurfaceControlViewHost.SurfacePackage.CREATOR);
+ mClientCallback = source.readTypedObject(RemoteCallback.CREATOR);
}
@Override
@@ -660,6 +707,7 @@ public final class SplashScreenView extends FrameLayout {
dest.writeLong(mIconAnimationDurationMillis);
dest.writeInt(mIconBackground);
dest.writeTypedObject(mSurfacePackage, flags);
+ dest.writeTypedObject(mClientCallback, flags);
}
public static final @NonNull Parcelable.Creator<SplashScreenViewParcelable> CREATOR =
@@ -697,5 +745,13 @@ public final class SplashScreenView extends FrameLayout {
int getIconBackground() {
return mIconBackground;
}
+
+ /**
+ * Sets the {@link RemoteCallback} that will be called by the client to notify the shell
+ * of the removal of the {@link SplashScreenView}.
+ */
+ public void setClientCallback(@NonNull RemoteCallback clientCallback) {
+ mClientCallback = clientCallback;
+ }
}
}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 3340cf4fb707..73995491668a 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -117,6 +117,16 @@ public class TaskOrganizer extends WindowOrganizer {
public void copySplashScreenView(int taskId) {}
/**
+ * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has
+ * removed the splash screen view.
+ * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int)
+ * @see SplashScreenView#remove()
+ */
+ @BinderThread
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ }
+
+ /**
* Called when a task with the registered windowing mode can be controlled by this task
* organizer. For non-root tasks, the leash may initially be hidden so it is up to the organizer
* to show this task.
@@ -236,11 +246,16 @@ public class TaskOrganizer extends WindowOrganizer {
}
@Override
- public void copySplashScreenView(int taskId) {
+ public void copySplashScreenView(int taskId) {
mExecutor.execute(() -> TaskOrganizer.this.copySplashScreenView(taskId));
}
@Override
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ mExecutor.execute(() -> TaskOrganizer.this.onAppSplashScreenViewRemoved(taskId));
+ }
+
+ @Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash));
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 26d6a0c74b08..aabcd7f82ac7 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -43,6 +43,10 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
@@ -53,6 +57,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -153,6 +158,11 @@ public class InteractionJankMonitor {
public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27;
public static final int CUJ_SETTINGS_PAGE_SCROLL = 28;
public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = 29;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = 30;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = 31;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33;
+ public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
private static final int NO_STATSD_LOGGING = -1;
@@ -191,6 +201,11 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
};
private static volatile InteractionJankMonitor sInstance;
@@ -240,6 +255,11 @@ public class InteractionJankMonitor {
CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET,
CUJ_SETTINGS_PAGE_SCROLL,
CUJ_LOCKSCREEN_UNLOCK_ANIMATION,
+ CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON,
+ CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER,
+ CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+ CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
+ CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -578,6 +598,16 @@ public class InteractionJankMonitor {
return "SETTINGS_PAGE_SCROLL";
case CUJ_LOCKSCREEN_UNLOCK_ANIMATION:
return "LOCKSCREEN_UNLOCK_ANIMATION";
+ case CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON:
+ return "SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON";
+ case CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER:
+ return "SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER";
+ case CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE:
+ return "SHADE_APP_LAUNCH_FROM_QS_TILE";
+ case CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON:
+ return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON";
+ case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP:
+ return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP";
}
return "UNKNOWN";
}
diff --git a/core/tests/coretests/src/android/os/PackageTagsListTest.java b/core/tests/coretests/src/android/os/PackageTagsListTest.java
index 518e02e44b06..9034a5ccdd7f 100644
--- a/core/tests/coretests/src/android/os/PackageTagsListTest.java
+++ b/core/tests/coretests/src/android/os/PackageTagsListTest.java
@@ -30,6 +30,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.Collections;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -40,7 +41,8 @@ public class PackageTagsListTest {
PackageTagsList.Builder builder = new PackageTagsList.Builder()
.add("package1", "attr1")
.add("package1", "attr2")
- .add("package2");
+ .add("package2")
+ .add("package4", Arrays.asList("attr1", "attr2"));
PackageTagsList list = builder.build();
assertTrue(list.contains(builder.build()));
@@ -49,10 +51,13 @@ public class PackageTagsListTest {
assertTrue(list.contains("package2", "attr1"));
assertTrue(list.contains("package2", "attr2"));
assertTrue(list.contains("package2", "attr3"));
+ assertTrue(list.contains("package4", "attr1"));
+ assertTrue(list.contains("package4", "attr2"));
assertTrue(list.containsAll("package2"));
assertTrue(list.includes("package1"));
assertTrue(list.includes("package2"));
assertFalse(list.contains("package1", "attr3"));
+ assertFalse(list.contains("package4", "attr3"));
assertFalse(list.containsAll("package1"));
assertFalse(list.includes("package3"));
@@ -92,6 +97,51 @@ public class PackageTagsListTest {
}
@Test
+ public void testPackageTagsList_Remove() {
+ PackageTagsList.Builder builder = new PackageTagsList.Builder()
+ .add("package1", "attr1")
+ .add("package1", "attr2")
+ .add("package2")
+ .add("package4", Arrays.asList("attr1", "attr2", "attr3"))
+ .add("package3", "attr1")
+ .remove("package1", "attr1")
+ .remove("package1", "attr2")
+ .remove("package2", "attr1")
+ .remove("package4", Arrays.asList("attr1", "attr2"))
+ .remove("package3");
+ PackageTagsList list = builder.build();
+
+ assertTrue(list.contains(builder.build()));
+ assertFalse(list.contains("package1", "attr1"));
+ assertFalse(list.contains("package1", "attr2"));
+ assertTrue(list.contains("package2", "attr1"));
+ assertTrue(list.contains("package2", "attr2"));
+ assertTrue(list.contains("package2", "attr3"));
+ assertFalse(list.contains("package3", "attr1"));
+ assertFalse(list.contains("package4", "attr1"));
+ assertFalse(list.contains("package4", "attr2"));
+ assertTrue(list.contains("package4", "attr3"));
+ assertTrue(list.containsAll("package2"));
+ assertFalse(list.includes("package1"));
+ assertTrue(list.includes("package2"));
+ assertFalse(list.includes("package3"));
+ assertTrue(list.includes("package4"));
+ }
+
+ @Test
+ public void testPackageTagsList_EmptyCollections() {
+ PackageTagsList.Builder builder = new PackageTagsList.Builder()
+ .add("package1", Collections.emptyList())
+ .add("package2")
+ .remove("package2", Collections.emptyList());
+ PackageTagsList list = builder.build();
+
+ assertTrue(list.contains(builder.build()));
+ assertFalse(list.contains("package1", "attr1"));
+ assertTrue(list.contains("package2", "attr2"));
+ }
+
+ @Test
public void testWriteToParcel() {
PackageTagsList list = new PackageTagsList.Builder()
.add("package1", "attr1")
diff --git a/data/etc/car/com.android.car.cluster.home.xml b/data/etc/car/com.android.car.cluster.home.xml
index e1d2b18d7167..a3d0fcffc813 100644
--- a/data/etc/car/com.android.car.cluster.home.xml
+++ b/data/etc/car/com.android.car.cluster.home.xml
@@ -18,5 +18,6 @@
<privapp-permissions package="com.android.car.cluster.home">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
+ <permission name="android.car.permission.CAR_MONITOR_INPUT"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 3c9086dde021..ac5e2d0fcacb 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -961,6 +961,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
+ "-1003678883": {
+ "message": "Cleaning splash screen token=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-1003060523": {
"message": "Finish needs to pause: %s",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index fe80b5845bf5..1651a8cdcad5 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -221,6 +221,7 @@ public class RippleDrawable extends LayerDrawable {
private boolean mForceSoftware;
// Patterned
+ private boolean mAddRipple = false;
private float mTargetBackgroundOpacity;
private ValueAnimator mBackgroundAnimation;
private float mBackgroundOpacity;
@@ -716,6 +717,7 @@ public class RippleDrawable extends LayerDrawable {
}
cancelExitingRipples();
+ exitPatternedAnimation();
}
@Override
@@ -807,7 +809,7 @@ public class RippleDrawable extends LayerDrawable {
}
private void startPatternedAnimation() {
- mRippleActive = true;
+ mAddRipple = true;
invalidateSelf(false);
}
@@ -862,17 +864,17 @@ public class RippleDrawable extends LayerDrawable {
h = bounds.height();
w = bounds.width();
}
- boolean shouldAnimate = mRippleActive;
+ boolean addRipple = mAddRipple;
boolean shouldExit = mExitingAnimation;
- mRippleActive = false;
mExitingAnimation = false;
- if (mRunningAnimations.size() > 0 && !shouldAnimate) {
+ mAddRipple = false;
+ if (mRunningAnimations.size() > 0 && !addRipple) {
// update paint when view is invalidated
getRipplePaint();
}
drawContent(canvas);
drawPatternedBackground(canvas, cx, cy);
- if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) {
+ if (addRipple && mRunningAnimations.size() <= MAX_RIPPLES) {
RippleAnimationSession.AnimationProperties<Float, Paint> properties =
createAnimationProperties(x, y, cx, cy, w, h);
mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps)
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 4c2863e4f594..8cea869aea34 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -40,7 +40,7 @@
<integer name="long_press_dock_anim_duration">250</integer>
<!-- Animation duration for translating of one handed when trigger / dismiss. -->
- <integer name="config_one_handed_translate_animation_duration">800</integer>
+ <integer name="config_one_handed_translate_animation_duration">600</integer>
<!-- One handed mode default offset % of display size -->
<fraction name="config_one_handed_offset">40%</fraction>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index f7fb63d9ab98..4b1955e56a6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -325,6 +325,13 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
@Override
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ if (mStartingWindow != null) {
+ mStartingWindow.onAppSplashScreenViewRemoved(taskId);
+ }
+ }
+
+ @Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
synchronized (mLock) {
onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index 8dc05de9bb8f..a525c2c0219c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -68,6 +68,11 @@ public interface OneHanded {
void setLockedDisabled(boolean locked, boolean enabled);
/**
+ * Registers callback to notify WMShell when user tap shortcut to expand notification.
+ */
+ void registerEventCallback(OneHandedEventCallback callback);
+
+ /**
* Registers callback to be notified after {@link OneHandedDisplayAreaOrganizer}
* transition start or finish
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index c275d50a5d56..b43daa0da2c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -43,6 +43,7 @@ import android.util.Slog;
import android.view.Surface;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -72,6 +73,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY =
"com.android.internal.systemui.onehanded.gestural";
private static final int OVERLAY_ENABLED_DELAY_MS = 250;
+ private static final int DISPLAY_AREA_READY_RETRY_MS = 10;
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
@@ -99,6 +101,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
private final Handler mMainHandler;
private final OneHandedImpl mImpl = new OneHandedImpl();
+ private OneHandedEventCallback mEventCallback;
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
@@ -288,7 +291,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
mTimeoutObserver = getObserver(this::onTimeoutSettingChanged);
mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged);
mSwipeToNotificationEnabledObserver =
- getObserver(this::onSwipeToNotificationEnabledSettingChanged);
+ getObserver(this::onSwipeToNotificationEnabledChanged);
mDisplayController.addDisplayChangingController(mRotationController);
setupCallback();
@@ -358,14 +361,23 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
Slog.d(TAG, "Temporary lock disabled");
return;
}
+
+ if (!mDisplayAreaOrganizer.isReady()) {
+ // Must wait until DisplayAreaOrganizer is ready for transitioning.
+ mMainExecutor.executeDelayed(this::startOneHanded, DISPLAY_AREA_READY_RETRY_MS);
+ return;
+ }
+
if (mState.isTransitioning() || mState.isInOneHanded()) {
return;
}
+
final int currentRotation = mDisplayAreaOrganizer.getDisplayLayout().rotation();
if (currentRotation != Surface.ROTATION_0 && currentRotation != Surface.ROTATION_180) {
Slog.w(TAG, "One handed mode only support portrait mode");
return;
}
+
mState.setState(STATE_ENTERING);
final int yOffSet = Math.round(
mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
@@ -394,6 +406,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
mOneHandedUiEventLogger.writeEvent(uiEvent);
}
+ void registerEventCallback(OneHandedEventCallback callback) {
+ mEventCallback = callback;
+ }
+
@VisibleForTesting
void registerTransitionCallback(OneHandedTransitionCallback callback) {
mDisplayAreaOrganizer.registerTransitionCallback(callback);
@@ -464,8 +480,29 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
@VisibleForTesting
+ void notifyExpandNotification() {
+ mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification());
+ }
+
+ @VisibleForTesting
+ void notifyUserConfigChanged(boolean success) {
+ if (!success) {
+ return;
+ }
+ // TODO Check UX if popup Toast to notify user when auto-enabled one-handed is good option.
+ Toast.makeText(mContext, R.string.one_handed_tutorial_title, Toast.LENGTH_LONG).show();
+ }
+
+ @VisibleForTesting
void onActivatedActionChanged() {
- if (mState.isTransitioning() || !isOneHandedEnabled()) {
+ if (!isOneHandedEnabled()) {
+ final boolean success = mOneHandedSettingsUtil.setOneHandedModeEnabled(
+ mContext.getContentResolver(), 1 /* Enabled for shortcut */, mUserId);
+ notifyUserConfigChanged(success);
+ }
+
+ if (isSwipeToNotificationEnabled()) {
+ notifyExpandNotification();
return;
}
@@ -494,11 +531,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
setOneHandedEnabled(enabled);
// Also checks swipe to notification settings since they all need gesture overlay.
- // Enabled overlay package may affect the current animation(e.g:Settings switch),
- // so we delay 250ms to enabled overlay after switch animation finish
- mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(
+ setEnabledGesturalOverlay(
enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- mContext.getContentResolver(), mUserId)), OVERLAY_ENABLED_DELAY_MS);
+ mContext.getContentResolver(), mUserId), true /* DelayExecute */);
}
@VisibleForTesting
@@ -542,7 +577,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
@VisibleForTesting
- void onSwipeToNotificationEnabledSettingChanged() {
+ void onSwipeToNotificationEnabledChanged() {
final boolean enabled =
mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver(), mUserId);
@@ -551,7 +586,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
// Also checks one handed mode settings since they all need gesture overlay.
setEnabledGesturalOverlay(
enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- mContext.getContentResolver(), mUserId));
+ mContext.getContentResolver(), mUserId), true /* DelayExecute */);
}
private void setupTimeoutListener() {
@@ -569,11 +604,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
return mIsOneHandedEnabled;
}
+ @VisibleForTesting
+ boolean isSwipeToNotificationEnabled() {
+ return mIsSwipeToNotificationEnabled;
+ }
+
private void updateOneHandedEnabled() {
if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) {
mMainExecutor.execute(() -> stopOneHanded());
}
+ // Reset and align shortcut one_handed_mode_activated status with current mState
+ notifyShortcutState(mState.getState());
+
mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
if (!mIsOneHandedEnabled) {
@@ -608,12 +651,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
if (info != null && !info.isEnabled()) {
// Enable the default gestural one handed overlay.
- setEnabledGesturalOverlay(true);
+ setEnabledGesturalOverlay(true /* enabled */, false /* delayExecute */);
}
}
@VisibleForTesting
- private void setEnabledGesturalOverlay(boolean enabled) {
+ private void setEnabledGesturalOverlay(boolean enabled, boolean delayExecute) {
+ if (mState.isTransitioning() || delayExecute) {
+ // Enabled overlay package may affect the current animation(e.g:Settings switch),
+ // so we delay 250ms to enabled overlay after switch animation finish, only delay once.
+ mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(enabled, false),
+ OVERLAY_ENABLED_DELAY_MS);
+ return;
+ }
try {
mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT);
} catch (RemoteException e) {
@@ -628,6 +678,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
if (enabled == isFeatureEnabled) {
return;
}
+
mLockedDisabled = locked && !enabled;
}
@@ -761,6 +812,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
@Override
+ public void registerEventCallback(OneHandedEventCallback callback) {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.registerEventCallback(callback);
+ });
+ }
+
+ @Override
public void registerTransitionCallback(OneHandedTransitionCallback callback) {
mMainExecutor.execute(() -> {
OneHandedController.this.registerTransitionCallback(callback);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index b8da37fd0c25..d749c320bf94 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -61,11 +61,12 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
private DisplayLayout mDisplayLayout = new DisplayLayout();
- private float mLastVisualOffset = 0;
private final Rect mLastVisualDisplayBounds = new Rect();
private final Rect mDefaultDisplayBounds = new Rect();
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
+ private boolean mIsReady;
+ private float mLastVisualOffset = 0;
private int mEnterExitAnimationDurationMs;
private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap();
@@ -157,6 +158,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
}
+ mIsReady = true;
updateDisplayBounds();
return displayAreaInfos;
}
@@ -164,9 +166,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
@Override
public void unregisterOrganizer() {
super.unregisterOrganizer();
+ mIsReady = false;
resetWindowsOffset();
}
+ boolean isReady() {
+ return mIsReady;
+ }
+
/**
* Handler for display rotation changes by {@link DisplayLayout}
*
@@ -312,6 +319,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
pw.println(mDisplayAreaTokenMap);
pw.print(innerPrefix + "mDefaultDisplayBounds=");
pw.println(mDefaultDisplayBounds);
+ pw.print(innerPrefix + "mIsReady=");
+ pw.println(mIsReady);
pw.print(innerPrefix + "mLastVisualDisplayBounds=");
pw.println(mLastVisualDisplayBounds);
pw.print(innerPrefix + "mLastVisualOffset=");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java
new file mode 100644
index 000000000000..d07eea271eac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.onehanded;
+
+/**
+ * Additional callback interface for OneHanded events.
+ */
+public interface OneHandedEventCallback {
+ /**
+ * Called to notify expand notification shade.
+ */
+ default void notifyExpandNotification() {
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index 90fc823fb574..da53b359a304 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -105,6 +105,17 @@ public final class OneHandedSettingsUtil {
}
/**
+ * Sets one handed enable or disable flag from Settings provider.
+ *
+ * @return true if the value was set, false on database errors
+ */
+ public boolean setOneHandedModeEnabled(ContentResolver resolver, int enabled, int userId) {
+ return Settings.Secure.putIntForUser(resolver,
+ Settings.Secure.ONE_HANDED_MODE_ENABLED, enabled, userId);
+ }
+
+
+ /**
* Queries taps app to exit config from Settings provider.
*
* @return enable or disable taps app exit.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 841edef9172f..f0bd8a2846ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -235,15 +235,20 @@ public class PipResizeGestureHandler {
@VisibleForTesting
void onInputEvent(InputEvent ev) {
+ if (!mEnableDragCornerResize && !mEnablePinchResize) {
+ // No need to handle anything if neither form of resizing is enabled.
+ return;
+ }
+
// Don't allow resize when PiP is stashed.
if (mPipBoundsState.isStashed()) {
return;
}
if (ev instanceof MotionEvent) {
- if (mOngoingPinchToResize) {
+ if (mEnablePinchResize && mOngoingPinchToResize) {
onPinchResize((MotionEvent) ev);
- } else {
+ } else if (mEnableDragCornerResize) {
onDragCornerResize((MotionEvent) ev);
}
}
@@ -318,8 +323,8 @@ public class PipResizeGestureHandler {
case MotionEvent.ACTION_POINTER_DOWN:
if (mEnablePinchResize && ev.getPointerCount() == 2) {
onPinchResize(ev);
- mOngoingPinchToResize = true;
- return true;
+ mOngoingPinchToResize = mAllowGesture;
+ return mAllowGesture;
}
break;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 4d33cb0452dc..46db35a6e29f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -35,6 +35,7 @@ import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
+import android.os.RemoteCallback;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Slog;
@@ -42,6 +43,7 @@ import android.util.SparseArray;
import android.view.Choreographer;
import android.view.Display;
import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -121,6 +123,13 @@ public class StartingSurfaceDrawer {
private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
+ /**
+ * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is
+ * rendered and that have not yet been removed by their client.
+ */
+ private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts =
+ new SparseArray<>(1);
+
/** Obtain proper context for showing splash screen on the provided display. */
private Context getDisplayContext(Context context, int displayId) {
if (displayId == DEFAULT_DISPLAY) {
@@ -386,25 +395,58 @@ public class StartingSurfaceDrawer {
/**
* Called when the Task wants to copy the splash screen.
- * @param taskId
*/
public void copySplashScreenView(int taskId) {
final StartingWindowRecord preView = mStartingWindowRecords.get(taskId);
SplashScreenViewParcelable parcelable;
- if (preView != null && preView.mContentView != null
- && preView.mContentView.isCopyable()) {
- parcelable = new SplashScreenViewParcelable(preView.mContentView);
- preView.mContentView.onCopied();
+ SplashScreenView splashScreenView = preView != null ? preView.mContentView : null;
+ if (splashScreenView != null && splashScreenView.isCopyable()) {
+ parcelable = new SplashScreenViewParcelable(splashScreenView);
+ parcelable.setClientCallback(
+ new RemoteCallback((bundle) -> mSplashScreenExecutor.execute(
+ () -> onAppSplashScreenViewRemoved(taskId, false))));
+ splashScreenView.onCopied();
+ mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost());
} else {
parcelable = null;
}
if (DEBUG_SPLASH_SCREEN) {
Slog.v(TAG, "Copying splash screen window view for task: " + taskId
- + " parcelable? " + parcelable);
+ + " parcelable: " + parcelable);
}
ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
}
+ /**
+ * Called when the {@link SplashScreenView} is removed from the client Activity view's hierarchy
+ * or when the Activity is clean up.
+ *
+ * @param taskId The Task id on which the splash screen was attached
+ */
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ onAppSplashScreenViewRemoved(taskId, true /* fromServer */);
+ }
+
+ /**
+ * @param fromServer If true, this means the removal was notified by the server. This is only
+ * used for debugging purposes.
+ * @see #onAppSplashScreenViewRemoved(int)
+ */
+ private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) {
+ SurfaceControlViewHost viewHost =
+ mAnimatedSplashScreenSurfaceHosts.get(taskId);
+ if (viewHost == null) {
+ return;
+ }
+ mAnimatedSplashScreenSurfaceHosts.remove(taskId);
+ if (DEBUG_SPLASH_SCREEN) {
+ String reason = fromServer ? "Server cleaned up" : "App removed";
+ Slog.v(TAG, reason + "the splash screen. Releasing SurfaceControlViewHost for task:"
+ + taskId);
+ }
+ viewHost.getView().post(viewHost::release);
+ }
+
protected boolean addWindow(int taskId, IBinder appToken, View view, WindowManager wm,
WindowManager.LayoutParams params) {
boolean shouldSaveView = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index cffc789106cb..9c1dde925762 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -150,6 +150,14 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
}
/**
+ * @see StartingSurfaceDrawer#onAppSplashScreenViewRemoved(int)
+ */
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ mSplashScreenExecutor.execute(
+ () -> mStartingSurfaceDrawer.onAppSplashScreenViewRemoved(taskId));
+ }
+
+ /**
* Called when the content of a task is ready to show, starting window can be removed.
*/
public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 1852279ee96c..47789b7490ee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -74,6 +74,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
+ OneHandedEventCallback mMockEventCallback;
+ @Mock
OneHandedTouchHandler mMockTouchHandler;
@Mock
OneHandedTutorialHandler mMockTutorialHandler;
@@ -106,6 +108,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
+ when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
mDefaultEnabled);
@@ -241,7 +244,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Test
public void testSettingsObserverUpdateSwipeToNotification() {
- mSpiedOneHandedController.onSwipeToNotificationEnabledSettingChanged();
+ mSpiedOneHandedController.onSwipeToNotificationEnabledChanged();
verify(mSpiedOneHandedController).setSwipeToNotificationEnabled(anyBoolean());
}
@@ -311,6 +314,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout);
testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
mSpiedTransitionState.setState(STATE_NONE);
+ when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout);
mSpiedOneHandedController.setOneHandedEnabled(true);
mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
@@ -372,8 +376,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
- verify(mSpiedOneHandedController, never()).startOneHanded();
- verify(mSpiedOneHandedController, never()).stopOneHanded();
+ verify(mSpiedTransitionState, never()).setState(STATE_EXITING);
}
@Test
@@ -383,20 +386,20 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
- verify(mSpiedOneHandedController, never()).startOneHanded();
- verify(mSpiedOneHandedController, never()).stopOneHanded();
+ verify(mSpiedTransitionState, never()).setState(STATE_ENTERING);
}
@Test
- public void testOneHandedDisabled_shortcutEnabled_skipActions() {
+ public void testOneHandedDisabled_shortcutTrigger_thenAutoEnabled() {
when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
- when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false);
+ when(mMockSettingsUitl.setOneHandedModeEnabled(any(), anyInt(), anyInt())).thenReturn(
+ false);
mSpiedOneHandedController.onActivatedActionChanged();
- verify(mSpiedOneHandedController, never()).startOneHanded();
- verify(mSpiedOneHandedController, never()).stopOneHanded();
+ verify(mSpiedOneHandedController).notifyUserConfigChanged(anyBoolean());
}
@Test
@@ -408,4 +411,28 @@ public class OneHandedControllerTest extends OneHandedTestCase {
verify(mSpiedTransitionState).addSListeners(mMockTutorialHandler);
}
+
+ @Test
+ public void testNotifyEventCallbackWithMainExecutor() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(true);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true);
+ mSpiedOneHandedController.registerEventCallback(mMockEventCallback);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mMockShellMainExecutor).execute(any());
+ }
+
+ @Test
+ public void testNotifyShortcutState_whenUpdateOneHandedEnabled() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true);
+ mSpiedOneHandedController.registerEventCallback(mMockEventCallback);
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+
+ verify(mSpiedOneHandedController).notifyShortcutState(anyInt());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index a27ed114de70..ef16fd391235 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -418,4 +418,18 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset();
}
+
+ @Test
+ public void testDisplayArea_notReadyForTransition() {
+ OneHandedDisplayAreaOrganizer testSpiedDisplayAreaOrganizer = spy(
+ new OneHandedDisplayAreaOrganizer(mContext,
+ mDisplayLayout,
+ mMockSettingsUitl,
+ mMockAnimationController,
+ mTutorialHandler,
+ mMockBackgroundOrganizer,
+ mMockShellMainExecutor));
+
+ assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse();
+ }
}
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index 763835c9cbe2..d59756d02348 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -16,14 +16,10 @@
package android.location;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.util.identity.CallerIdentity;
-
-import com.android.internal.annotations.Immutable;
-
-import java.util.Set;
+import android.os.PackageTagsList;
/**
* Location manager local system service interface.
@@ -43,18 +39,14 @@ public abstract class LocationManagerInternal {
}
/**
- * Interface for getting callbacks when a location provider's location tags change.
- *
- * @see LocationTagInfo
+ * Interface for getting callbacks when an app id's location provider package tags change.
*/
- public interface OnProviderLocationTagsChangeListener {
+ public interface LocationPackageTagsListener {
/**
- * Called when the location tags for a provider change.
- *
- * @param providerLocationTagInfo The tag info for a provider.
+ * Called when the package tags for a location provider change for a uid.
*/
- void onLocationTagsChanged(@NonNull LocationTagInfo providerLocationTagInfo);
+ void onLocationPackageTagsChanged(int uid, @NonNull PackageTagsList packageTagsList);
}
/**
@@ -109,58 +101,9 @@ public abstract class LocationManagerInternal {
public abstract @Nullable LocationTime getGnssTimeMillis();
/**
- * Sets a listener for changes in the location providers' tags. Passing
+ * Sets a listener for changes in an app id's location provider package tags. Passing
* {@code null} clears the current listener.
- *
- * @param listener The listener.
*/
- public abstract void setOnProviderLocationTagsChangeListener(
- @Nullable OnProviderLocationTagsChangeListener listener);
-
- /**
- * This class represents the location permission tags used by the location provider
- * packages in a given UID. These tags are strictly used for accessing state guarded
- * by the location permission(s) by a location provider which are required for the
- * provider to fulfill its function as being a location provider.
- */
- @Immutable
- public static class LocationTagInfo {
- private final int mUid;
-
- @NonNull
- private final String mPackageName;
-
- @Nullable
- private final Set<String> mLocationTags;
-
- public LocationTagInfo(int uid, @NonNull String packageName,
- @Nullable Set<String> locationTags) {
- mUid = uid;
- mPackageName = packageName;
- mLocationTags = locationTags;
- }
-
- /**
- * @return The UID for which tags are related.
- */
- public int getUid() {
- return mUid;
- }
-
- /**
- * @return The package for which tags are related.
- */
- @NonNull
- public String getPackageName() {
- return mPackageName;
- }
-
- /**
- * @return The tags for the package used for location related accesses.
- */
- @Nullable
- public Set<String> getTags() {
- return mLocationTags;
- }
- }
+ public abstract void setLocationPackageTagsListener(
+ @Nullable LocationPackageTagsListener listener);
}
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index bb6dfa3a4ede..18915039b6a1 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index 2835d57474d9..eb2202586753 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index bb6dfa3a4ede..18915039b6a1 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index 2e1a78989b70..b3f923c4176f 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
index a5373944474c..0b3a519cd919 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
@@ -16,8 +16,10 @@
package com.android.settingslib.enterprise;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.DialogInterface;
import androidx.annotation.Nullable;
@@ -54,4 +56,13 @@ public interface ActionDisabledByAdminController {
* Updates the enforced admin
*/
void updateEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin, @UserIdInt int adminUserId);
+
+ /**
+ * Returns a listener for handling positive button clicks
+ */
+ @Nullable
+ default DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context,
+ @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+ return null;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
index da42e330b8b4..44cafb17e1d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
@@ -20,6 +20,11 @@ import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.ParentalControlsUtilsInternal;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
/**
* A factory that returns the relevant instance of {@link ActionDisabledByAdminController}.
@@ -30,10 +35,28 @@ public final class ActionDisabledByAdminControllerFactory {
* Returns the relevant instance of {@link ActionDisabledByAdminController}.
*/
public static ActionDisabledByAdminController createInstance(Context context,
- DeviceAdminStringProvider stringProvider) {
- return isFinancedDevice(context)
- ? new FinancedDeviceActionDisabledByAdminController(stringProvider)
- : new ManagedDeviceActionDisabledByAdminController(stringProvider);
+ String restriction, DeviceAdminStringProvider stringProvider) {
+ if (doesBiometricRequireParentalConsent(context, restriction)) {
+ return new BiometricActionDisabledByAdminController(stringProvider);
+ } else if (isFinancedDevice(context)) {
+ return new FinancedDeviceActionDisabledByAdminController(stringProvider);
+ } else {
+ return new ManagedDeviceActionDisabledByAdminController(stringProvider);
+ }
+ }
+
+ /**
+ * @return true if the restriction == UserManager.DISALLOW_BIOMETRIC and parental consent
+ * is required.
+ */
+ private static boolean doesBiometricRequireParentalConsent(Context context,
+ String restriction) {
+ if (!TextUtils.equals(UserManager.DISALLOW_BIOMETRIC, restriction)) {
+ return false;
+ }
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm,
+ BiometricAuthenticator.TYPE_ANY_BIOMETRIC, new UserHandle(UserHandle.myUserId()));
}
private static boolean isFinancedDevice(Context context) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
new file mode 100644
index 000000000000..814d5d23f458
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.enterprise;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.RestrictedLockUtils;
+
+public class BiometricActionDisabledByAdminController extends BaseActionDisabledByAdminController {
+
+ private static final String TAG = "BiometricActionDisabledByAdminController";
+
+ // These MUST not change, as they are the stable API between here and device admin specified
+ // by the component below.
+ private static final String ACTION_LEARN_MORE = "android.settings.LEARN_MORE";
+ private static final String EXTRA_FROM_BIOMETRIC_SETUP = "from_biometric_setup";
+
+ BiometricActionDisabledByAdminController(
+ DeviceAdminStringProvider stringProvider) {
+ super(stringProvider);
+ }
+
+ @Override
+ public void setupLearnMoreButton(Context context) {
+
+ }
+
+ @Override
+ public String getAdminSupportTitle(@Nullable String restriction) {
+ return mStringProvider.getDisabledBiometricsParentConsentTitle();
+ }
+
+ @Override
+ public CharSequence getAdminSupportContentString(Context context,
+ @Nullable CharSequence supportMessage) {
+ return mStringProvider.getDisabledBiometricsParentConsentContent();
+ }
+
+ @Override
+ public DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context,
+ @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+ return (dialog, which) -> {
+ Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component);
+ final Intent intent = new Intent(ACTION_LEARN_MORE)
+ .setComponent(enforcedAdmin.component)
+ .putExtra(EXTRA_FROM_BIOMETRIC_SETUP, true)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ };
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java
index c47d789a514d..b83837e6caf6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java
@@ -72,4 +72,14 @@ public interface DeviceAdminStringProvider {
* a financed device.
*/
String getDisabledByPolicyTitleForFinancedDevice();
+
+ /**
+ * Returns the dialog title for when biometrics require parental consent.
+ */
+ String getDisabledBiometricsParentConsentTitle();
+
+ /**
+ * Returns the dialog contents for when biometrics require parental consent.
+ */
+ String getDisabledBiometricsParentConsentContent();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
new file mode 100644
index 000000000000..766c2f5f9872
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.enterprise;
+
+import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN;
+import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID;
+import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DEVICE_ADMIN_STRING_PROVIDER;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BiometricActionDisabledByAdminControllerTest {
+
+ private final ActionDisabledByAdminControllerTestUtils mTestUtils =
+ new ActionDisabledByAdminControllerTestUtils();
+ private final BiometricActionDisabledByAdminController mController =
+ new BiometricActionDisabledByAdminController(DEFAULT_DEVICE_ADMIN_STRING_PROVIDER);
+
+ @Before
+ public void setUp() {
+ mController.initialize(mTestUtils.createLearnMoreButtonLauncher());
+ mController.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
+ }
+
+ @Test
+ public void buttonClicked() {
+ Context context = mock(Context.class);
+ ComponentName componentName = mock(ComponentName.class);
+ RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils.EnforcedAdmin(
+ componentName, new UserHandle(UserHandle.myUserId()));
+
+ DialogInterface.OnClickListener listener =
+ mController.getPositiveButtonListener(context, enforcedAdmin);
+ assertNotNull("Biometric Controller must supply a non-null listener", listener);
+ listener.onClick(mock(DialogInterface.class), 0 /* which */);
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(context).startActivity(intentCaptor.capture());
+ assertEquals("android.settings.LEARN_MORE",
+ intentCaptor.getValue().getAction());
+ assertTrue("from_biometric_setup", intentCaptor.getValue()
+ .getBooleanExtra("from_biometric_setup", false));
+ assertEquals(componentName, intentCaptor.getValue().getComponent());
+ }
+
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
index be3e9fcf45ea..99e13c325472 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
@@ -30,6 +30,8 @@ class FakeDeviceAdminStringProvider implements DeviceAdminStringProvider {
static final String DEFAULT_DISABLED_BY_POLICY_CONTENT = "default_disabled_by_policy_content";
static final String DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE =
"default_disabled_by_policy_title_financed_device";
+ static final String DEFAULT_BIOMETRIC_TITLE = "biometric_title";
+ static final String DEFAULT_BIOMETRIC_CONTENTS = "biometric_contents";
static final DeviceAdminStringProvider DEFAULT_DEVICE_ADMIN_STRING_PROVIDER =
new FakeDeviceAdminStringProvider(/* url = */ null);
@@ -88,4 +90,15 @@ class FakeDeviceAdminStringProvider implements DeviceAdminStringProvider {
public String getDisabledByPolicyTitleForFinancedDevice() {
return DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE;
}
+
+ @Override
+ public String getDisabledBiometricsParentConsentTitle() {
+ return DEFAULT_BIOMETRIC_TITLE;
+ }
+
+ @Override
+ public String getDisabledBiometricsParentConsentContent() {
+ return DEFAULT_BIOMETRIC_CONTENTS;
+ }
+
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 8bc3d228f7a8..2579e7084e08 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -238,7 +238,9 @@ class ActivityLaunchAnimator(
* during the animation.
*/
@JvmStatic
- fun fromView(view: View): Controller = GhostedViewLaunchAnimatorController(view)
+ fun fromView(view: View, cujType: Int? = null): Controller {
+ return GhostedViewLaunchAnimatorController(view, cujType)
+ }
}
/**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 4b655a1a1b02..ffb7ab4eff7c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -14,6 +14,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
import android.widget.FrameLayout
+import com.android.internal.jank.InteractionJankMonitor
import kotlin.math.min
/**
@@ -29,7 +30,10 @@ import kotlin.math.min
*/
open class GhostedViewLaunchAnimatorController(
/** The view that will be ghosted and from which the background will be extracted. */
- private val ghostedView: View
+ private val ghostedView: View,
+
+ /** The [InteractionJankMonitor.CujType] associated to this animation. */
+ private val cujType: Int? = null
) : ActivityLaunchAnimator.Controller {
/** The container to which we will add the ghost view and expanding background. */
override var launchContainer = ghostedView.rootView as ViewGroup
@@ -125,6 +129,8 @@ open class GhostedViewLaunchAnimatorController(
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
matrix.getValues(initialGhostViewMatrixValues)
+
+ cujType?.let { InteractionJankMonitor.getInstance().begin(ghostedView, it) }
}
override fun onLaunchAnimationProgress(
@@ -167,6 +173,8 @@ open class GhostedViewLaunchAnimatorController(
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ cujType?.let { InteractionJankMonitor.getInstance().end(it) }
+
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
GhostView.removeGhost(ghostedView)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 298b7c38066b..7c81325d685f 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -92,5 +92,12 @@ public interface ActivityStarter {
* *after* returning to start hiding the keyguard.
*/
boolean onDismiss();
+
+ /**
+ * Whether running this action when we are locked will start an animation on the keyguard.
+ */
+ default boolean willRunAnimationOnKeyguard() {
+ return false;
+ }
}
}
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/SystemUI/res/layout/global_actions_change_panel.xml
index dffb0f011bb5..bc9c203b299a 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/SystemUI/res/layout/global_actions_change_panel.xml
@@ -14,8 +14,18 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<ImageView
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/global_actions_change_message"
+ android:layout_width="wrap_content"
+ android:visibility="gone"
+ android:layout_height="wrap_content"
+ android:text="@string/global_actions_change_description" />
+ <ImageView
+ android:id="@+id/global_actions_change_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 4765b44d78c8..3f4baaf27b84 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -99,6 +99,7 @@
app:handleThickness="@dimen/screenshot_crop_handle_thickness"
app:handleColor="?androidprv:attr/colorAccentPrimary"
app:scrimColor="@color/screenshot_crop_scrim"
+ app:containerBackgroundColor="?android:colorBackgroundFloating"
tools:background="?android:colorBackground"
tools:minHeight="100dp"
tools:minWidth="100dp" />
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 70bd85036198..2f0957caaaae 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -33,6 +33,7 @@
<item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item>
<item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item>
<item>com.android.systemui.statusbar.tv.VpnStatusObserver</item>
+ <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 067d56f3d157..d2ed6017b205 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -143,6 +143,7 @@
<attr name="handleThickness" format="dimension" />
<attr name="handleColor" format="color" />
<attr name="scrimColor" format="color" />
+ <attr name="containerBackgroundColor" format="color" />
<attr name="isVertical" format="boolean" />
@@ -178,6 +179,7 @@
<attr name="handleThickness" />
<attr name="handleColor" />
<attr name="scrimColor" />
+ <attr name="containerBackgroundColor" />
</declare-styleable>
<declare-styleable name="MagnifierView">
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b4deaa0af543..82ce881b2fbc 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -396,10 +396,6 @@
<!-- Whether or not the notifications should always fade as they are dismissed. -->
<bool name="config_fadeNotificationsOnDismiss">false</bool>
- <!-- Whether or not the parent of the notification row itself is being translated when swiped or
- its children views. If true, then the contents are translated and vice versa. -->
- <bool name="config_translateNotificationContentsOnSwipe">true</bool>
-
<!-- Whether or not the fade on the notification is based on the amount that it has been swiped
off-screen. -->
<bool name="config_fadeDependingOnAmountSwiped">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0ccde60f9bfd..9b860c75f476 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -347,7 +347,7 @@
<!-- Padding to make tappable chip height 48dp (18+11+11+4+4) -->
<dimen name="screenshot_action_chip_margin_vertical">4dp</dimen>
<dimen name="screenshot_action_chip_padding_vertical">11dp</dimen>
- <dimen name="screenshot_action_chip_icon_size">18dp</dimen>
+ <dimen name="screenshot_action_chip_icon_size">18sp</dimen>
<!-- Padding on each side of the icon for icon-only chips -->
<dimen name="screenshot_action_chip_icon_only_padding_horizontal">14dp</dimen>
<!-- Padding at the edges of the chip for icon-and-text chips -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 91301dfda5e8..93ce8f3c2a96 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2979,6 +2979,8 @@
<!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
<string name="ongoing_phone_call_content_description">Ongoing phone call</string>
+ <!-- Placeholder for string describing changes in global actions -->
+ <string name="global_actions_change_description" translatable="false"><xliff:g>%1$s</xliff:g></string>
<!-- URL for more information about changes in global actions -->
<string name="global_actions_change_url" translatable="false"></string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index b0f4da251208..affad7a57d86 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -391,9 +391,9 @@ public class SwipeHelper implements Gefingerpoken {
boolean animateLeft = (Math.abs(velocity) > getEscapeVelocity() && velocity < 0) ||
(getTranslation(animView) < 0 && !isDismissAll);
if (animateLeft || animateLeftForRtl || animateUpForMenu) {
- newPos = -getSize(animView);
+ newPos = -getTotalTranslationLength(animView);
} else {
- newPos = getSize(animView);
+ newPos = getTotalTranslationLength(animView);
}
long duration;
if (fixedDuration == 0) {
@@ -470,6 +470,15 @@ public class SwipeHelper implements Gefingerpoken {
}
/**
+ * Get the total translation length where we want to swipe to when dismissing the view. By
+ * default this is the size of the view, but can also be larger.
+ * @param animView the view to ask about
+ */
+ protected float getTotalTranslationLength(View animView) {
+ return getSize(animView);
+ }
+
+ /**
* Called to update the dismiss animation.
*/
protected void prepareDismissAnimation(View view, Animator anim) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index 47f373920b90..05256e646948 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -53,7 +53,7 @@ public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
@FloatRange(from = 0.0, to = 1.0)
private static final float DEFAULT_POSITION_X_PERCENT = 1.0f;
@FloatRange(from = 0.0, to = 1.0)
- private static final float DEFAULT_POSITION_Y_PERCENT = 0.8f;
+ private static final float DEFAULT_POSITION_Y_PERCENT = 0.9f;
private final Context mContext;
private final AccessibilityFloatingMenuView mMenuView;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
index 63cfd5123c96..ee09c620ec1d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
@@ -143,7 +143,7 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
}
void updateItemPadding(int padding, int size) {
- itemView.setPaddingRelative(padding, padding, padding, padding);
+ itemView.setPaddingRelative(padding, padding, padding, 0);
}
}
@@ -154,7 +154,7 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
@Override
void updateItemPadding(int padding, int size) {
- final int paddingBottom = size <= 2 ? padding : 0;
+ final int paddingBottom = size <= 1 ? padding : 0;
itemView.setPaddingRelative(padding, padding, padding, paddingBottom);
}
}
@@ -166,7 +166,7 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
@Override
void updateItemPadding(int padding, int size) {
- itemView.setPaddingRelative(padding, 0, padding, padding);
+ itemView.setPaddingRelative(padding, padding, padding, padding);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index cf577a37d625..77cca2e3089c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -24,9 +24,13 @@ import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
+import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
@@ -49,10 +53,12 @@ class AuthRippleController @Inject constructor(
private val commandRegistry: CommandRegistry,
private val notificationShadeWindowController: NotificationShadeWindowController,
private val bypassController: KeyguardBypassController,
+ private val biometricUnlockController: BiometricUnlockController,
rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView) {
var fingerprintSensorLocation: PointF? = null
private var faceSensorLocation: PointF? = null
+ private var circleReveal: LightRevealEffect? = null
@VisibleForTesting
public override fun onViewAttached() {
@@ -96,15 +102,47 @@ class AuthRippleController @Inject constructor(
private fun showRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- mView.startRipple(Runnable {
- notificationShadeWindowController.setForcePluginOpen(false, this)
- })
+ val biometricUnlockMode = biometricUnlockController.mode
+ val useCircleReveal = circleReveal != null &&
+ (biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK ||
+ biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING ||
+ biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+ val lightRevealScrim = statusBar.lightRevealScrim
+ if (useCircleReveal) {
+ lightRevealScrim?.revealEffect = circleReveal!!
+ }
+
+ mView.startRipple(
+ /* end runnable */
+ Runnable {
+ notificationShadeWindowController.setForcePluginOpen(false, this)
+ if (useCircleReveal) {
+ lightRevealScrim?.revealEffect = LiftReveal
+ }
+ },
+ /* circleReveal */
+ if (useCircleReveal) {
+ lightRevealScrim
+ } else {
+ null
+ }
+ )
}
fun updateSensorLocation() {
fingerprintSensorLocation = authController.fingerprintSensorLocation
faceSensorLocation = authController.faceAuthSensorLocation
- statusBar.updateCircleReveal()
+ fingerprintSensorLocation?.let {
+ circleReveal = CircleReveal(
+ it.x,
+ it.y,
+ 0f,
+ Math.max(
+ Math.max(it.x, statusBar.displayWidth - it.x),
+ Math.max(it.y, statusBar.displayHeight - it.y)
+ )
+ )
+ }
}
private fun updateRippleColor() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 75373abc5124..dd73c4f8d071 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -31,6 +31,7 @@ import android.util.MathUtils
import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
+import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.charging.RippleShader
private const val RIPPLE_ANIMATION_DURATION: Long = 1533
@@ -70,51 +71,79 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
.toFloat()
}
- fun startRipple(onAnimationEnd: Runnable?) {
+ fun startRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) {
if (rippleInProgress) {
return // Ignore if ripple effect is already playing
}
- val animator = ValueAnimator.ofFloat(0f, 1f)
- animator.interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
- animator.duration = RIPPLE_ANIMATION_DURATION
- animator.addUpdateListener { animator ->
- val now = animator.currentPlayTime
- rippleShader.progress = animator.animatedValue as Float
- rippleShader.time = now.toFloat()
- rippleShader.distortionStrength = 1 - rippleShader.progress
- invalidate()
+ val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
+ duration = RIPPLE_ANIMATION_DURATION
+ addUpdateListener { animator ->
+ val now = animator.currentPlayTime
+ rippleShader.progress = animator.animatedValue as Float
+ rippleShader.time = now.toFloat()
+
+ lightReveal?.revealAmount = animator.animatedValue as Float
+ invalidate()
+ }
}
- val alphaInAnimator = ValueAnimator.ofInt(0, 127)
- alphaInAnimator.duration = 167
- alphaInAnimator.addUpdateListener { alphaInAnimator ->
- rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
- alphaInAnimator.animatedValue as Int)
- invalidate()
+
+ val revealAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ interpolator = rippleAnimator.interpolator
+ startDelay = 10
+ duration = rippleAnimator.duration
+ addUpdateListener { animator ->
+ lightReveal?.revealAmount = animator.animatedValue as Float
+ }
}
- val alphaOutAnimator = ValueAnimator.ofInt(127, 0)
- alphaOutAnimator.startDelay = 417
- alphaOutAnimator.duration = 1116
- alphaOutAnimator.addUpdateListener { alphaOutAnimator ->
- rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
- alphaOutAnimator.animatedValue as Int)
- invalidate()
+
+ val alphaInAnimator = ValueAnimator.ofInt(0, 127).apply {
+ duration = 167
+ addUpdateListener { animator ->
+ rippleShader.color = ColorUtils.setAlphaComponent(
+ rippleShader.color,
+ animator.animatedValue as Int
+ )
+ invalidate()
+ }
}
- val animatorSet = AnimatorSet()
- animatorSet.playTogether(animator, alphaInAnimator, alphaOutAnimator)
- animatorSet.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- onAnimationEnd?.run()
- rippleInProgress = false
- visibility = GONE
+ val alphaOutAnimator = ValueAnimator.ofInt(127, 0).apply {
+ startDelay = 417
+ duration = 1116
+ addUpdateListener { animator ->
+ rippleShader.color = ColorUtils.setAlphaComponent(
+ rippleShader.color,
+ animator.animatedValue as Int
+ )
+ invalidate()
}
- })
+ }
+
+ val animatorSet = AnimatorSet().apply {
+ playTogether(
+ rippleAnimator,
+ revealAnimator,
+ alphaInAnimator,
+ alphaOutAnimator
+ )
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator?) {
+ rippleInProgress = true
+ visibility = VISIBLE
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ onAnimationEnd?.run()
+ rippleInProgress = false
+ visibility = GONE
+ }
+ })
+ }
// TODO (b/185124905): custom haptic TBD
// vibrate()
animatorSet.start()
- visibility = VISIBLE
- rippleInProgress = true
}
fun setColor(color: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index dfd85fe4dc90..1fb36d7fbd3b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -72,6 +72,7 @@ import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -133,7 +134,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
- RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
+ RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler,
+ StatusBar statusBar) {
super(context, windowManagerFuncs,
audioManager, iDreamManager,
@@ -152,7 +154,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
backgroundExecutor,
uiEventLogger,
null,
- ringerModeTracker, sysUiState, handler);
+ ringerModeTracker, sysUiState, handler, statusBar);
mLockPatternUtils = lockPatternUtils;
mKeyguardStateController = keyguardStateController;
@@ -227,7 +229,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
this::getWalletViewController, mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger());
+ mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger(),
+ getStatusBar());
if (shouldShowLockMessage(dialog)) {
dialog.showLockMessage();
@@ -295,11 +298,13 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
+ MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
+ StatusBar statusBar) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
adapter, overflowAdapter, depthController, sysuiColorExtractor,
statusBarService, notificationShadeWindowController, sysuiState,
- onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger, null);
+ onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger, null,
+ statusBar);
mWalletFactory = walletFactory;
// Update window attributes
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 8e152830e208..ad920cbc0192 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -74,8 +74,10 @@ import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.Log;
import android.view.ContextThemeWrapper;
+import android.view.GestureDetector;
import android.view.IWindowManager;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -119,6 +121,7 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -228,6 +231,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
+ private final StatusBar mStatusBar;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -322,7 +326,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
GlobalActionsInfoProvider infoProvider,
- RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
+ RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler,
+ StatusBar statusBar) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -352,6 +357,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mSysUiState = sysUiState;
mMainHandler = handler;
mSmallestScreenWidthDp = mContext.getResources().getConfiguration().smallestScreenWidthDp;
+ mStatusBar = statusBar;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -392,6 +398,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
return mUiEventLogger;
}
+ protected StatusBar getStatusBar() {
+ return mStatusBar;
+ }
+
/**
* Show the global actions dialog (creating if necessary)
*
@@ -625,7 +635,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
- mInfoProvider);
+ mInfoProvider, mStatusBar);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -2100,9 +2110,53 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected final Runnable mOnRotateCallback;
private UiEventLogger mUiEventLogger;
private GlobalActionsInfoProvider mInfoProvider;
+ private GestureDetector mGestureDetector;
+ private StatusBar mStatusBar;
protected ViewGroup mContainer;
+ @VisibleForTesting
+ protected GestureDetector.SimpleOnGestureListener mGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // All gestures begin with this message, so continue listening
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ // Close without opening shade
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ cancel();
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ if (distanceY < 0 && distanceY > distanceX
+ && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+ // Downwards scroll from top
+ openShadeAndDismiss();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
+ && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+ // Downwards fling from top
+ openShadeAndDismiss();
+ return true;
+ }
+ return false;
+ }
+ };
+
ActionsDialogLite(Context context, int themeRes, MyAdapter adapter,
MyOverflowAdapter overflowAdapter,
NotificationShadeDepthController depthController,
@@ -2110,7 +2164,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- @Nullable GlobalActionsInfoProvider infoProvider) {
+ @Nullable GlobalActionsInfoProvider infoProvider, StatusBar statusBar) {
super(context, themeRes);
mContext = context;
mAdapter = adapter;
@@ -2125,6 +2179,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
mInfoProvider = infoProvider;
+ mStatusBar = statusBar;
+
+ mGestureDetector = new GestureDetector(mContext, mGestureListener);
// Window initialization
Window window = getWindow();
@@ -2146,6 +2203,23 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
initializeLayout();
}
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
+ }
+
+ private void openShadeAndDismiss() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ if (mStatusBar.isKeyguardShowing()) {
+ // match existing lockscreen behavior to open QS when swiping from status bar
+ mStatusBar.animateExpandSettingsPanel(null);
+ } else {
+ // otherwise, swiping down should expand notification shade
+ mStatusBar.animateExpandNotificationsPanel();
+ }
+ dismiss();
+ }
+
private ListPopupWindow createPowerOverflowPopup() {
GlobalActionsPopupMenu popup = new GlobalActionsPopupMenu(
new ContextThemeWrapper(
@@ -2194,9 +2268,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mGlobalActionsLayout.setRotationListener(this::onRotate);
mGlobalActionsLayout.setAdapter(mAdapter);
mContainer = findViewById(com.android.systemui.R.id.global_actions_container);
- mContainer.setOnClickListener(v -> {
- mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- cancel();
+ mContainer.setOnTouchListener((v, event) -> {
+ mGestureDetector.onTouchEvent(event);
+ return v.onTouchEvent(event);
});
View overflowButton = findViewById(
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
index 39008eecd6a2..17b532a643cd 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
@@ -25,6 +25,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
+import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.plugins.ActivityStarter
@@ -70,6 +71,11 @@ class GlobalActionsInfoProvider @Inject constructor(
val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel,
parent, false)
+
+ val walletTitle = walletClient.serviceLabel ?: context.getString(R.string.wallet_title)
+ val message = view.findViewById<TextView>(R.id.global_actions_change_message)
+ message?.setText(context.getString(R.string.global_actions_change_description, walletTitle))
+
val button = view.findViewById<ImageView>(R.id.global_actions_change_button)
button.setOnClickListener { _ ->
dismissParent.run()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index c2b580773424..19190cd4a17d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -46,6 +46,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.constraintlayout.widget.ConstraintSet;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -468,7 +469,8 @@ public class MediaControlPanel {
TransitionLayout player) {
// TODO(b/174236650): Make sure that the carousel indicator also fades out.
// TODO(b/174236650): Instrument the animation to measure jank.
- return new GhostedViewLaunchAnimatorController(player) {
+ return new GhostedViewLaunchAnimatorController(player,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER) {
@Override
protected float getCurrentTopCornerRadius() {
return ((IlluminationDrawable) player.getBackground()).getCornerRadius();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 5b1e039ad0f8..28d336ea18d5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -39,6 +39,7 @@ import android.media.session.MediaSession
import android.net.Uri
import android.os.Parcelable
import android.os.UserHandle
+import android.provider.Settings
import android.service.notification.StatusBarNotification
import android.text.TextUtils
import android.util.Log
@@ -54,6 +55,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
import com.android.systemui.statusbar.notification.row.HybridGroupManager
+import com.android.systemui.tuner.TunerService
import com.android.systemui.util.Assert
import com.android.systemui.util.Utils
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -114,7 +116,8 @@ class MediaDataManager(
private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
private var useMediaResumption: Boolean,
private val useQsMediaPlayer: Boolean,
- private val systemClock: SystemClock
+ private val systemClock: SystemClock,
+ private val tunerService: TunerService
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
companion object {
@@ -147,6 +150,7 @@ class MediaDataManager(
// There should ONLY be at most one Smartspace media recommendation.
private var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
private var smartspaceSession: SmartspaceSession? = null
+ private var allowMediaRecommendations = Utils.allowMediaRecommendations(context)
@Inject
constructor(
@@ -164,12 +168,13 @@ class MediaDataManager(
mediaDataFilter: MediaDataFilter,
activityStarter: ActivityStarter,
smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
- clock: SystemClock
+ clock: SystemClock,
+ tunerService: TunerService
) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter,
activityStarter, smartspaceMediaDataProvider, Utils.useMediaResumption(context),
- Utils.useQsMediaPlayer(context), clock)
+ Utils.useQsMediaPlayer(context), clock, tunerService)
private val appChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -243,6 +248,14 @@ class MediaDataManager(
})
}
smartspaceSession?.let { it.requestSmartspaceUpdate() }
+ tunerService.addTunable(object : TunerService.Tunable {
+ override fun onTuningChanged(key: String?, newValue: String?) {
+ allowMediaRecommendations = Utils.allowMediaRecommendations(context)
+ if (!allowMediaRecommendations) {
+ dismissSmartspaceRecommendation(key = smartspaceMediaData.targetId, delay = 0L)
+ }
+ }
+ }, Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION)
}
fun destroy() {
@@ -695,8 +708,7 @@ class MediaDataManager(
}
override fun onSmartspaceTargetsUpdated(targets: List<Parcelable>) {
- if (!Utils.allowMediaRecommendations(context)) {
- Log.d(TAG, "Smartspace recommendation is disabled in Settings.")
+ if (!allowMediaRecommendations) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 075bc700cfa0..edbf18789e28 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -187,6 +187,12 @@ class MediaHierarchyManager @Inject constructor(
private var currentAttachmentLocation = -1
/**
+ * Is there any active media in the carousel?
+ */
+ private var hasActiveMedia: Boolean = false
+ get() = mediaHosts.get(LOCATION_QQS)?.visible == true
+
+ /**
* Are we currently waiting on an animation to start?
*/
private var animationPending: Boolean = false
@@ -476,8 +482,12 @@ class MediaHierarchyManager @Inject constructor(
val viewHost = createUniqueObjectHost()
mediaObject.hostView = viewHost
mediaObject.addVisibilityChangeListener {
+ // If QQS changes visibility, we need to force an update to ensure the transition
+ // goes into the correct state
+ val stateUpdate = mediaObject.location == LOCATION_QQS
+
// Never animate because of a visibility change, only state changes should do that
- updateDesiredLocation(forceNoAnimation = true)
+ updateDesiredLocation(forceNoAnimation = true, forceStateUpdate = stateUpdate)
}
mediaHosts[mediaObject.location] = mediaObject
if (mediaObject.location == desiredLocation) {
@@ -521,10 +531,15 @@ class MediaHierarchyManager @Inject constructor(
* going from the old desired location to the new one.
*
* @param forceNoAnimation optional parameter telling the system not to animate
+ * @param forceStateUpdate optional parameter telling the system to update transition state
+ * even if location did not change
*/
- private fun updateDesiredLocation(forceNoAnimation: Boolean = false) {
+ private fun updateDesiredLocation(
+ forceNoAnimation: Boolean = false,
+ forceStateUpdate: Boolean = false
+ ) {
val desiredLocation = calculateLocation()
- if (desiredLocation != this.desiredLocation) {
+ if (desiredLocation != this.desiredLocation || forceStateUpdate) {
if (this.desiredLocation >= 0) {
previousLocation = this.desiredLocation
}
@@ -784,7 +799,7 @@ class MediaHierarchyManager @Inject constructor(
private fun getQSTransformationProgress(): Float {
val currentHost = getHost(desiredLocation)
val previousHost = getHost(previousLocation)
- if (currentHost?.location == LOCATION_QS) {
+ if (hasActiveMedia && currentHost?.location == LOCATION_QS) {
if (previousHost?.location == LOCATION_QQS) {
if (previousHost.visible || statusbarState != StatusBarState.KEYGUARD) {
return qsExpansion
@@ -917,6 +932,7 @@ class MediaHierarchyManager @Inject constructor(
val location = when {
qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
+ !hasActiveMedia -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 0d9749e05262..ff5d0b157c80 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -251,8 +251,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private float mMLResults;
// For debugging
- private ArrayDeque<String> mPredictionLog = new ArrayDeque<>();
- private ArrayDeque<String> mGestureLog = new ArrayDeque<>();
+ private LogArray mPredictionLog = new LogArray(MAX_NUM_LOGGED_PREDICTIONS);
+ private LogArray mGestureLogInsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES);
+ private LogArray mGestureLogOutsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES);
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
@@ -631,7 +632,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
return mMLResults >= mMLModelThreshold ? 1 : 0;
}
- private boolean isWithinTouchRegion(int x, int y) {
+ private boolean isWithinInsets(int x, int y) {
// Disallow if we are in the bottom gesture area
if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
return false;
@@ -644,7 +645,10 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
&& x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) {
return false;
}
+ return true;
+ }
+ private boolean isWithinTouchRegion(int x, int y) {
// If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back
// gesture
final boolean isInsidePip = mIsInPipMode && mPipExcludedBounds.contains(x, y);
@@ -675,14 +679,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
}
// For debugging purposes
- if (mPredictionLog.size() >= MAX_NUM_LOGGED_PREDICTIONS) {
- mPredictionLog.removeFirst();
- }
- mPredictionLog.addLast(String.format("Prediction [%d,%d,%d,%d,%f,%d]",
+ mPredictionLog.log(String.format("Prediction [%d,%d,%d,%d,%f,%d]",
System.currentTimeMillis(), x, y, app, mMLResults, withinRange ? 1 : 0));
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, mPredictionLog.peekLast());
- }
// Always allow if the user is in a transient sticky immersive state
if (mIsNavBarShownTransiently) {
@@ -755,7 +753,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mMLResults = 0;
mLogGesture = false;
mInRejectedExclusion = false;
- mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed
+ boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());
+ mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed && isWithinInsets
&& !mGestureBlockingActivityRunning
&& !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
@@ -769,18 +768,13 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mThresholdCrossed = false;
}
- // For debugging purposes
- if (mGestureLog.size() >= MAX_NUM_LOGGED_GESTURES) {
- mGestureLog.removeFirst();
- }
- mGestureLog.addLast(String.format(
+ // For debugging purposes, only log edge points
+ (isWithinInsets ? mGestureLogInsideInsets : mGestureLogOutsideInsets).log(String.format(
"Gesture [%d,alw=%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]",
- System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, mIsBackGestureAllowed,
+ System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge,
+ mIsBackGestureAllowed,
QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize,
mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion));
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, mGestureLog.peekLast());
- }
} else if (mAllowGesture || mLogGesture) {
if (!mThresholdCrossed) {
mEndPoint.x = (int) ev.getX();
@@ -907,7 +901,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
pw.println(" mUseMLModel=" + mUseMLModel);
pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep);
pw.println(" mStartingQuickstepRotation=" + mStartingQuickstepRotation);
- pw.println(" mInRejectedExclusion" + mInRejectedExclusion);
+ pw.println(" mInRejectedExclusion=" + mInRejectedExclusion);
pw.println(" mExcludeRegion=" + mExcludeRegion);
pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
pw.println(" mIsInPipMode=" + mIsInPipMode);
@@ -922,7 +916,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
pw.println(" mTouchSlop=" + mTouchSlop);
pw.println(" mBottomGestureHeight=" + mBottomGestureHeight);
pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog));
- pw.println(" mGestureLog=" + String.join("\n", mGestureLog));
+ pw.println(" mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets));
+ pw.println(" mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets));
pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin);
}
@@ -945,4 +940,23 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
}
proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
}
+
+
+ private static class LogArray extends ArrayDeque<String> {
+ private final int mLength;
+
+ LogArray(int length) {
+ mLength = length;
+ }
+
+ void log(String message) {
+ if (size() >= mLength) {
+ removeFirst();
+ }
+ addLast(message);
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, message);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index f6d93895ce05..a981b6283764 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -28,6 +28,7 @@ import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -272,7 +273,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
private void startSettingsActivity() {
ActivityLaunchAnimator.Controller animationController =
mSettingsButtonContainer != null ? ActivityLaunchAnimator.Controller.fromView(
- mSettingsButtonContainer) : null;
+ mSettingsButtonContainer,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON) : null;
mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
true /* dismissShade */, animationController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index e1a66b2c07ee..7b8a6a0a8d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -539,8 +539,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) {
View hostView = mediaHost.getHostView();
- // on keyguard we cross-fade to expanded, so no need to pin it.
- if (mLastQSExpansion > 0 && !isKeyguardState()) {
+ // On keyguard we cross-fade to expanded, so no need to pin it.
+ // If the collapsed qs isn't visible, we also just keep it at the laid out position.
+ if (mLastQSExpansion > 0 && !isKeyguardState() && mQqsMediaHost.getVisible()) {
float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView)
- hostView.getHeight();
float currentPosition = mediaHost.getCurrentBounds().top
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 7cc6ecd8cf62..997b96626747 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -399,12 +399,14 @@ public class QuickStatusBarHeader extends FrameLayout {
mClockIconsSeparatorLayoutParams.width = 0;
setSeparatorVisibility(false);
mShowClockIconsSeparator = false;
+ mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
} else {
datePrivacySeparatorLayoutParams.width = topCutout.width();
mDatePrivacySeparator.setVisibility(View.VISIBLE);
mClockIconsSeparatorLayoutParams.width = topCutout.width();
mShowClockIconsSeparator = true;
setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
+ mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ON);
}
}
mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index a938821a343f..894ab528032d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -50,6 +50,7 @@ import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
@@ -389,7 +390,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
*/
protected void handleLongClick(@Nullable View view) {
ActivityLaunchAnimator.Controller animationController =
- view != null ? ActivityLaunchAnimator.Controller.fromView(view) : null;
+ view != null ? ActivityLaunchAnimator.Controller.fromView(view,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) : null;
mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
animationController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 69d49d44f822..73d13700d61b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -11,6 +11,7 @@ import android.text.TextUtils
import android.text.format.DateFormat
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -70,7 +71,10 @@ class AlarmTile @Inject constructor(
}
override fun handleClick(view: View?) {
- val animationController = view?.let { ActivityLaunchAnimator.Controller.fromView(it) }
+ val animationController = view?.let {
+ ActivityLaunchAnimator.Controller.fromView(
+ it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
+ }
val pendingIntent = lastAlarmInfo?.showIntent
if (pendingIntent != null) {
mActivityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 6d3190ffa725..f66b7226fbae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -22,6 +22,7 @@ import android.os.Handler
import android.os.Looper
import android.service.quicksettings.Tile
import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -106,7 +107,8 @@ class DeviceControlsTile @Inject constructor(
putExtra(ControlsUiController.EXTRA_ANIMATE, true)
}
val animationController = view?.let {
- ActivityLaunchAnimator.Controller.fromView(it)
+ ActivityLaunchAnimator.Controller.fromView(
+ it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
}
mUiHandler.post {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 0e4434baa0e8..98cd88af232f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -37,6 +37,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -120,7 +121,8 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
@Override
protected void handleClick(@Nullable View view) {
ActivityLaunchAnimator.Controller animationController =
- view == null ? null : ActivityLaunchAnimator.Controller.fromView(view);
+ view == null ? null : ActivityLaunchAnimator.Controller.fromView(view,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
mUiHandler.post(() -> {
if (mSelectedCard != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 9e11451afa06..0a60f6da159e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -62,6 +62,7 @@ public class CropView extends View {
private final float mCropTouchMargin;
private final Paint mShadePaint;
private final Paint mHandlePaint;
+ private final Paint mContainerBackgroundPaint;
// Crop rect with each element represented as [0,1] along its proper axis.
private RectF mCrop = new RectF(0, 0, 1, 1);
@@ -79,6 +80,9 @@ public class CropView extends View {
// The allowable values for the current boundary being dragged
private Range<Float> mMotionRange;
+ // Value [0,1] indicating progress in animateEntrance()
+ private float mEntranceInterpolation = 1f;
+
private CropInteractionListener mCropInteractionListener;
private final ExploreByTouchHelper mExploreByTouchHelper;
@@ -92,6 +96,9 @@ public class CropView extends View {
attrs, R.styleable.CropView, 0, 0);
mShadePaint = new Paint();
mShadePaint.setColor(t.getColor(R.styleable.CropView_scrimColor, Color.TRANSPARENT));
+ mContainerBackgroundPaint = new Paint();
+ mContainerBackgroundPaint.setColor(t.getColor(R.styleable.CropView_containerBackgroundColor,
+ Color.TRANSPARENT));
mHandlePaint = new Paint();
mHandlePaint.setColor(t.getColor(R.styleable.CropView_handleColor, Color.BLACK));
mHandlePaint.setStrokeCap(Paint.Cap.ROUND);
@@ -125,10 +132,22 @@ public class CropView extends View {
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- drawShade(canvas, 0, 0, 1, mCrop.top);
- drawShade(canvas, 0, mCrop.bottom, 1, 1);
+ // Top and bottom borders reflect the boundary between the (scrimmed) image and the
+ // opaque container background. This is only meaningful during an entrance transition.
+ float topBorder = MathUtils.lerp(mCrop.top, 0, mEntranceInterpolation);
+ float bottomBorder = MathUtils.lerp(mCrop.bottom, 1, mEntranceInterpolation);
+ drawShade(canvas, 0, topBorder, 1, mCrop.top);
+ drawShade(canvas, 0, mCrop.bottom, 1, bottomBorder);
drawShade(canvas, 0, mCrop.top, mCrop.left, mCrop.bottom);
drawShade(canvas, mCrop.right, mCrop.top, 1, mCrop.bottom);
+
+ // Entrance transition expects the crop bounds to be full width, so we only draw container
+ // background on the top and bottom.
+ drawContainerBackground(canvas, 0, 0, 1, topBorder);
+ drawContainerBackground(canvas, 0, bottomBorder, 1, 1);
+
+ mHandlePaint.setAlpha((int) (mEntranceInterpolation * 255));
+
drawHorizontalHandle(canvas, mCrop.top, /* draw the handle tab up */ true);
drawHorizontalHandle(canvas, mCrop.bottom, /* draw the handle tab down */ false);
drawVerticalHandle(canvas, mCrop.left, /* left */ true);
@@ -282,6 +301,22 @@ public class CropView extends View {
}
/**
+ * Fade in crop bounds, animate reveal of cropped-out area from current crop bounds.
+ */
+ public void animateEntrance() {
+ mEntranceInterpolation = 0;
+ ValueAnimator animator = new ValueAnimator();
+ animator.addUpdateListener(animation -> {
+ mEntranceInterpolation = animation.getAnimatedFraction();
+ invalidate();
+ });
+ animator.setFloatValues(0f, 1f);
+ animator.setDuration(750);
+ animator.setInterpolator(new FastOutSlowInInterpolator());
+ animator.start();
+ }
+
+ /**
* Set additional top and bottom padding for the image being cropped (used when the
* corresponding ImageView doesn't take the full height).
*/
@@ -369,6 +404,13 @@ public class CropView extends View {
fractionToVerticalPixels(bottom), mShadePaint);
}
+ private void drawContainerBackground(Canvas canvas, float left, float top, float right,
+ float bottom) {
+ canvas.drawRect(fractionToHorizontalPixels(left), fractionToVerticalPixels(top),
+ fractionToHorizontalPixels(right),
+ fractionToVerticalPixels(bottom), mContainerBackgroundPaint);
+ }
+
private void drawHorizontalHandle(Canvas canvas, float frac, boolean handleTabUp) {
int y = fractionToVerticalPixels(frac);
canvas.drawLine(fractionToHorizontalPixels(mCrop.left), y,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index d5b4032b1c0f..25ec1d74008e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -192,7 +192,6 @@ public class LongScreenshotActivity extends Activity {
mLongScreenshot = longScreenshot;
Drawable drawable = mLongScreenshot.getDrawable();
mPreview.setImageDrawable(drawable);
- mCropView.setVisibility(View.VISIBLE);
mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
mLongScreenshot.getWidth(), mLongScreenshot.getHeight());
// Original boundaries go from the image tile set's y=0 to y=pageSize, so
@@ -219,10 +218,12 @@ public class LongScreenshotActivity extends Activity {
public void onTransitionEnd(Transition transition) {
super.onTransitionEnd(transition);
mPreview.animate().alpha(1f);
- mCropView.animateBoundaryTo(
+ mCropView.setBoundaryPosition(
CropView.CropBoundary.TOP, topFraction);
- mCropView.animateBoundaryTo(
+ mCropView.setBoundaryPosition(
CropView.CropBoundary.BOTTOM, bottomFraction);
+ mCropView.animateEntrance();
+ mCropView.setVisibility(View.VISIBLE);
setButtonsEnabled(true);
mEnterTransitionView.setVisibility(View.GONE);
}
@@ -250,6 +251,7 @@ public class LongScreenshotActivity extends Activity {
Log.d(TAG, "onCachedImageLoaded(imageResult=" + imageResult + ")");
BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap);
mPreview.setImageDrawable(drawable);
+ mPreview.setAlpha(1f);
mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(),
imageResult.bitmap.getHeight());
mCropView.setVisibility(View.VISIBLE);
@@ -476,19 +478,21 @@ public class LongScreenshotActivity extends Activity {
params.height = boundaries.height();
mTransitionView.setLayoutParams(params);
- ConstraintLayout.LayoutParams enterTransitionParams =
- (ConstraintLayout.LayoutParams) mEnterTransitionView.getLayoutParams();
- float topFraction = Math.max(0,
- -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight());
- enterTransitionParams.width = (int) (scale * drawable.getIntrinsicWidth());
- enterTransitionParams.height = (int) (scale * mLongScreenshot.getPageHeight());
- mEnterTransitionView.setLayoutParams(enterTransitionParams);
-
- Matrix matrix = new Matrix();
- matrix.setScale(scale, scale);
- matrix.postTranslate(0, -scale * drawable.getIntrinsicHeight() * topFraction);
- mEnterTransitionView.setImageMatrix(matrix);
- mEnterTransitionView.setTranslationY(
- topFraction * previewHeight + mPreview.getPaddingTop() + extraPadding);
+ if (mLongScreenshot != null) {
+ ConstraintLayout.LayoutParams enterTransitionParams =
+ (ConstraintLayout.LayoutParams) mEnterTransitionView.getLayoutParams();
+ float topFraction = Math.max(0,
+ -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight());
+ enterTransitionParams.width = (int) (scale * drawable.getIntrinsicWidth());
+ enterTransitionParams.height = (int) (scale * mLongScreenshot.getPageHeight());
+ mEnterTransitionView.setLayoutParams(enterTransitionParams);
+
+ Matrix matrix = new Matrix();
+ matrix.setScale(scale, scale);
+ matrix.postTranslate(0, -scale * drawable.getIntrinsicHeight() * topFraction);
+ mEnterTransitionView.setImageMatrix(matrix);
+ mEnterTransitionView.setTranslationY(
+ topFraction * previewHeight + mPreview.getPaddingTop() + extraPadding);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 6a5f001ac2ee..ec648ad519a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -93,10 +93,10 @@ class CircleReveal(
val endRadius: Float
) : LightRevealEffect {
override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
- val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(amount)
- val fadeAmount =
- LightRevealEffect.getPercentPastThreshold(interpolatedAmount, 0.75f)
- val radius = startRadius + ((endRadius - startRadius) * interpolatedAmount)
+ // reveal amount updates already have an interpolator, so we intentionally use the
+ // non-interpolated amount
+ val fadeAmount = LightRevealEffect.getPercentPastThreshold(amount, 0.5f)
+ val radius = startRadius + ((endRadius - startRadius) * amount)
scrim.revealGradientEndColorAlpha = 1f - fadeAmount
scrim.setRevealGradientBounds(
centerX - radius /* left */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 9f59023f1890..f8a1ff879e72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -145,11 +145,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
}
@Override
- public boolean setState(int state) {
+ public boolean setState(int state, boolean force) {
if (state > MAX_STATE || state < MIN_STATE) {
throw new IllegalArgumentException("Invalid state " + state);
}
- if (state == mState) {
+ if (!force && state == mState) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index b6d6ed53b681..73f3d90bd4f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -59,7 +59,19 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
* @param state see {@link StatusBarState} for valid options
* @return {@code true} if the state changed, else {@code false}
*/
- boolean setState(int state);
+ default boolean setState(int state) {
+ return setState(state, false /* force */);
+ }
+
+ /**
+ * Update the status bar state
+ * @param state see {@link StatusBarState} for valid options
+ * @param force whether to set the state even if it's the same as the current state. This will
+ * dispatch the state to all StatusBarStateListeners, ensuring that all listening
+ * components are reset to this state.
+ * @return {@code true} if the state was changed or set forcefully
+ */
+ boolean setState(int state, boolean force);
/**
* Update the dozing state from {@link StatusBar}'s perspective
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 8479b30c3a75..f30010cf4d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -383,10 +383,18 @@ const val ANIMATING_OUT = 3
const val SHOWING_PERSISTENT_DOT = 4
private const val TAG = "SystemStatusAnimationScheduler"
-private const val DELAY: Long = 100
-private const val DISPLAY_LENGTH = 5000L
-private const val ENTRANCE_ANIM_LENGTH = 500L
-private const val CHIP_ANIM_LENGTH = 500L
+private const val DELAY = 0L
+
+/**
+ * The total time spent animation should be 1500ms. The entrance animation is how much time
+ * we give to the system to animate system elements out of the way. Total chip animation length
+ * will be equivalent to 2*chip_anim_length + display_length
+ */
+private const val ENTRANCE_ANIM_LENGTH = 250L
+private const val CHIP_ANIM_LENGTH = 250L
+// 1s + entrance time + chip anim_length
+private const val DISPLAY_LENGTH = 1500L
+
private const val MIN_UPTIME: Long = 5 * 1000
private const val DEBUG = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index aef01e9bd811..0fb1c54bb150 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -71,11 +71,11 @@ public final class NotificationClicker implements View.OnClickListener {
// Check if the notification is displaying the menu, if so slide notification back
if (isMenuVisible(row)) {
mLogger.logMenuVisible(entry);
- row.animateTranslateNotification(0);
+ row.animateResetTranslation();
return;
} else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
mLogger.logParentMenuVisible(entry);
- row.getNotificationParent().animateTranslateNotification(0);
+ row.getNotificationParent().animateResetTranslation();
return;
} else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
// We never want to open the app directly if the user clicks in between
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 413662447028..c24c2be3faa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -330,30 +330,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
}
- @Override
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- super.setDistanceToTopRoundness(distanceToTopRoundness);
- mBackgroundNormal.setDistanceToTopRoundness(distanceToTopRoundness);
- }
-
- /** Sets whether this view is the last notification in a section. */
- @Override
- public void setLastInSection(boolean lastInSection) {
- if (lastInSection != mLastInSection) {
- super.setLastInSection(lastInSection);
- mBackgroundNormal.setLastInSection(lastInSection);
- }
- }
-
- /** Sets whether this view is the first notification in a section. */
- @Override
- public void setFirstInSection(boolean firstInSection) {
- if (firstInSection != mFirstInSection) {
- super.setFirstInSection(firstInSection);
- mBackgroundNormal.setFirstInSection(firstInSection);
- }
- }
-
/**
* Set an override tint color that is used for the background.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 6fd556763943..ba28dc59def4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -846,8 +846,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
updateClickAndFocus();
if (mNotificationParent != null) {
setOverrideTintColor(NO_COLOR, 0.0f);
- // Let's reset the distance to top roundness, as this isn't applied to group children
- setDistanceToTopRoundness(NO_ROUNDNESS);
mNotificationParent.updateBackgroundForGroupState();
}
updateBackgroundClipping();
@@ -876,7 +874,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected boolean handleSlideBack() {
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
- animateTranslateNotification(0 /* targetLeft */);
+ animateResetTranslation();
return true;
}
return false;
@@ -1713,21 +1711,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
mChildrenContainer.onNotificationUpdated();
- if (mShouldTranslateContents) {
- mTranslateableViews.add(mChildrenContainer);
- }
+ mTranslateableViews.add(mChildrenContainer);
});
- if (mShouldTranslateContents) {
- // Add the views that we translate to reveal the menu
- mTranslateableViews = new ArrayList<>();
- for (int i = 0; i < getChildCount(); i++) {
- mTranslateableViews.add(getChildAt(i));
- }
- // Remove views that don't translate
- mTranslateableViews.remove(mChildrenContainerStub);
- mTranslateableViews.remove(mGutsStub);
+ // Add the views that we translate to reveal the menu
+ mTranslateableViews = new ArrayList<>();
+ for (int i = 0; i < getChildCount(); i++) {
+ mTranslateableViews.add(getChildAt(i));
}
+ // Remove views that don't translate
+ mTranslateableViews.remove(mChildrenContainerStub);
+ mTranslateableViews.remove(mGutsStub);
}
private void doLongClickCallback() {
@@ -1805,7 +1799,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mTranslateAnim.cancel();
}
- if (!mShouldTranslateContents) {
+ if (mDismissUsingRowTranslationX) {
setTranslationX(0);
} else if (mTranslateableViews != null) {
for (int i = 0; i < mTranslateableViews.size(); i++) {
@@ -1867,23 +1861,47 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mPrivateLayout.getActiveRemoteInputText();
}
- public void animateTranslateNotification(final float leftTarget) {
+ /**
+ * Reset the translation with an animation.
+ */
+ public void animateResetTranslation() {
if (mTranslateAnim != null) {
mTranslateAnim.cancel();
}
- mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
+ mTranslateAnim = getTranslateViewAnimator(0, null /* updateListener */);
if (mTranslateAnim != null) {
mTranslateAnim.start();
}
}
+ /**
+ * Set the dismiss behavior of the view.
+ * @param usingRowTranslationX {@code true} if the view should translate using regular
+ * translationX, otherwise the contents will be
+ * translated.
+ */
+ @Override
+ public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) {
+ if (usingRowTranslationX != mDismissUsingRowTranslationX) {
+ // In case we were already transitioning, let's switch over!
+ float previousTranslation = getTranslation();
+ if (previousTranslation != 0) {
+ setTranslation(0);
+ }
+ super.setDismissUsingRowTranslationX(usingRowTranslationX);
+ if (previousTranslation != 0) {
+ setTranslation(previousTranslation);
+ }
+ }
+ }
+
@Override
public void setTranslation(float translationX) {
invalidate();
if (isBlockingHelperShowingAndTranslationFinished()) {
mGuts.setTranslationX(translationX);
return;
- } else if (!mShouldTranslateContents) {
+ } else if (mDismissUsingRowTranslationX) {
setTranslationX(translationX);
} else if (mTranslateableViews != null) {
// Translate the group of views
@@ -1907,7 +1925,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public float getTranslation() {
- if (!mShouldTranslateContents) {
+ if (mDismissUsingRowTranslationX) {
return getTranslationX();
}
@@ -2898,7 +2916,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
float y = event.getY();
NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper();
NotificationHeaderView header = wrapper == null ? null : wrapper.getNotificationHeader();
- if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
+ // the extra translation only needs to be added, if we're translating the notification
+ // contents, otherwise the motionEvent is already at the right place due to the
+ // touch event system.
+ float translation = !mDismissUsingRowTranslationX ? getTranslation() : 0;
+ if (header != null && header.isInTouchRect(x - translation, y)) {
return true;
}
if ((!mIsSummaryWithChildren || shouldShowPublic())
@@ -3037,24 +3059,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
- public boolean topAmountNeedsClipping() {
- if (isGroupExpanded()) {
- return true;
- }
- if (isGroupExpansionChanging()) {
- return true;
- }
- if (getShowingLayout().shouldClipToRounding(true /* topRounded */,
- false /* bottomRounded */)) {
- return true;
- }
- if (mGuts != null && mGuts.getAlpha() != 0.0f) {
- return true;
- }
- return false;
- }
-
- @Override
protected boolean childNeedsClipping(View child) {
if (child instanceof NotificationContentView) {
NotificationContentView contentView = (NotificationContentView) child;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 5134c62dc182..d58fe3b3c4a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -71,21 +71,19 @@ public abstract class ExpandableOutlineView extends ExpandableView {
private int mBackgroundTop;
/**
- * {@code true} if the children views of the {@link ExpandableOutlineView} are translated when
+ * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
* it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
*/
- protected boolean mShouldTranslateContents;
- private boolean mTopAmountRounded;
- private float mDistanceToTopRoundness = -1;
+ protected boolean mDismissUsingRowTranslationX = true;
private float[] mTmpCornerRadii = new float[8];
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
if (!mCustomOutline && getCurrentTopRoundness() == 0.0f
- && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners
- && !mTopAmountRounded) {
- int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
+ && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners) {
+ // Only when translating just the contents, does the outline need to be shifted.
+ int translation = !mDismissUsingRowTranslationX ? (int) getTranslation() : 0;
int left = Math.max(translation, 0);
int top = mClipTopAmount + mBackgroundTop;
int right = getWidth() + Math.min(translation, 0);
@@ -110,7 +108,9 @@ public abstract class ExpandableOutlineView extends ExpandableView {
float topRoundness = mAlwaysRoundBothCorners
? mOutlineRadius : getCurrentBackgroundRadiusTop();
if (!mCustomOutline) {
- int translation = mShouldTranslateContents && !ignoreTranslation
+ // The outline just needs to be shifted if we're translating the contents. Otherwise
+ // it's already in the right place.
+ int translation = !mDismissUsingRowTranslationX && !ignoreTranslation
? (int) getTranslation() : 0;
int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
left = Math.max(translation, 0) - halfExtraWidth;
@@ -168,33 +168,15 @@ public abstract class ExpandableOutlineView extends ExpandableView {
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
canvas.save();
- Path intersectPath = null;
- if (mTopAmountRounded && topAmountNeedsClipping()) {
- int left = (int) (- mExtraWidthForClipping / 2.0f);
- int top = (int) (mClipTopAmount - mDistanceToTopRoundness);
- int right = getWidth() + (int) (mExtraWidthForClipping + left);
- int bottom = (int) Math.max(mMinimumHeightForClipping,
- Math.max(getActualHeight() - mClipBottomAmount, top + mOutlineRadius));
- getRoundedRectPath(left, top, right, bottom, mOutlineRadius, 0.0f, mClipPath);
- intersectPath = mClipPath;
- }
- boolean clipped = false;
if (childNeedsClipping(child)) {
Path clipPath = getCustomClipPath(child);
if (clipPath == null) {
clipPath = getClipPath(false /* ignoreTranslation */);
}
if (clipPath != null) {
- if (intersectPath != null) {
- clipPath.op(intersectPath, Path.Op.INTERSECT);
- }
canvas.clipPath(clipPath);
- clipped = true;
}
}
- if (!clipped && intersectPath != null) {
- canvas.clipPath(intersectPath);
- }
boolean result = super.drawChild(canvas, child, drawingTime);
canvas.restore();
return result;
@@ -212,32 +194,19 @@ public abstract class ExpandableOutlineView extends ExpandableView {
invalidate();
}
- @Override
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- super.setDistanceToTopRoundness(distanceToTopRoundness);
- if (distanceToTopRoundness != mDistanceToTopRoundness) {
- mTopAmountRounded = distanceToTopRoundness >= 0;
- mDistanceToTopRoundness = distanceToTopRoundness;
- applyRoundness();
- }
- }
-
protected boolean childNeedsClipping(View child) {
return false;
}
- public boolean topAmountNeedsClipping() {
- return true;
- }
-
protected boolean isClippingNeeded() {
- return mAlwaysRoundBothCorners || mCustomOutline || getTranslation() != 0 ;
+ // When translating the contents instead of the overall view, we need to make sure we clip
+ // rounded to the contents.
+ boolean forTranslation = getTranslation() != 0 && !mDismissUsingRowTranslationX;
+ return mAlwaysRoundBothCorners || mCustomOutline || forTranslation;
}
private void initDimens() {
Resources res = getResources();
- mShouldTranslateContents =
- res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe);
mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius);
mAlwaysRoundBothCorners = res.getBoolean(R.bool.config_clipNotificationsToOutline);
if (!mAlwaysRoundBothCorners) {
@@ -272,11 +241,6 @@ public abstract class ExpandableOutlineView extends ExpandableView {
}
public float getCurrentBackgroundRadiusTop() {
- // If this view is top amount notification view, it should always has round corners on top.
- // It will be applied with applyRoundness()
- if (mTopAmountRounded) {
- return mOutlineRadius;
- }
return getCurrentTopRoundness() * mOutlineRadius;
}
@@ -382,9 +346,25 @@ public abstract class ExpandableOutlineView extends ExpandableView {
}
}
+ /**
+ * Set the dismiss behavior of the view.
+ * @param usingRowTranslationX {@code true} if the view should translate using regular
+ * translationX, otherwise the contents will be
+ * translated.
+ */
+ public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) {
+ mDismissUsingRowTranslationX = usingRowTranslationX;
+ }
+
@Override
public int getOutlineTranslation() {
- return mCustomOutline ? mOutlineRect.left : (int) getTranslation();
+ if (mCustomOutline) {
+ return mOutlineRect.left;
+ }
+ if (mDismissUsingRowTranslationX) {
+ return 0;
+ }
+ return (int) getTranslation();
}
public void updateOutline() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 763d197847c3..8b0764b1c313 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -46,7 +46,6 @@ import java.util.List;
public abstract class ExpandableView extends FrameLayout implements Dumpable {
private static final String TAG = "ExpandableView";
- public static final float NO_ROUNDNESS = -1;
protected OnHeightChangedListener mOnHeightChangedListener;
private int mActualHeight;
protected int mClipTopAmount;
@@ -192,14 +191,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
}
}
- /**
- * Set the distance to the top roundness, from where we should start clipping a value above
- * or equal to 0 is the effective distance, and if a value below 0 is received, there should
- * be no clipping.
- */
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- }
-
public void setActualHeight(int actualHeight) {
setActualHeight(actualHeight, true /* notifyListeners */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 4b1f679b8851..754de580cd61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -42,10 +42,8 @@ public class NotificationBackgroundView extends View {
private int mActualHeight;
private int mClipBottomAmount;
private int mTintColor;
- private float[] mCornerRadii = new float[8];
+ private final float[] mCornerRadii = new float[8];
private boolean mBottomIsRounded;
- private boolean mLastInSection;
- private boolean mFirstInSection;
private int mBackgroundTop;
private boolean mBottomAmountClips = true;
private boolean mExpandAnimationRunning;
@@ -53,9 +51,6 @@ public class NotificationBackgroundView extends View {
private int mDrawableAlpha = 255;
private boolean mIsPressedAllowed;
- private boolean mTopAmountRounded;
- private float mDistanceToTopRoundness;
-
public NotificationBackgroundView(Context context, AttributeSet attrs) {
super(context, attrs);
mDontModifyCorners = getResources().getBoolean(
@@ -90,15 +85,6 @@ public class NotificationBackgroundView extends View {
left = (int) ((getWidth() - mActualWidth) / 2.0f);
right = (int) (left + mActualWidth);
}
- if (mTopAmountRounded) {
- int clipTop = (int) (mClipTopAmount - mDistanceToTopRoundness);
- if (clipTop >= 0 || !mFirstInSection) {
- top += clipTop;
- }
- if (clipTop >= 0 && !mLastInSection) {
- bottom += clipTop;
- }
- }
drawable.setBounds(left, top, right, bottom);
drawable.draw(canvas);
}
@@ -180,14 +166,6 @@ public class NotificationBackgroundView extends View {
invalidate();
}
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- if (distanceToTopRoundness != mDistanceToTopRoundness) {
- mTopAmountRounded = distanceToTopRoundness >= 0;
- mDistanceToTopRoundness = distanceToTopRoundness;
- invalidate();
- }
- }
-
@Override
public boolean hasOverlappingRendering() {
@@ -246,18 +224,6 @@ public class NotificationBackgroundView extends View {
}
}
- /** Sets whether this background belongs to the last notification in a section. */
- public void setLastInSection(boolean lastInSection) {
- mLastInSection = lastInSection;
- invalidate();
- }
-
- /** Sets whether this background belongs to the first notification in a section. */
- public void setFirstInSection(boolean firstInSection) {
- mFirstInSection = firstInSection;
- invalidate();
- }
-
private void updateBackgroundRadii() {
if (mDontModifyCorners) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 0c86262d9037..6822d24947c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -75,7 +75,6 @@ public class AmbientState {
private int mExpandAnimationTopChange;
private ExpandableNotificationRow mExpandingNotification;
private float mHideAmount;
- private float mNotificationScrimTop;
private boolean mAppearing;
private float mPulseHeight = MAX_PULSE_HEIGHT;
private float mDozeAmount = 0.0f;
@@ -256,20 +255,6 @@ public class AmbientState {
return mHideAmount;
}
- /**
- * Set y position of top of notifications background scrim, relative to top of screen.
- */
- public void setNotificationScrimTop(float notificationScrimTop) {
- mNotificationScrimTop = notificationScrimTop;
- }
-
- /**
- * @return Y position of top of notifications background scrim, relative to top of screen.
- */
- public float getNotificationScrimTop() {
- return mNotificationScrimTop;
- }
-
public void setHideSensitive(boolean hideSensitive) {
mHideSensitive = hideSensitive;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index f90b4c079c50..d79c57565dcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -40,6 +40,7 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
@@ -424,13 +425,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final Rect mBackgroundAnimationRect = new Rect();
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
+
+ /**
+ * The position of the scroll boundary relative to this view. This is where the notifications
+ * stop scrolling and will start to clip instead.
+ */
+ private int mQsScrollBoundaryPosition;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final Rect mTmpRect = new Rect();
private DismissListener mDismissListener;
private DismissAllAnimationListener mDismissAllAnimationListener;
private NotificationRemoteInputManager mRemoteInputManager;
private ShadeController mShadeController;
- private Runnable mOnStackYChanged;
+ private Consumer<Boolean> mOnStackYChanged;
protected boolean mClearAllEnabled;
@@ -453,6 +460,38 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private NotificationStackScrollLayoutController mController;
private boolean mKeyguardMediaControllorVisible;
+
+ /**
+ * The clip path used to clip the view in a rounded way.
+ */
+ private final Path mRoundedClipPath = new Path();
+
+ /**
+ * Should we use rounded rect clipping right now
+ */
+ private boolean mShouldUseRoundedRectClipping = false;
+
+ private int mRoundedRectClippingLeft;
+ private int mRoundedRectClippingTop;
+ private int mRoundedRectClippingBottom;
+ private int mRoundedRectClippingRight;
+ private float[] mBgCornerRadii = new float[8];
+
+ /**
+ * Whether stackY should be animated in case the view is getting shorter than the scroll
+ * position and this scrolling will lead to the top scroll inset getting smaller.
+ */
+ private boolean mAnimateStackYForContentHeightChange = false;
+
+ /**
+ * Are we launching a notification right now
+ */
+ private boolean mLaunchingNotification;
+
+ /**
+ * Do notifications dismiss with normal transitioning
+ */
+ private boolean mDismissUsingRowTranslationX = true;
private NotificationEntry mTopHeadsUpEntry;
private long mNumHeadsUp;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
@@ -506,7 +545,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSectionsManager = notificationSectionsManager;
mFeatureFlags = featureFlags;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res);
+ updateSplitNotificationShade();
mSectionsManager.initialize(this, LayoutInflater.from(context));
mSections = mSectionsManager.createSectionsForBuckets();
@@ -862,6 +901,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mCornerRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius);
mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
+ mQsScrollBoundaryPosition = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.quick_qs_offset_height);
}
void updateCornerRadius() {
@@ -961,6 +1002,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateFirstAndLastBackgroundViews();
updateAlgorithmLayoutMinHeight();
updateOwnTranslationZ();
+
+ // Once the layout has finished, we don't need to animate any scrolling clampings anymore.
+ mAnimateStackYForContentHeightChange = false;
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -1017,33 +1061,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void onPreDrawDuringAnimation() {
mShelf.updateAppearance();
- updateClippingToTopRoundedCorner();
if (!mNeedsAnimation && !mChildrenUpdateRequested) {
updateBackground();
}
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void updateClippingToTopRoundedCorner() {
- Float clipStart = mAmbientState.getNotificationScrimTop();
- Float clipEnd = clipStart + mCornerRadius;
- boolean first = true;
- for (int i = 0; i < getChildCount(); i++) {
- ExpandableView child = (ExpandableView) getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
- float start = child.getTranslationY();
- float end = start + child.getActualHeight();
- boolean clip = clipStart > start && clipStart < end
- || clipEnd >= start && clipEnd <= end;
- clip &= !(first && mScrollAdapter.isScrolledToTop());
- child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
- : ExpandableView.NO_ROUNDNESS);
- first = false;
- }
- }
-
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateScrollStateForAddedChildren() {
if (mChildrenToAddAnimated.isEmpty()) {
@@ -1117,7 +1139,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private void clampScrollPosition() {
int scrollRange = getScrollRange();
if (scrollRange < mOwnScrollY) {
- setOwnScrollY(scrollRange);
+ boolean animateStackY = false;
+ if (scrollRange < getScrollAmountToScrollBoundary()
+ && mAnimateStackYForContentHeightChange) {
+ // if the scroll boundary updates the position of the stack,
+ animateStackY = true;
+ }
+ setOwnScrollY(scrollRange, animateStackY);
}
}
@@ -1146,6 +1174,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* Apply expansion fraction to the y position and height of the notifications panel.
*/
private void updateStackPosition() {
+ updateStackPosition(false /* listenerNeedsAnimation */);
+ }
+
+ /**
+ * Apply expansion fraction to the y position and height of the notifications panel.
+ * @param listenerNeedsAnimation does the listener need to animate?
+ */
+ private void updateStackPosition(boolean listenerNeedsAnimation) {
// Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition
+ mAmbientState.getOverExpansion();
@@ -1153,7 +1189,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
mAmbientState.setStackY(stackY);
if (mOnStackYChanged != null) {
- mOnStackYChanged.run();
+ mOnStackYChanged.accept(listenerNeedsAnimation);
}
if (mQsExpansionFraction <= 0) {
final float stackEndHeight = Math.max(0f,
@@ -1165,7 +1201,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
- void setOnStackYChanged(Runnable onStackYChanged) {
+ /**
+ * Add a listener when the StackY changes. The argument signifies whether an animation is
+ * needed.
+ */
+ void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
mOnStackYChanged = onStackYChanged;
}
@@ -1600,7 +1640,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Resources res = getResources();
- mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res);
+ updateSplitNotificationShade();
mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
float densityScale = res.getDisplayMetrics().density;
mSwipeHelper.setDensityScale(densityScale);
@@ -2527,8 +2567,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateScrollStateForRemovedChild(child);
boolean animationGenerated = generateRemoveAnimation(child);
if (animationGenerated) {
- if (!mSwipedOutViews.contains(child)
- || Math.abs(child.getTranslation()) != child.getWidth()) {
+ if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) {
container.addTransientView(child, 0);
child.setTransientContainer(container);
}
@@ -2540,6 +2579,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
focusNextViewIfFocused(child);
}
+ /**
+ * Has this view been fully swiped out such that it's not visible anymore.
+ */
+ public boolean isFullySwipedOut(ExpandableView child) {
+ return Math.abs(child.getTranslation()) >= Math.abs(getTotalTranslationLength(child));
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void focusNextViewIfFocused(View view) {
if (view instanceof ExpandableNotificationRow) {
@@ -2659,17 +2705,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final int startingPosition = getPositionInLinearLayout(removedChild);
final int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements;
final int endPosition = startingPosition + childHeight;
- if (endPosition <= mOwnScrollY) {
+ final int scrollBoundaryStart = getScrollAmountToScrollBoundary();
+ mAnimateStackYForContentHeightChange = true;
+ // This is reset onLayout
+ if (endPosition <= mOwnScrollY - scrollBoundaryStart) {
// This child is fully scrolled of the top, so we have to deduct its height from the
// scrollPosition
setOwnScrollY(mOwnScrollY - childHeight);
- } else if (startingPosition < mOwnScrollY) {
+ } else if (startingPosition < mOwnScrollY - scrollBoundaryStart) {
// This child is currently being scrolled into, set the scroll position to the
// start of this child
- setOwnScrollY(startingPosition);
+ setOwnScrollY(startingPosition + scrollBoundaryStart);
}
}
+ /**
+ * @return the amount of scrolling needed to start clipping notifications.
+ */
+ private int getScrollAmountToScrollBoundary() {
+ return mTopPadding - mQsScrollBoundaryPosition;
+ }
+
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int getIntrinsicHeight(View view) {
if (view instanceof ExpandableView) {
@@ -2758,7 +2814,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateAnimationState(child);
updateChronometerForChild(child);
if (child instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl);
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ row.setDismissRtl(mDismissRtl);
+ row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX);
+
}
}
@@ -2818,6 +2877,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
+
+ // Disable clipping for launches
+ setLaunchingNotification(params != null);
requestChildrenUpdate();
}
@@ -2901,7 +2963,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mAnimationEvents.clear();
updateBackground();
updateViewShadows();
- updateClippingToTopRoundedCorner();
} else {
applyCurrentState();
}
@@ -3030,7 +3091,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
removedTranslation = row.getTranslationWhenRemoved();
ignoreChildren = false;
}
- childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
+ childWasSwipedOut |= isFullySwipedOut(row);
} else if (child instanceof MediaHeaderView) {
childWasSwipedOut = true;
}
@@ -3038,11 +3099,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
Rect clipBounds = child.getClipBounds();
childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
- if (childWasSwipedOut && child instanceof ExpandableView) {
+ if (childWasSwipedOut) {
// Clean up any potential transient views if the child has already been swiped
// out, as we won't be animating it further (due to its height already being
// clipped to 0.
- ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
+ ViewGroup transientContainer = child.getTransientContainer();
if (transientContainer != null) {
transientContainer.removeTransientView(child);
}
@@ -3795,6 +3856,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateNotificationAnimationStates();
updateChronometers();
requestChildrenUpdate();
+ updateUseRoundedRectClipping();
}
}
@@ -3815,6 +3877,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
void onChildHeightChanged(ExpandableView view, boolean needsAnimation) {
+ boolean previouslyNeededAnimation = mAnimateStackYForContentHeightChange;
+ if (needsAnimation) {
+ mAnimateStackYForContentHeightChange = true;
+ }
updateContentHeight();
updateScrollPositionOnExpandInBottom(view);
clampScrollPosition();
@@ -3835,6 +3901,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
requestAnimationOnViewResize(row);
}
requestChildrenUpdate();
+ mAnimateStackYForContentHeightChange = previouslyNeededAnimation;
}
void onChildHeightReset(ExpandableView view) {
@@ -4015,7 +4082,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
setAnimationRunning(false);
updateBackground();
updateViewShadows();
- updateClippingToTopRoundedCorner();
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4048,7 +4114,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
expandableView.setFakeShadowIntensity(
diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
previous.getOutlineAlpha(), (int) yLocation,
- previous.getOutlineTranslation());
+ (int) (previous.getOutlineTranslation() + previous.getTranslation()));
}
previous = expandableView;
}
@@ -4550,6 +4616,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setQsExpansionFraction(float qsExpansionFraction) {
mQsExpansionFraction = qsExpansionFraction;
+ updateUseRoundedRectClipping();
// If notifications are scrolled,
// clear out scrollY by the time we push notifications offscreen
@@ -4560,13 +4627,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void setOwnScrollY(int ownScrollY) {
+ setOwnScrollY(ownScrollY, false /* animateScrollChangeListener */);
+ }
+
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
+ private void setOwnScrollY(int ownScrollY, boolean animateStackYChangeListener) {
if (ownScrollY != mOwnScrollY) {
// We still want to call the normal scrolled changed for accessibility reasons
onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
mOwnScrollY = ownScrollY;
mAmbientState.setScrollY(mOwnScrollY);
updateOnScrollChange();
- updateStackPosition();
+ updateStackPosition(animateStackYChangeListener);
}
}
@@ -4632,6 +4704,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mStatusBarState = statusBarState;
mAmbientState.setStatusBarState(statusBarState);
updateSpeedBumpIndex();
+ updateDismissBehavior();
}
void onStatePostChange(boolean fromShadeLocked) {
@@ -5192,6 +5265,108 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
/**
+ * Set rounded rect clipping bounds on this view.
+ */
+ public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
+ int bottomRadius) {
+ if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right
+ && mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top
+ && mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) {
+ return;
+ }
+ mRoundedRectClippingLeft = left;
+ mRoundedRectClippingTop = top;
+ mRoundedRectClippingBottom = bottom;
+ mRoundedRectClippingRight = right;
+ mBgCornerRadii[0] = topRadius;
+ mBgCornerRadii[1] = topRadius;
+ mBgCornerRadii[2] = topRadius;
+ mBgCornerRadii[3] = topRadius;
+ mBgCornerRadii[4] = bottomRadius;
+ mBgCornerRadii[5] = bottomRadius;
+ mBgCornerRadii[6] = bottomRadius;
+ mBgCornerRadii[7] = bottomRadius;
+ mRoundedClipPath.reset();
+ mRoundedClipPath.addRoundRect(left, top, right, bottom, mBgCornerRadii, Path.Direction.CW);
+ if (mShouldUseRoundedRectClipping) {
+ invalidate();
+ }
+ }
+
+ private void updateSplitNotificationShade() {
+ boolean split = shouldUseSplitNotificationShade(mFeatureFlags, getResources());
+ if (split != mShouldUseSplitNotificationShade) {
+ mShouldUseSplitNotificationShade = split;
+ updateDismissBehavior();
+ updateUseRoundedRectClipping();
+ }
+ }
+
+ private void updateDismissBehavior() {
+ // On the split keyguard, dismissing with clipping without a visual boundary looks odd,
+ // so let's use the content dismiss behavior instead.
+ boolean dismissUsingRowTranslationX = !mShouldUseSplitNotificationShade
+ || mStatusBarState != StatusBarState.KEYGUARD;
+ if (mDismissUsingRowTranslationX != dismissUsingRowTranslationX) {
+ mDismissUsingRowTranslationX = dismissUsingRowTranslationX;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) child).setDismissUsingRowTranslationX(
+ dismissUsingRowTranslationX);
+ }
+ }
+ }
+ }
+
+ /**
+ * Set if we're launching a notification right now.
+ */
+ private void setLaunchingNotification(boolean launching) {
+ if (launching == mLaunchingNotification) {
+ return;
+ }
+ mLaunchingNotification = launching;
+ updateUseRoundedRectClipping();
+ }
+
+ /**
+ * Should we use rounded rect clipping
+ */
+ private void updateUseRoundedRectClipping() {
+ // We don't want to clip notifications when QS is expanded, because incoming heads up on
+ // the bottom would be clipped otherwise
+ boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade;
+ boolean clip = !mLaunchingNotification && mIsExpanded && qsAllowsClipping;
+ if (clip != mShouldUseRoundedRectClipping) {
+ mShouldUseRoundedRectClipping = clip;
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (mShouldUseRoundedRectClipping) {
+ // Let's clip rounded.
+ canvas.clipPath(mRoundedClipPath);
+ }
+ super.dispatchDraw(canvas);
+ }
+
+ /**
+ * Calculate the total translation needed when dismissing.
+ */
+ public float getTotalTranslationLength(View animView) {
+ if (!mDismissUsingRowTranslationX) {
+ return animView.getMeasuredWidth();
+ }
+ float notificationWidth = animView.getMeasuredWidth();
+ int containerWidth = getMeasuredWidth();
+ float padding = (containerWidth - notificationWidth) / 2.0f;
+ return containerWidth - padding;
+ }
+
+ /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
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 dec98887577e..fb4f5592e97f 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
@@ -384,6 +384,11 @@ public class NotificationStackScrollLayoutController {
}
@Override
+ public float getTotalTranslationLength(View animView) {
+ return mView.getTotalTranslationLength(animView);
+ }
+
+ @Override
public void onSnooze(StatusBarNotification sbn,
NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
@@ -822,8 +827,18 @@ public class NotificationStackScrollLayoutController {
return mView.isLayoutRtl();
}
+ /**
+ * @return the left of the view.
+ */
public int getLeft() {
- return mView.getLeft();
+ return mView.getLeft();
+ }
+
+ /**
+ * @return the top of the view.
+ */
+ public int getTop() {
+ return mView.getTop();
}
public float getTranslationX() {
@@ -1008,7 +1023,7 @@ public class NotificationStackScrollLayoutController {
mView.setQsExpansionFraction(expansionFraction);
}
- public void setOnStackYChanged(Runnable onStackYChanged) {
+ public void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
mView.setOnStackYChanged(onStackYChanged);
}
@@ -1440,6 +1455,14 @@ public class NotificationStackScrollLayoutController {
}
/**
+ * Set rounded rect clipping bounds on this view.
+ */
+ public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
+ int bottomRadius) {
+ mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
+ }
+
+ /**
* Enum for UiEvent logged from this class
*/
enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index f4c4d440b063..664776975b24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -325,6 +325,11 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
}
@Override
+ protected float getTotalTranslationLength(View animView) {
+ return mCallback.getTotalTranslationLength(animView);
+ }
+
+ @Override
public void setTranslation(View v, float translate) {
if (v instanceof SwipeableView) {
((SwipeableView) v).setTranslation(translate);
@@ -466,6 +471,13 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
void onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption);
void onDismiss();
+
+ /**
+ * Get the total translation length where we want to swipe to when dismissing the view. By
+ * default this is the size of the view, but can also be larger.
+ * @param animView the view to ask about
+ */
+ float getTotalTranslationLength(View animView);
}
static class Builder {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e5fd103c239f..74e8de4c9d11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -158,7 +158,7 @@ public class StackScrollAlgorithm {
AmbientState ambientState) {
float drawStart = ambientState.isOnKeyguard() ? 0
: ambientState.getStackY() - ambientState.getScrollY();
- float clipStart = ambientState.getNotificationScrimTop();
+ float clipStart = 0;
int childCount = algorithmState.visibleChildren.size();
boolean firstHeadsUp = true;
for (int i = 0; i < childCount; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 4fd2064b394d..ee12b4b2d728 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -392,7 +392,7 @@ public class StackStateAnimator {
0, () -> removeTransientView(changingView), null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
- if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
+ if (mHostLayout.isFullySwipedOut(changingView)
&& changingView.getTransientContainer() != null) {
changingView.getTransientContainer().removeTransientView(changingView);
}
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 528827f639dd..35d1526c4c59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -830,6 +830,7 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.setOverscrollTopChangedListener(
mOnOverscrollTopChangedListener);
mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled);
+ mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged);
mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
mOnEmptySpaceClickListener);
addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
@@ -2198,15 +2199,17 @@ public class NotificationPanelViewController extends PanelViewController {
mDepthController.setQsPanelExpansion(qsExpansionFraction);
}
- private Runnable mOnStackYChanged = () -> {
+ private void onStackYChanged(boolean shouldAnimate) {
if (mQs != null) {
+ if (shouldAnimate) {
+ mAnimateNextNotificationBounds = true;
+ mNotificationBoundsAnimationDelay = 0;
+ }
setQSClippingBounds();
}
};
private void onNotificationScrolled(int newScrollPosition) {
- // Since this is an overscroller, sometimes the scrollY can be temporarily negative
- // (when overscrollng on the top and flinging). Let's
updateQSExpansionEnabledAmbient();
}
@@ -2228,14 +2231,13 @@ public class NotificationPanelViewController extends PanelViewController {
* and QS state.
*/
private void setQSClippingBounds() {
- int top = 0;
- int bottom = 0;
- int left = 0;
- int right = 0;
+ int top;
+ int bottom;
+ int left;
+ int right;
final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
- final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0)
- && !mShouldUseSplitNotificationShade;
+ final boolean qsVisible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0);
if (!mShouldUseSplitNotificationShade) {
if (mTransitioningToFullShadeProgress > 0.0f) {
@@ -2244,7 +2246,6 @@ public class NotificationPanelViewController extends PanelViewController {
top = mTransitionToFullShadeQSPosition;
} else {
final float notificationTop = getQSEdgePosition();
- mAmbientState.setNotificationScrimTop(notificationTop);
top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop)
: notificationTop);
}
@@ -2252,8 +2253,7 @@ public class NotificationPanelViewController extends PanelViewController {
// notification bounds should take full screen width regardless of insets
left = 0;
right = getView().getRight() + mDisplayRightInset;
- } else if (qsPanelBottomY > 0) { // so bounds are empty on lockscreen
- mAmbientState.setNotificationScrimTop(mSplitShadeNotificationsTopPadding);
+ } else {
top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
bottom = mNotificationStackScrollLayoutController.getHeight();
left = mNotificationStackScrollLayoutController.getLeft();
@@ -2261,17 +2261,17 @@ public class NotificationPanelViewController extends PanelViewController {
}
// top should never be lower than bottom, otherwise it will be invisible.
top = Math.min(top, bottom);
- applyQSClippingBounds(left, top, right, bottom, visible);
+ applyQSClippingBounds(left, top, right, bottom, qsVisible);
}
private void applyQSClippingBounds(int left, int top, int right, int bottom,
- boolean visible) {
+ boolean qsVisible) {
if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
if (mQsClippingAnimation != null) {
// update the end position of the animator
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
} else {
- applyQSClippingImmediately(left, top, right, bottom, visible);
+ applyQSClippingImmediately(left, top, right, bottom, qsVisible);
}
} else {
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
@@ -2295,7 +2295,7 @@ public class NotificationPanelViewController extends PanelViewController {
int animBottom = (int) MathUtils.lerp(startBottom,
mQsClippingAnimationEndBounds.bottom, fraction);
applyQSClippingImmediately(animLeft, animTop, animRight, animBottom,
- visible /* visible */);
+ qsVisible /* qsVisible */);
});
mQsClippingAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@@ -2310,7 +2310,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void applyQSClippingImmediately(int left, int top, int right, int bottom,
- boolean visible) {
+ boolean qsVisible) {
// Fancy clipping for quick settings
int radius = mScrimCornerRadius;
int statusBarClipTop = 0;
@@ -2318,19 +2318,34 @@ public class NotificationPanelViewController extends PanelViewController {
if (!mShouldUseSplitNotificationShade) {
// The padding on this area is large enough that we can use a cheaper clipping strategy
mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
- clipStatusView = visible;
+ clipStatusView = qsVisible;
radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
Math.min(top / (float) mScrimCornerRadius, 1f));
statusBarClipTop = top - mKeyguardStatusBar.getTop();
}
if (mQs != null) {
- mQs.setFancyClipping(top, bottom, radius, visible);
+ mQs.setFancyClipping(top, bottom, radius, qsVisible
+ && !mShouldUseSplitNotificationShade);
}
mKeyguardStatusViewController.setClipBounds(
clipStatusView ? mKeyguardStatusAreaClipBounds : null);
- mScrimController.setNotificationsBounds(left, top, right, bottom);
+ if (!qsVisible && mShouldUseSplitNotificationShade) {
+ // On the lockscreen when qs isn't visible, we don't want the bounds of the shade to
+ // be visible, otherwise you can see the bounds once swiping up to see bouncer
+ mScrimController.setNotificationsBounds(0, 0, 0, 0);
+ } else {
+ mScrimController.setNotificationsBounds(left, top, right, bottom);
+ }
+
mScrimController.setScrimCornerRadius(radius);
mKeyguardStatusBar.setTopClipping(statusBarClipTop);
+ int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
+ int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
+ int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
+ int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
+ int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0;
+ mNotificationStackScrollLayoutController.setRoundedClippingBounds(
+ nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
}
private float getQSEdgePosition() {
@@ -3323,7 +3338,6 @@ public class NotificationPanelViewController extends PanelViewController {
// The expandedHeight is always the full panel Height when bypassing
expandedHeight = getMaxPanelHeightNonBypass();
}
- mNotificationStackScrollLayoutController.setOnStackYChanged(mOnStackYChanged);
mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight);
updateKeyguardBottomAreaAlpha();
updateBigClockAlpha();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 5ee5e489479d..f152d0cc28fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
-import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -46,8 +45,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.wm.shell.bubbles.BubbleController.TASKBAR_CHANGED_BROADCAST;
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -121,6 +118,7 @@ import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;
+import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
@@ -402,8 +400,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private LightRevealScrim mLightRevealScrim;
private WiredChargingRippleController mChargingRippleAnimationController;
private PowerButtonReveal mPowerButtonReveal;
- private CircleReveal mCircleReveal;
- private ValueAnimator mCircleRevealAnimator = ValueAnimator.ofFloat(0f, 1f);
private final Object mQueueLock = new Object();
@@ -2778,9 +2774,14 @@ public class StatusBar extends SystemUI implements DemoMode,
+ String.valueOf(CameraIntents.getOverrideCameraPackage(mContext)));
}
- public static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
+ public static void dumpBarTransitions(
+ PrintWriter pw, String var, @Nullable BarTransitions transitions) {
pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode=");
- pw.println(BarTransitions.modeToString(transitions.getMode()));
+ if (transitions != null) {
+ pw.println(BarTransitions.modeToString(transitions.getMode()));
+ } else {
+ pw.println("Unknown");
+ }
}
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
@@ -2803,11 +2804,11 @@ public class StatusBar extends SystemUI implements DemoMode,
return mDisplayMetrics.density;
}
- float getDisplayWidth() {
+ public float getDisplayWidth() {
return mDisplayMetrics.widthPixels;
}
- float getDisplayHeight() {
+ public float getDisplayHeight() {
return mDisplayMetrics.heightPixels;
}
@@ -2841,9 +2842,11 @@ public class StatusBar extends SystemUI implements DemoMode,
mActivityIntentHelper.wouldLaunchResolverActivity(intent,
mLockscreenUserManager.getCurrentUserId());
+ boolean animate =
+ animationController != null && !willLaunchResolverActivity && shouldAnimateLaunch(
+ true /* isActivityIntent */);
ActivityLaunchAnimator.Controller animController =
- !willLaunchResolverActivity && shouldAnimateLaunch(true /* isActivityIntent */)
- ? wrapAnimationController(animationController, dismissShade) : null;
+ animate ? 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.
@@ -2857,7 +2860,7 @@ public class StatusBar extends SystemUI implements DemoMode,
int[] result = new int[]{ActivityManager.START_CANCELED};
mActivityLaunchAnimator.startIntentWithAnimation(animController,
- true /* animate */, intent.getPackage(), (adapter) -> {
+ animate, intent.getPackage(), (adapter) -> {
ActivityOptions options = new ActivityOptions(
getActivityOptions(mDisplayId, adapter));
options.setDisallowEnterPictureInPictureWhileLaunching(
@@ -2907,16 +2910,12 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly,
- willLaunchResolverActivity, true /* deferred */);
+ willLaunchResolverActivity, true /* deferred */, animate);
}
@Nullable
private ActivityLaunchAnimator.Controller wrapAnimationController(
- @Nullable ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
- if (animationController == null) {
- return null;
- }
-
+ ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
View rootView = animationController.getLaunchContainer().getRootView();
if (rootView == mSuperStatusBarViewFactory.getStatusBarWindowView()) {
// We are animating a view in the status bar. We have to make sure that the status bar
@@ -2959,34 +2958,56 @@ public class StatusBar extends SystemUI implements DemoMode,
final boolean dismissShade,
final boolean afterKeyguardGone,
final boolean deferred) {
- dismissKeyguardThenExecute(() -> {
- if (runnable != null) {
- if (mStatusBarKeyguardViewManager.isShowing()
- && mStatusBarKeyguardViewManager.isOccluded()) {
- mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
- } else {
- AsyncTask.execute(runnable);
- }
- }
- if (dismissShade) {
- if (mExpandedVisible && !mBouncerShowing) {
- mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
- true /* force */, true /* delayed*/);
- } else {
+ executeRunnableDismissingKeyguard(runnable, cancelAction, dismissShade, afterKeyguardGone,
+ deferred, false /* willAnimateOnKeyguard */);
+ }
- // Do it after DismissAction has been processed to conserve the needed ordering.
- mHandler.post(mShadeController::runPostCollapseRunnables);
+ 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 {
+ AsyncTask.execute(runnable);
+ }
}
- } else if (isInLaunchTransition()
- && mNotificationPanelViewController.isLaunchTransitionFinished()) {
+ 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.
+ mHandler.post(mShadeController::runPostCollapseRunnables);
+ }
+ } else if (StatusBar.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.
+ mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone);
+ }
+ return deferred;
+ }
- // 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.
- mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone);
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return willAnimateOnKeyguard;
}
- return deferred;
- }, cancelAction, afterKeyguardGone);
+ };
+ dismissKeyguardThenExecute(onDismissAction, cancelAction, afterKeyguardGone);
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -3408,6 +3429,10 @@ public class StatusBar extends SystemUI implements DemoMode,
}
boolean updateIsKeyguard() {
+ return updateIsKeyguard(false /* force */);
+ }
+
+ boolean updateIsKeyguard(boolean force) {
boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -3431,7 +3456,7 @@ public class StatusBar extends SystemUI implements DemoMode,
showKeyguardImpl();
}
} else {
- return hideKeyguardImpl();
+ return hideKeyguardImpl(force);
}
return false;
}
@@ -3513,9 +3538,6 @@ public class StatusBar extends SystemUI implements DemoMode,
public void fadeKeyguardWhilePulsing() {
mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
()-> {
- if (shouldShowCircleReveal()) {
- startCircleReveal();
- }
hideKeyguard();
mStatusBarKeyguardViewManager.onKeyguardFadedAway();
}).start();
@@ -3560,11 +3582,11 @@ public class StatusBar extends SystemUI implements DemoMode,
/**
* @return true if we would like to stay in the shade, false if it should go away entirely
*/
- public boolean hideKeyguardImpl() {
+ public boolean hideKeyguardImpl(boolean force) {
mIsKeyguard = false;
Trace.beginSection("StatusBar#hideKeyguard");
boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
- if (!(mStatusBarStateController.setState(StatusBarState.SHADE))) {
+ if (!(mStatusBarStateController.setState(StatusBarState.SHADE, force))) {
//TODO: StatusBarStateController should probably know about hiding the keyguard and
// notify listeners.
@@ -3856,7 +3878,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onDozeAmountChanged(float linear, float eased) {
if (mFeatureFlags.useNewLockscreenAnimations()
- && !mCircleRevealAnimator.isRunning()) {
+ && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
}
@@ -3879,7 +3901,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|| (!isDozing && mWakefulnessLifecycle.getLastWakeReason()
== PowerManager.WAKE_REASON_POWER_BUTTON)) {
mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
- } else if (!mCircleRevealAnimator.isRunning()) {
+ } else if (!(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
}
@@ -3891,36 +3913,8 @@ public class StatusBar extends SystemUI implements DemoMode,
Trace.endSection();
}
- /**
- * Update the parameters for the dozing circle reveal that animates when the user authenticates
- * from AOD using the fingerprint sensor.
- */
- public void updateCircleReveal() {
- final PointF fpLocation = mAuthRippleController.getFingerprintSensorLocation();
- if (fpLocation != null) {
- mCircleReveal =
- new CircleReveal(
- fpLocation.x,
- fpLocation.y,
- 0,
- Math.max(Math.max(fpLocation.x, getDisplayWidth() - fpLocation.x),
- Math.max(fpLocation.y, getDisplayHeight() - fpLocation.y)));
- }
- }
-
- private void startCircleReveal() {
- mLightRevealScrim.setRevealEffect(mCircleReveal);
- mCircleRevealAnimator.cancel();
- mCircleRevealAnimator.addUpdateListener(animation ->
- mLightRevealScrim.setRevealAmount(
- (float) mCircleRevealAnimator.getAnimatedValue()));
- mCircleRevealAnimator.setDuration(900);
- mCircleRevealAnimator.start();
- }
-
- private boolean shouldShowCircleReveal() {
- return mCircleReveal != null && !mCircleRevealAnimator.isRunning()
- && mBiometricUnlockController.getBiometricType() == FINGERPRINT;
+ public LightRevealScrim getLightRevealScrim() {
+ return mLightRevealScrim;
}
private void updateKeyguardState() {
@@ -4609,28 +4603,37 @@ public class StatusBar extends SystemUI implements DemoMode,
*
* @param action The action to execute after dismissing the keyguard.
* @param collapsePanel Whether we should collapse the panel after dismissing the keyguard.
- * @param deferKeyguardDismiss Whether we should defer the keyguard actual dismissal, for
- * instance to run animations on the keyguard before hiding it.
+ * @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 deferKeyguardDismiss) {
+ boolean collapsePanel, boolean willAnimateOnKeyguard) {
if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
- dismissKeyguardThenExecute(() -> {
- 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();
+ 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() : deferKeyguardDismiss;
- }, afterKeyguardGone);
+ return collapsePanel ? mShadeController.collapsePanel() : willAnimateOnKeyguard;
+ }
+
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return willAnimateOnKeyguard;
+ }
+ };
+ dismissKeyguardThenExecute(onDismissAction, afterKeyguardGone);
}
@Override
@@ -4674,7 +4677,6 @@ public class StatusBar extends SystemUI implements DemoMode,
// 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;
- boolean deferKeyguardDismiss = animate;
executeActionDismissingKeyguard(() -> {
try {
// We wrap animationCallback with a StatusBarLaunchAnimatorController so that the
@@ -4703,7 +4705,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (intentSentUiThreadCallback != null) {
postOnUiThread(intentSentUiThreadCallback);
}
- }, willLaunchResolverActivity, collapse, deferKeyguardDismiss);
+ }, willLaunchResolverActivity, collapse, animate);
}
private void postOnUiThread(Runnable runnable) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index c7efcb2923e7..e8463992ed13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -192,6 +192,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private OnDismissAction mAfterKeyguardGoneAction;
private Runnable mKeyguardGoneCancelAction;
+ private boolean mDismissActionWillAnimateOnKeyguard;
private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
// Dismiss action to be launched when we stop dozing or the keyguard is gone.
@@ -447,6 +448,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mAfterKeyguardGoneAction = r;
mKeyguardGoneCancelAction = cancelAction;
+ mDismissActionWillAnimateOnKeyguard = r != null && r.willRunAnimationOnKeyguard();
// If there is an an alternate auth interceptor (like the UDFPS), show that one instead
// of the bouncer.
@@ -625,9 +627,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncer.startPreHideAnimation(finishRunnable);
mStatusBar.onBouncerPreHideAnimation();
- // startPreHideAnimation() will change the visibility of the bouncer, so we have to
- // make sure to update its state.
- updateStates();
+ // We update the state (which will show the keyguard) only if an animation will run on
+ // the keyguard. If there is no animation, we wait before updating the state so that we
+ // go directly from bouncer to launcher/app.
+ if (mDismissActionWillAnimateOnKeyguard) {
+ updateStates();
+ }
} else if (finishRunnable != null) {
finishRunnable.run();
}
@@ -798,6 +803,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mAfterKeyguardGoneAction = null;
}
mKeyguardGoneCancelAction = null;
+ mDismissActionWillAnimateOnKeyguard = false;
for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) {
mAfterKeyguardGoneRunnables.get(i).run();
}
@@ -866,6 +872,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
return; // allow bouncer to trigger saved actions
}
mAfterKeyguardGoneAction = null;
+ mDismissActionWillAnimateOnKeyguard = false;
if (mKeyguardGoneCancelAction != null) {
mKeyguardGoneCancelAction.run();
mKeyguardGoneCancelAction = null;
@@ -892,6 +899,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
};
protected void updateStates() {
+ if (mContainer == null ) {
+ return;
+ }
int vis = mContainer.getSystemUiVisibility();
boolean showing = mShowing;
boolean occluded = mOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index d93b76646d58..98b9cc9bc716 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -41,6 +41,7 @@ import android.text.TextUtils;
import android.util.EventLog;
import android.view.View;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
@@ -260,10 +261,19 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
&& mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
- ActivityStarter.OnDismissAction postKeyguardAction =
- () -> handleNotificationClickAfterKeyguardDismissed(
+ ActivityStarter.OnDismissAction postKeyguardAction = new ActivityStarter.OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ return handleNotificationClickAfterKeyguardDismissed(
entry, row, controller, intent,
isActivityIntent, animate, showOverLockscreen);
+ }
+
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return animate;
+ }
+ };
if (showOverLockscreen) {
mIsCollapsingToShowActivityOverLockscreen = true;
postKeyguardAction.onDismiss();
@@ -453,53 +463,76 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
public void startNotificationGutsIntent(final Intent intent, final int appUid,
ExpandableNotificationRow row) {
boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
- mActivityStarter.dismissKeyguardThenExecute(() -> {
- AsyncTask.execute(() -> {
- ActivityLaunchAnimator.Controller animationController =
- new StatusBarLaunchAnimatorController(
- mNotificationAnimationProvider.getAnimatorController(row),
- mStatusBar, true /* isActivityIntent */);
-
- mActivityLaunchAnimator.startIntentWithAnimation(
- animationController, animate, intent.getPackage(),
- (adapter) -> TaskStackBuilder.create(mContext)
- .addNextIntentWithParentStack(intent)
- .startActivities(getActivityOptions(
- mStatusBar.getDisplayId(),
- adapter),
- new UserHandle(UserHandle.getUserId(appUid))));
- });
- return true;
- }, null, false /* afterKeyguardGone */);
+ ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ AsyncTask.execute(() -> {
+ ActivityLaunchAnimator.Controller animationController =
+ new StatusBarLaunchAnimatorController(
+ mNotificationAnimationProvider.getAnimatorController(row),
+ mStatusBar, true /* isActivityIntent */);
+
+ mActivityLaunchAnimator.startIntentWithAnimation(
+ animationController, animate, intent.getPackage(),
+ (adapter) -> TaskStackBuilder.create(mContext)
+ .addNextIntentWithParentStack(intent)
+ .startActivities(getActivityOptions(
+ mStatusBar.getDisplayId(),
+ adapter),
+ new UserHandle(UserHandle.getUserId(appUid))));
+ });
+ return true;
+ }
+
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return animate;
+ }
+ };
+ mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null,
+ false /* afterKeyguardGone */);
}
@Override
public void startHistoryIntent(View view, boolean showHistory) {
boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
- mActivityStarter.dismissKeyguardThenExecute(() -> {
- AsyncTask.execute(() -> {
- Intent intent = showHistory ? new Intent(
- Settings.ACTION_NOTIFICATION_HISTORY) : new Intent(
- Settings.ACTION_NOTIFICATION_SETTINGS);
- TaskStackBuilder tsb = TaskStackBuilder.create(mContext)
- .addNextIntent(new Intent(Settings.ACTION_NOTIFICATION_SETTINGS));
- if (showHistory) {
- tsb.addNextIntent(intent);
- }
+ ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ AsyncTask.execute(() -> {
+ Intent intent = showHistory ? new Intent(
+ Settings.ACTION_NOTIFICATION_HISTORY) : new Intent(
+ Settings.ACTION_NOTIFICATION_SETTINGS);
+ TaskStackBuilder tsb = TaskStackBuilder.create(mContext)
+ .addNextIntent(new Intent(Settings.ACTION_NOTIFICATION_SETTINGS));
+ if (showHistory) {
+ tsb.addNextIntent(intent);
+ }
- ActivityLaunchAnimator.Controller animationController =
- new StatusBarLaunchAnimatorController(
- ActivityLaunchAnimator.Controller.fromView(view), mStatusBar,
- true /* isActivityIntent */);
+ ActivityLaunchAnimator.Controller viewController =
+ ActivityLaunchAnimator.Controller.fromView(view,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
+ );
+ ActivityLaunchAnimator.Controller animationController =
+ new StatusBarLaunchAnimatorController(viewController, mStatusBar,
+ true /* isActivityIntent */);
+
+ mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
+ intent.getPackage(),
+ (adapter) -> tsb.startActivities(
+ getActivityOptions(mStatusBar.getDisplayId(), adapter),
+ UserHandle.CURRENT));
+ });
+ return true;
+ }
- mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
- intent.getPackage(),
- (adapter) -> tsb.startActivities(
- getActivityOptions(mStatusBar.getDisplayId(), adapter),
- UserHandle.CURRENT));
- });
- return true;
- }, null, false /* afterKeyguardGone */);
+ @Override
+ public boolean willRunAnimationOnKeyguard() {
+ return animate;
+ }
+ };
+ mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null,
+ false /* afterKeyguardGone */);
}
private void removeHunAfterClick(ExpandableNotificationRow row) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index e135cc51a7bc..a2d0672e6812 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -131,9 +131,14 @@ class UnlockedScreenOffAnimationController @Inject constructor(
lightRevealAnimationPlaying = false
aodUiAnimationPlaying = false
- // Make sure the status bar is in the correct keyguard state, since we might have left it in
- // the KEYGUARD state if this wakeup cancelled the screen off animation.
- statusBar.updateIsKeyguard()
+ // Make sure the status bar is in the correct keyguard state, forcing it if necessary. This
+ // is required if the screen off animation is cancelled, since it might be incorrectly left
+ // in the KEYGUARD or SHADE states depending on when it was cancelled and whether 'lock
+ // instantly' is enabled. We need to force it so that the state is set even if we're going
+ // from SHADE to SHADE or KEYGUARD to KEYGUARD, since we might have changed parts of the UI
+ // (such as showing AOD in the shade) without actually changing the StatusBarState. This
+ // ensures that the UI definitely reflects the desired state.
+ statusBar.updateIsKeyguard(true /* force */)
}
override fun onStartedGoingToSleep() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index ef7fac311799..b295f6659f81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -25,6 +25,7 @@ import android.content.Intent
import android.util.Log
import android.view.View
import android.widget.Chronometer
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
@@ -179,7 +180,10 @@ class OngoingCallController @Inject constructor(
logger.logChipClicked()
activityStarter.postStartActivityDismissingKeyguard(
currentCallNotificationInfo.intent, 0,
- ActivityLaunchAnimator.Controller.fromView(backgroundView))
+ ActivityLaunchAnimator.Controller.fromView(
+ backgroundView,
+ InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
+ )
}
setUpUidObserver(currentCallNotificationInfo)
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 26f4a2b9d2b2..d97815f92964 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -68,7 +68,8 @@ public class TunerServiceImpl extends TunerService {
private static final String[] RESET_EXCEPTION_LIST = new String[] {
QSTileHost.TILES_SETTING,
Settings.Secure.DOZE_ALWAYS_ON,
- Settings.Secure.MEDIA_CONTROLS_RESUME
+ Settings.Secure.MEDIA_CONTROLS_RESUME,
+ Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION
};
private final Observer mObserver = new Observer();
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 92ef8504d123..3af82f91af1c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -34,6 +34,7 @@ import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.view.KeyEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -57,6 +58,7 @@ import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.nano.WmShellTraceProto;
import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
@@ -253,6 +255,15 @@ public final class WMShell extends SystemUI
}
});
+ oneHanded.registerEventCallback(new OneHandedEventCallback() {
+ @Override
+ public void notifyExpandNotification() {
+ mSysUiMainExecutor.execute(
+ () -> mCommandQueue.handleSystemKey(
+ KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
+ }
+ });
+
mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onKeyguardBouncerChanged(boolean bouncer) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 240fdf3a4e17..d87a26b096fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -25,6 +25,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -53,6 +54,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var authController: AuthController
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var bypassController: KeyguardBypassController
+ @Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Before
fun setUp() {
@@ -66,6 +68,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
commandRegistry,
notificationShadeWindowController,
bypassController,
+ biometricUnlockController,
rippleView
)
controller.init()
@@ -90,7 +93,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
// THEN update sensor location and show ripple
verify(rippleView).setSensorLocation(fpsLocation)
- verify(rippleView).startRipple(any())
+ verify(rippleView).startRipple(any(), any())
}
@Test
@@ -111,7 +114,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -132,7 +135,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -156,7 +159,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
// THEN show ripple
verify(rippleView).setSensorLocation(faceLocation)
- verify(rippleView).startRipple(any())
+ verify(rippleView).startRipple(any(), any())
}
@Test
@@ -176,7 +179,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -191,7 +194,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
0 /* userId */,
BiometricSourceType.FACE /* type */,
false /* isStrongBiometric */)
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -206,7 +209,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
0 /* userId */,
BiometricSourceType.FINGERPRINT /* type */,
false /* isStrongBiometric */)
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 83e7b17eb746..f0f5420b2531 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -38,8 +38,9 @@ import android.os.UserManager;
import android.service.dreams.IDreamManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.GestureDetector;
import android.view.IWindowManager;
-import android.view.View;
+import android.view.MotionEvent;
import android.view.WindowManagerPolicyConstants;
import androidx.test.filters.SmallTest;
@@ -57,6 +58,7 @@ import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -109,6 +111,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private SysUiState mSysUiState;
@Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
+ @Mock private StatusBar mStatusBar;
private TestableLooper mTestableLooper;
@@ -150,7 +153,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mInfoProvider,
mRingerModeTracker,
mSysUiState,
- mHandler
+ mHandler,
+ mStatusBar
);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
@@ -194,7 +198,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
}
@Test
- public void testShouldLogOnTapOutside() {
+ public void testSingleTap_logAndDismiss() {
mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
@@ -207,9 +211,58 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
};
doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
- View container = dialog.findViewById(com.android.systemui.R.id.global_actions_container);
- container.callOnClick();
+
+ GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
+ gestureListener.onSingleTapConfirmed(null);
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ }
+
+ @Test
+ public void testSwipeDownLockscreen_logAndOpenQS() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ doReturn(true).when(mStatusBar).isKeyguardShowing();
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+
+ GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
+ MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
+ gestureListener.onFling(start, end, 0, 1000);
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ verify(mStatusBar).animateExpandSettingsPanel(null);
+ }
+
+ @Test
+ public void testSwipeDown_logAndOpenNotificationShade() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ doReturn(false).when(mStatusBar).isKeyguardShowing();
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+
+ GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
+ MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
+ gestureListener.onFling(start, end, 0, 1000);
verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ verify(mStatusBar).animateExpandNotificationsPanel();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 3130e977dc83..c54347063487 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -65,6 +65,7 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -124,6 +125,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
@Mock private Handler mHandler;
@Mock private UserTracker mUserTracker;
@Mock private SecureSettings mSecureSettings;
+ @Mock private StatusBar mStatusBar;
private TestableLooper mTestableLooper;
@@ -164,7 +166,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
mUiEventLogger,
mRingerModeTracker,
mSysUiState,
- mHandler
+ mHandler,
+ mStatusBar
);
mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
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 15cfee828293..3128db423a24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -9,8 +9,8 @@ import android.media.MediaDescription
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
-import android.provider.Settings
import android.os.Bundle
+import android.provider.Settings
import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -20,6 +20,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
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.capture
import com.android.systemui.util.mockito.eq
@@ -86,6 +87,8 @@ class MediaDataManagerTest : SysuiTestCase() {
lateinit var mediaNotification: StatusBarNotification
@Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
private val clock = FakeSystemClock()
+ @Mock private lateinit var tunerService: TunerService
+ @Captor lateinit var tunableCaptor: ArgumentCaptor<TunerService.Tunable>
private val originalSmartspaceSetting = Settings.Secure.getInt(context.contentResolver,
Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1)
@@ -114,8 +117,11 @@ class MediaDataManagerTest : SysuiTestCase() {
smartspaceMediaDataProvider = smartspaceMediaDataProvider,
useMediaResumption = true,
useQsMediaPlayer = true,
- systemClock = clock
+ systemClock = clock,
+ tunerService = tunerService
)
+ verify(tunerService).addTunable(capture(tunableCaptor),
+ eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
session = MediaSession(context, "MediaDataManagerTestSession")
mediaNotification = SbnBuilder().run {
setPkg(PACKAGE_NAME)
@@ -364,6 +370,9 @@ class MediaDataManagerTest : SysuiTestCase() {
fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_callsRemoveListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
smartspaceMediaDataProvider.onTargetsAvailable(listOf())
+ foregroundExecutor.advanceClockToLast()
+ foregroundExecutor.runAllReady()
+
verify(listener).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(false))
}
@@ -372,6 +381,8 @@ class MediaDataManagerTest : SysuiTestCase() {
// WHEN media recommendation setting is off
Settings.Secure.putInt(context.contentResolver,
Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0)
+ tunableCaptor.value.onTuningChanged(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, "0")
+
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
// THEN smartspace signal is ignored
@@ -380,6 +391,24 @@ class MediaDataManagerTest : SysuiTestCase() {
}
@Test
+ fun testMediaRecommendationDisabled_removesSmartspaceData() {
+ // GIVEN a media recommendation card is present
+ smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+ verify(listener).onSmartspaceMediaDataLoaded(eq(KEY_MEDIA_SMARTSPACE), anyObject(),
+ anyBoolean())
+
+ // WHEN the media recommendation setting is turned off
+ Settings.Secure.putInt(context.contentResolver,
+ Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0)
+ tunableCaptor.value.onTuningChanged(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, "0")
+
+ // THEN listeners are notified
+ foregroundExecutor.advanceClockToLast()
+ foregroundExecutor.runAllReady()
+ verify(listener).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(true))
+ }
+
+ @Test
fun testOnMediaDataChanged_updatesLastActiveTime() {
val currentTime = clock.elapsedRealtime()
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
index 5f4d90b3666f..f6264ffc6a70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
@@ -49,6 +49,7 @@ import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubble;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -213,6 +214,7 @@ public class LaunchConversationActivityTest extends SysuiTestCase {
verify(mBubblesManager, never()).expandStackAndSelectBubble(any(NotificationEntry.class));
}
+ @Ignore
@Test
public void testBubbleWithNoNotifOpensBubble() throws Exception {
Bubble bubble = mock(Bubble.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 2e0827f24bf8..fa25c3f1e005 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -289,6 +289,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Test
public void testIconScrollXAfterTranslationAndReset() throws Exception {
+ mGroupRow.setDismissUsingRowTranslationX(false);
mGroupRow.setTranslation(50);
assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 9e939eefa705..2d51683c8ae5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -84,7 +84,6 @@ import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
@@ -675,21 +674,6 @@ public class NotificationPanelViewTest extends SysuiTestCase {
verify(mTapAgainViewController).show();
}
- @Test
- public void testNotificationClipping_isAlignedWithNotificationScrimInSplitShade() {
- mStatusBarStateController.setState(SHADE);
- QS qs = mock(QS.class);
- when(qs.getHeader()).thenReturn(mock(View.class));
- mNotificationPanelViewController.mQs = qs;
- enableSplitShade();
-
- // hacky way to refresh notification scrim top with non-zero qsPanelBottom value
- mNotificationPanelViewController.setTransitionToFullShadeAmount(200, false, 0);
-
- verify(mAmbientState)
- .setNotificationScrimTop(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE);
- }
-
private FalsingManager.FalsingTapListener getFalsingTapListener() {
for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
listener.onViewAttachedToWindow(mView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index b2efd682bc62..cbc7c6dd0447 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -777,6 +777,12 @@ public class StatusBarTest extends SysuiTestCase {
}
@Test
+ public void testDumpBarTransitions_DoesNotCrash() {
+ StatusBar.dumpBarTransitions(
+ new PrintWriter(new ByteArrayOutputStream()), "var", /* transitions= */ null);
+ }
+
+ @Test
@RunWithLooper(setAsMainLooper = true)
public void testUpdateKeyguardState_DoesNotCrash() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
@@ -843,12 +849,14 @@ public class StatusBarTest extends SysuiTestCase {
// By default, showKeyguardImpl sets state to KEYGUARD.
mStatusBar.showKeyguardImpl();
- verify(mStatusBarStateController).setState(eq(StatusBarState.KEYGUARD));
+ verify(mStatusBarStateController).setState(
+ eq(StatusBarState.KEYGUARD), eq(false) /* force */);
// If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER.
when(mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true);
mStatusBar.showKeyguardImpl();
- verify(mStatusBarStateController).setState(eq(StatusBarState.FULLSCREEN_USER_SWITCHER));
+ verify(mStatusBarStateController).setState(
+ eq(StatusBarState.FULLSCREEN_USER_SWITCHER), eq(false) /* force */);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 6e2e4cb9ecfa..77bfc19820e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -287,6 +287,7 @@ public class BubblesTest extends SysuiTestCase {
// TODO: Fix
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
+ mPositioner.setMaxBubbles(5);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 9339f81940d9..d0bced8b190d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -232,6 +232,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
+ mPositioner.setMaxBubbles(5);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
index 24a7cd5c89ac..6edc373d2926 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
@@ -22,9 +22,11 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.view.WindowManager;
+import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.BubblePositioner;
public class TestableBubblePositioner extends BubblePositioner {
+ private int mMaxBubbles;
public TestableBubblePositioner(Context context,
WindowManager windowManager) {
@@ -33,5 +35,15 @@ public class TestableBubblePositioner extends BubblePositioner {
updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
new Rect(0, 0, 500, 1000));
+ mMaxBubbles = context.getResources().getInteger(R.integer.bubbles_max_rendered);
+ }
+
+ public void setMaxBubbles(int max) {
+ mMaxBubbles = max;
+ }
+
+ @Override
+ public int getMaxBubbles() {
+ return mMaxBubbles;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 1dd0b21bda30..ff15d0151ea4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -37,6 +37,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
@@ -106,6 +107,7 @@ public class WMShellTest extends SysuiTestCase {
verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class));
+ verify(mOneHanded).registerEventCallback(any(OneHandedEventCallback.class));
}
@Test
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 83dfe8ed2576..05131d44b01e 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -454,19 +454,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}).cancelTimeout();
}, FgThread.getExecutor()).whenComplete(uncheckExceptions((association, err) -> {
-
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- if (err == null) {
- addAssociation(association);
- } else {
- Slog.e(LOG_TAG, "Failed to discover device(s)", err);
- callback.onFailure("No devices found: " + err.getMessage());
- }
- cleanup();
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
+ if (err == null) {
+ addAssociation(association);
+ } else {
+ Slog.e(LOG_TAG, "Failed to discover device(s)", err);
+ callback.onFailure("No devices found: " + err.getMessage());
}
+ cleanup();
}));
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0802123b39da..5e388d94869d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1877,7 +1877,6 @@ public final class ActiveServices {
active.mNumActive++;
}
r.isForeground = true;
- r.mLogEntering = true;
// The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
// be deferred, make a copy of mAllowStartForeground and
// mAllowWhileInUsePermissionInFgs.
@@ -1903,6 +1902,9 @@ public final class ActiveServices {
AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
registerAppOpCallbackLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
+ logFGSStateChangeLocked(r,
+ FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
+ 0);
}
// Even if the service is already a FGS, we need to update the notification,
// so we need to call it again.
@@ -1958,6 +1960,7 @@ public final class ActiveServices {
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
r.mFgsExitTime > r.mFgsEnterTime
? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
+ r.mFgsNotificationWasDeferred = false;
resetFgsRestrictionLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
if (r.app != null) {
@@ -2162,6 +2165,7 @@ public final class ActiveServices {
}
r.fgDisplayTime = when;
r.mFgsNotificationDeferred = true;
+ r.mFgsNotificationWasDeferred = true;
r.mFgsNotificationShown = false;
mPendingFgsNotifications.add(r);
if (DEBUG_FOREGROUND_SERVICE) {
@@ -2205,11 +2209,6 @@ public final class ActiveServices {
Slog.d(TAG_SERVICE, " - service no longer running/fg, ignoring");
}
}
- // Regardless of whether we needed to post the notification or the
- // service is no longer running, we may not have logged its FGS
- // transition yet depending on the timing and API sequence that led
- // to this point - so make sure to do so.
- maybeLogFGSStateEnteredLocked(r);
}
}
if (DEBUG_FOREGROUND_SERVICE) {
@@ -2252,16 +2251,6 @@ public final class ActiveServices {
}
}
- private void maybeLogFGSStateEnteredLocked(ServiceRecord r) {
- if (r.mLogEntering) {
- logFGSStateChangeLocked(r,
- FrameworkStatsLog
- .FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
- 0);
- r.mLogEntering = false;
- }
- }
-
/**
* Callback from NotificationManagerService whenever it posts a notification
* associated with a foreground service. This is the unified handling point
@@ -2280,9 +2269,7 @@ public final class ActiveServices {
&& id == sr.foregroundId
&& sr.appInfo.packageName.equals(pkg)) {
// Found it. If 'shown' is false, it means that the notification
- // subsystem will not be displaying it yet, so all we do is log
- // the "fgs entered" transition noting deferral, then we're done.
- maybeLogFGSStateEnteredLocked(sr);
+ // subsystem will not be displaying it yet.
if (shown) {
if (DEBUG_FOREGROUND_SERVICE) {
Slog.d(TAG_SERVICE, "Notification shown; canceling deferral of "
@@ -4240,6 +4227,7 @@ public final class ActiveServices {
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
+ r.mFgsNotificationWasDeferred = false;
resetFgsRestrictionLocked(r);
// Clear start entries.
@@ -6263,7 +6251,7 @@ public final class ActiveServices {
? r.mRecentCallerApplicationInfo.targetSdkVersion : 0,
r.mInfoTempFgsAllowListReason != null
? r.mInfoTempFgsAllowListReason.mCallingUid : INVALID_UID,
- r.mFgsNotificationDeferred,
+ r.mFgsNotificationWasDeferred,
r.mFgsNotificationShown,
durationMs,
r.mStartForegroundCount,
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 3ba07af710c2..141f081ab9a7 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -109,7 +109,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
boolean fgWaiting; // is a timeout for going foreground already scheduled?
boolean isNotAppComponentUsage; // is service binding not considered component/package usage?
boolean isForeground; // is service currently in foreground mode?
- boolean mLogEntering; // need to report fgs transition once deferral policy is known
int foregroundId; // Notification ID of last foreground req.
Notification foregroundNoti; // Notification record of foreground state.
long fgDisplayTime; // time at which the FGS notification should become visible
@@ -167,8 +166,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
long mFgsEnterTime = 0;
// The uptime when the service exits FGS state.
long mFgsExitTime = 0;
- // FGS notification was deferred.
+ // FGS notification is deferred.
boolean mFgsNotificationDeferred;
+ // FGS notification was deferred.
+ boolean mFgsNotificationWasDeferred;
// FGS notification was shown before the FGS finishes, or it wasn't deferred in the first place.
boolean mFgsNotificationShown;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 104bc9b2c8d1..99a33e4462e2 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -848,7 +848,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return mAttributionFlags;
}
- /** @return attributoin chiang id for the access */
+ /** @return attribution chain id for the access */
public int getAttributionChainId() {
return mAttributionChainId;
}
@@ -912,7 +912,8 @@ public class AppOpsService extends IAppOpsService.Stub {
proxyAttributionTag, uidState, flags);
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
- tag, uidState, flags, accessTime);
+ tag, uidState, flags, accessTime, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
+ AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
}
/**
@@ -1053,9 +1054,9 @@ public class AppOpsService extends IAppOpsService.Stub {
event.numUnfinishedStarts++;
if (isStarted) {
- // TODO: Consider storing the attribution chain flags and id
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
- parent.packageName, tag, uidState, flags, startTime);
+ parent.packageName, tag, uidState, flags, startTime, attributionFlags,
+ attributionChainId);
}
}
@@ -1112,7 +1113,8 @@ public class AppOpsService extends IAppOpsService.Stub {
mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
parent.packageName, tag, event.getUidState(),
- event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration());
+ event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
+ event.getAttributionFlags(), event.getAttributionChainId());
if (!isPausing) {
mInProgressStartOpEventPool.release(event);
@@ -1215,7 +1217,8 @@ public class AppOpsService extends IAppOpsService.Stub {
event.mStartElapsedTime = SystemClock.elapsedRealtime();
event.mStartTime = startTime;
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
- parent.packageName, tag, event.mUidState, event.mFlags, startTime);
+ parent.packageName, tag, event.mUidState, event.mFlags, startTime,
+ event.getAttributionFlags(), event.getAttributionChainId());
if (shouldSendActive) {
scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName,
tag, true, event.getAttributionFlags(), event.getAttributionChainId());
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 10cfddfb3ae4..49469cc8a597 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -26,6 +26,7 @@ import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
+import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
@@ -156,8 +157,11 @@ final class DiscreteRegistry {
private static final String ATTR_NOTE_DURATION = "nd";
private static final String ATTR_UID_STATE = "us";
private static final String ATTR_FLAGS = "f";
+ private static final String ATTR_ATTRIBUTION_FLAGS = "af";
+ private static final String ATTR_CHAIN_ID = "ci";
- private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;
+ private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED
+ | OP_FLAG_TRUSTED_PROXY;
// Lock for read/write access to on disk state
private final Object mOnDiskLock = new Object();
@@ -227,13 +231,14 @@ final class DiscreteRegistry {
void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime,
- long accessDuration) {
+ long accessDuration, @AppOpsManager.AttributionFlags int attributionFlags,
+ int attributionChainId) {
if (!isDiscreteOp(op, flags)) {
return;
}
synchronized (mInMemoryLock) {
mDiscreteOps.addDiscreteAccess(op, uid, packageName, attributionTag, flags, uidState,
- accessTime, accessDuration);
+ accessTime, accessDuration, attributionFlags, attributionChainId);
}
}
@@ -383,9 +388,10 @@ final class DiscreteRegistry {
void addDiscreteAccess(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
- @AppOpsManager.UidState int uidState, long accessTime, long accessDuration) {
+ @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, attributionTag, flags,
- uidState, accessTime, accessDuration);
+ uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
}
private void filter(long beginTimeMillis, long endTimeMillis,
@@ -613,9 +619,10 @@ final class DiscreteRegistry {
void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
- long accessTime, long accessDuration) {
+ long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
getOrCreateDiscretePackageOps(packageName).addDiscreteAccess(op, attributionTag, flags,
- uidState, accessTime, accessDuration);
+ uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
}
private DiscretePackageOps getOrCreateDiscretePackageOps(String packageName) {
@@ -680,9 +687,10 @@ final class DiscreteRegistry {
void addDiscreteAccess(int op, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
- long accessTime, long accessDuration) {
+ long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
getOrCreateDiscreteOp(op).addDiscreteAccess(attributionTag, flags, uidState, accessTime,
- accessDuration);
+ accessDuration, attributionFlags, attributionChainId);
}
void merge(DiscretePackageOps other) {
@@ -823,37 +831,39 @@ final class DiscreteRegistry {
for (int j = 0; j < n; j++) {
DiscreteOpEvent event = list.get(j);
list.set(j, new DiscreteOpEvent(event.mNoteTime - offset, event.mNoteDuration,
- event.mUidState, event.mOpFlag));
+ event.mUidState, event.mOpFlag, event.mAttributionFlags,
+ event.mAttributionChainId));
}
}
}
void addDiscreteAccess(@Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
- long accessTime, long accessDuration) {
+ long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
attributionTag);
- accessTime = accessTime / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
- accessDuration = accessDuration == -1 ? -1
- : (accessDuration + sDiscreteHistoryQuantization - 1)
- / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
int nAttributedOps = attributedOps.size();
int i = nAttributedOps;
for (; i > 0; i--) {
DiscreteOpEvent previousOp = attributedOps.get(i - 1);
- if (previousOp.mNoteTime < accessTime) {
+ if (discretizeTimeStamp(previousOp.mNoteTime) < discretizeTimeStamp(accessTime)) {
break;
}
- if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
- if (accessDuration != previousOp.mNoteDuration) {
+ if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState
+ && previousOp.mAttributionFlags == attributionFlags
+ && previousOp.mAttributionChainId == attributionChainId) {
+ if (discretizeDuration(accessDuration) != discretizeDuration(
+ previousOp.mNoteDuration)) {
break;
} else {
return;
}
}
}
- attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
+ attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags,
+ attributionFlags, attributionChainId));
}
private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) {
@@ -875,7 +885,8 @@ final class DiscreteRegistry {
for (int j = 0; j < nEvents; j++) {
DiscreteOpEvent event = events.get(j);
result.addDiscreteAccess(op, uid, packageName, tag, event.mUidState,
- event.mOpFlag, event.mNoteTime, event.mNoteDuration);
+ event.mOpFlag, discretizeTimeStamp(event.mNoteTime),
+ discretizeDuration(event.mNoteDuration));
}
}
}
@@ -932,11 +943,15 @@ final class DiscreteRegistry {
-1);
int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
+ int attributionFlags = parser.getAttributeInt(null,
+ ATTR_ATTRIBUTION_FLAGS, AppOpsManager.ATTRIBUTION_FLAGS_NONE);
+ int attributionChainId = parser.getAttributeInt(null, ATTR_CHAIN_ID,
+ AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
if (noteTime + noteDuration < beginTimeMillis) {
continue;
}
DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
- uidState, opFlags);
+ uidState, opFlags, attributionFlags, attributionChainId);
events.add(event);
}
}
@@ -952,13 +967,18 @@ final class DiscreteRegistry {
final long mNoteDuration;
final @AppOpsManager.UidState int mUidState;
final @AppOpsManager.OpFlags int mOpFlag;
+ final @AppOpsManager.AttributionFlags int mAttributionFlags;
+ final int mAttributionChainId;
DiscreteOpEvent(long noteTime, long noteDuration, @AppOpsManager.UidState int uidState,
- @AppOpsManager.OpFlags int opFlag) {
+ @AppOpsManager.OpFlags int opFlag,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
mNoteTime = noteTime;
mNoteDuration = noteDuration;
mUidState = uidState;
mOpFlag = opFlag;
+ mAttributionFlags = attributionFlags;
+ mAttributionChainId = attributionChainId;
}
private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
@@ -969,13 +989,19 @@ final class DiscreteRegistry {
pw.print("-");
pw.print(flagsToString(mOpFlag));
pw.print("] at ");
- date.setTime(mNoteTime);
+ date.setTime(discretizeTimeStamp(mNoteTime));
pw.print(sdf.format(date));
if (mNoteDuration != -1) {
pw.print(" for ");
- pw.print(mNoteDuration);
+ pw.print(discretizeDuration(mNoteDuration));
pw.print(" milliseconds ");
}
+ if (mAttributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE) {
+ pw.print(" attribution flags=");
+ pw.print(mAttributionFlags);
+ pw.print(" with chainId=");
+ pw.print(mAttributionChainId);
+ }
pw.println();
}
@@ -984,6 +1010,12 @@ final class DiscreteRegistry {
if (mNoteDuration != -1) {
out.attributeLong(null, ATTR_NOTE_DURATION, mNoteDuration);
}
+ if (mAttributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE) {
+ out.attributeInt(null, ATTR_ATTRIBUTION_FLAGS, mAttributionFlags);
+ }
+ if (mAttributionChainId != AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE) {
+ out.attributeInt(null, ATTR_CHAIN_ID, mAttributionChainId);
+ }
out.attributeInt(null, ATTR_UID_STATE, mUidState);
out.attributeInt(null, ATTR_FLAGS, mOpFlag);
}
@@ -1055,6 +1087,16 @@ final class DiscreteRegistry {
return true;
}
+ private static long discretizeTimeStamp(long timeStamp) {
+ return timeStamp / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+
+ }
+
+ private static long discretizeDuration(long duration) {
+ return duration == -1 ? -1 : (duration + sDiscreteHistoryQuantization - 1)
+ / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+ }
+
void setDebugMode(boolean debugMode) {
this.mDebugMode = debugMode;
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 8b72be78a7f6..dd5df503d936 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -487,7 +487,8 @@ final class HistoricalRegistry {
void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
- long accessTime) {
+ long accessTime, @AppOpsManager.AttributionFlags int attributionFlags,
+ int attributionChainId) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -499,7 +500,7 @@ final class HistoricalRegistry {
attributionTag, uidState, flags, 1);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
- flags, uidState, accessTime, -1);
+ flags, uidState, accessTime, -1, attributionFlags, attributionChainId);
}
}
}
@@ -521,7 +522,8 @@ final class HistoricalRegistry {
void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
- long eventStartTime, long increment) {
+ long eventStartTime, long increment,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -532,7 +534,8 @@ final class HistoricalRegistry {
System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
attributionTag, uidState, flags, increment);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
- flags, uidState, eventStartTime, increment);
+ flags, uidState, eventStartTime, increment, attributionFlags,
+ attributionChainId);
}
}
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index b9e97e89051b..aeb1893c78b6 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -65,7 +65,7 @@ import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
-import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
+import android.location.LocationManagerInternal.LocationPackageTagsListener;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
@@ -93,6 +93,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.location.eventlog.LocationEventLog;
@@ -259,7 +260,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
new CopyOnWriteArrayList<>();
@GuardedBy("mLock")
- @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener;
+ @Nullable LocationPackageTagsListener mLocationTagsChangedListener;
LocationManagerService(Context context, Injector injector) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
@@ -1363,32 +1364,28 @@ public class LocationManagerService extends ILocationManager.Stub implements
refreshAppOpsRestrictions(UserHandle.USER_ALL);
}
- OnProviderLocationTagsChangeListener listener;
- synchronized (mLock) {
- listener = mOnProviderLocationTagsChangeListener;
- }
-
- if (listener != null) {
- if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
- || !Objects.equals(oldState.identity, newState.identity)) {
- if (oldState.identity != null) {
- listener.onLocationTagsChanged(
- new LocationManagerInternal.LocationTagInfo(
- oldState.identity.getUid(),
- oldState.identity.getPackageName(),
- Collections.emptySet()));
- }
- if (newState.identity != null) {
- ArraySet<String> attributionTags = new ArraySet<>(
- newState.extraAttributionTags.size() + 1);
- attributionTags.addAll(newState.extraAttributionTags);
- attributionTags.add(newState.identity.getAttributionTag());
-
- listener.onLocationTagsChanged(
- new LocationManagerInternal.LocationTagInfo(
- newState.identity.getUid(),
- newState.identity.getPackageName(),
- attributionTags));
+ if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
+ || !Objects.equals(oldState.identity, newState.identity)) {
+ // since we're potentially affecting the tag lists for two different uids, acquire the
+ // lock to ensure providers cannot change while we're looping over the providers
+ // multiple times, which could lead to inconsistent results.
+ synchronized (mLock) {
+ LocationPackageTagsListener listener = mLocationTagsChangedListener;
+ if (listener != null) {
+ int oldUid = oldState.identity != null ? oldState.identity.getUid() : -1;
+ int newUid = newState.identity != null ? newState.identity.getUid() : -1;
+ if (oldUid != -1) {
+ PackageTagsList tags = calculateAppOpsLocationSourceTags(oldUid);
+ FgThread.getHandler().post(
+ () -> listener.onLocationPackageTagsChanged(oldUid, tags));
+ }
+ // if the new app id is the same as the old app id, no need to invoke the
+ // listener twice, it's already been taken care of
+ if (newUid != -1 && newUid != oldUid) {
+ PackageTagsList tags = calculateAppOpsLocationSourceTags(newUid);
+ FgThread.getHandler().post(
+ () -> listener.onLocationPackageTagsChanged(newUid, tags));
+ }
}
}
}
@@ -1436,6 +1433,31 @@ public class LocationManagerService extends ILocationManager.Stub implements
userId);
}
+ PackageTagsList calculateAppOpsLocationSourceTags(int uid) {
+ PackageTagsList.Builder builder = new PackageTagsList.Builder();
+ for (LocationProviderManager manager : mProviderManagers) {
+ AbstractLocationProvider.State managerState = manager.getState();
+ if (managerState.identity == null) {
+ continue;
+ }
+ if (managerState.identity.getUid() != uid) {
+ continue;
+ }
+
+ builder.add(managerState.identity.getPackageName(), managerState.extraAttributionTags);
+ if (managerState.extraAttributionTags.isEmpty()
+ || managerState.identity.getAttributionTag() != null) {
+ builder.add(managerState.identity.getPackageName(),
+ managerState.identity.getAttributionTag());
+ } else {
+ Log.e(TAG, manager.getName() + " provider has specified a null attribution tag and "
+ + "a non-empty set of extra attribution tags - dropping the null "
+ + "attribution tag");
+ }
+ }
+ return builder.build();
+ }
+
private class LocalService extends LocationManagerInternal {
LocalService() {}
@@ -1506,10 +1528,29 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
@Override
- public void setOnProviderLocationTagsChangeListener(
- @Nullable OnProviderLocationTagsChangeListener listener) {
+ public void setLocationPackageTagsListener(
+ @Nullable LocationPackageTagsListener listener) {
synchronized (mLock) {
- mOnProviderLocationTagsChangeListener = listener;
+ mLocationTagsChangedListener = listener;
+
+ // calculate initial tag list and send to listener
+ if (listener != null) {
+ ArraySet<Integer> uids = new ArraySet<>(mProviderManagers.size());
+ for (LocationProviderManager manager : mProviderManagers) {
+ CallerIdentity identity = manager.getIdentity();
+ if (identity != null) {
+ uids.add(identity.getUid());
+ }
+ }
+
+ for (int uid : uids) {
+ PackageTagsList tags = calculateAppOpsLocationSourceTags(uid);
+ if (!tags.isEmpty()) {
+ FgThread.getHandler().post(
+ () -> listener.onLocationPackageTagsChanged(uid, tags));
+ }
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
index 1da45bd49767..eb7b77a2234f 100644
--- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
@@ -263,10 +263,10 @@ public abstract class AbstractLocationProvider {
}
/**
- * The current allowed state of this provider.
+ * The current state of the provider.
*/
- public final boolean isAllowed() {
- return mInternalState.get().state.allowed;
+ public final State getState() {
+ return mInternalState.get().state;
}
/**
@@ -277,13 +277,6 @@ public abstract class AbstractLocationProvider {
}
/**
- * The current provider properties of this provider.
- */
- public final @Nullable ProviderProperties getProperties() {
- return mInternalState.get().state.properties;
- }
-
- /**
* Call this method to report a change in provider properties.
*/
protected void setProperties(@Nullable ProviderProperties properties) {
@@ -291,13 +284,6 @@ public abstract class AbstractLocationProvider {
}
/**
- * The current identity of this provider.
- */
- public final @Nullable CallerIdentity getIdentity() {
- return mInternalState.get().state.identity;
- }
-
- /**
* Call this method to report a change in the provider's identity.
*/
protected void setIdentity(@Nullable CallerIdentity identity) {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 4f8b87b51294..8d335b83d99c 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1407,12 +1407,16 @@ public class LocationProviderManager extends
return mName;
}
+ public AbstractLocationProvider.State getState() {
+ return mProvider.getState();
+ }
+
public @Nullable CallerIdentity getIdentity() {
- return mProvider.getIdentity();
+ return mProvider.getState().identity;
}
public @Nullable ProviderProperties getProperties() {
- return mProvider.getProperties();
+ return mProvider.getState().properties;
}
public boolean hasProvider() {
@@ -2403,7 +2407,7 @@ public class LocationProviderManager extends
Preconditions.checkArgument(userId >= 0);
boolean enabled = mState == STATE_STARTED
- && mProvider.isAllowed()
+ && mProvider.getState().allowed
&& mSettingsHelper.isLocationEnabled(userId);
int index = mEnabled.indexOfKey(userId);
diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
index 021e8dbdb44e..22295fe7ba28 100644
--- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
@@ -21,9 +21,7 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
-import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
-import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
import com.android.internal.annotations.GuardedBy;
@@ -32,7 +30,6 @@ import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
-import java.util.Set;
/**
* Represents a location provider that may switch between a mock implementation and a real
@@ -290,21 +287,21 @@ public class MockableLocationProvider extends AbstractLocationProvider {
Preconditions.checkState(!Thread.holdsLock(mOwnerLock));
AbstractLocationProvider provider;
+ State providerState;
synchronized (mOwnerLock) {
provider = mProvider;
- pw.println("allowed=" + isAllowed());
- CallerIdentity identity = getIdentity();
- if (identity != null) {
- pw.println("identity=" + identity);
- }
- Set<String> extraAttributionTags = getExtraAttributionTags();
- if (!extraAttributionTags.isEmpty()) {
- pw.println("extra attribution tags=" + extraAttributionTags);
- }
- ProviderProperties properties = getProperties();
- if (properties != null) {
- pw.println("properties=" + properties);
- }
+ providerState = getState();
+ }
+
+ pw.println("allowed=" + providerState.allowed);
+ if (providerState.identity != null) {
+ pw.println("identity=" + providerState.identity);
+ }
+ if (!providerState.extraAttributionTags.isEmpty()) {
+ pw.println("extra attribution tags=" + providerState.extraAttributionTags);
+ }
+ if (providerState.properties != null) {
+ pw.println("properties=" + providerState.properties);
}
if (provider != null) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9370b14341dc..5b2c80903ce5 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1000,16 +1000,15 @@ public class LauncherAppsService extends SystemService {
intents[0].setSourceBounds(sourceBounds);
// Replace theme for splash screen
- final int splashScreenThemeResId =
- mShortcutServiceInternal.getShortcutStartingThemeResId(getCallingUserId(),
+ final String splashScreenThemeResName =
+ mShortcutServiceInternal.getShortcutStartingThemeResName(getCallingUserId(),
callingPackage, packageName, shortcutId, targetUserId);
- if (splashScreenThemeResId != 0) {
+ if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) {
if (startActivityOptions == null) {
startActivityOptions = new Bundle();
}
- startActivityOptions.putInt(KEY_SPLASH_SCREEN_THEME, splashScreenThemeResId);
+ startActivityOptions.putString(KEY_SPLASH_SCREEN_THEME, splashScreenThemeResName);
}
-
return startShortcutIntentsAsPublisher(
intents, packageName, featureId, startActivityOptions, targetUserId);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 1bd9e5eedb84..b4bd086af272 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -139,7 +139,7 @@ class ShortcutPackage extends ShortcutPackageItem {
private static final String ATTR_BITMAP_PATH = "bitmap-path";
private static final String ATTR_ICON_URI = "icon-uri";
private static final String ATTR_LOCUS_ID = "locus-id";
- private static final String ATTR_SPLASH_SCREEN_THEME_ID = "splash-screen-theme-id";
+ private static final String ATTR_SPLASH_SCREEN_THEME_NAME = "splash-screen-theme-name";
private static final String ATTR_PERSON_NAME = "name";
private static final String ATTR_PERSON_URI = "uri";
@@ -1799,7 +1799,7 @@ class ShortcutPackage extends ShortcutPackageItem {
ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
ShortcutService.writeAttr(out, ATTR_TITLE_RES_ID, si.getTitleResId());
ShortcutService.writeAttr(out, ATTR_TITLE_RES_NAME, si.getTitleResName());
- ShortcutService.writeAttr(out, ATTR_SPLASH_SCREEN_THEME_ID, si.getStartingThemeResId());
+ ShortcutService.writeAttr(out, ATTR_SPLASH_SCREEN_THEME_NAME, si.getStartingThemeResName());
ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId());
ShortcutService.writeAttr(out, ATTR_TEXT_RES_NAME, si.getTextResName());
@@ -2010,7 +2010,7 @@ class ShortcutPackage extends ShortcutPackageItem {
String bitmapPath;
String iconUri;
final String locusIdString;
- int splashScreenThemeResId;
+ String splashScreenThemeResName;
int backupVersionCode;
ArraySet<String> categories = null;
ArrayList<Person> persons = new ArrayList<>();
@@ -2021,8 +2021,8 @@ class ShortcutPackage extends ShortcutPackageItem {
title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
titleResId = ShortcutService.parseIntAttribute(parser, ATTR_TITLE_RES_ID);
titleResName = ShortcutService.parseStringAttribute(parser, ATTR_TITLE_RES_NAME);
- splashScreenThemeResId = ShortcutService.parseIntAttribute(parser,
- ATTR_SPLASH_SCREEN_THEME_ID);
+ splashScreenThemeResName = ShortcutService.parseStringAttribute(parser,
+ ATTR_SPLASH_SCREEN_THEME_NAME);
text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT);
textResId = ShortcutService.parseIntAttribute(parser, ATTR_TEXT_RES_ID);
textResName = ShortcutService.parseStringAttribute(parser, ATTR_TEXT_RES_NAME);
@@ -2117,7 +2117,7 @@ class ShortcutPackage extends ShortcutPackageItem {
rank, extras, lastChangedTimestamp, flags,
iconResId, iconResName, bitmapPath, iconUri,
disabledReason, persons.toArray(new Person[persons.size()]), locusId,
- splashScreenThemeResId);
+ splashScreenThemeResName);
}
private static Intent parseIntent(TypedXmlPullParser parser)
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index c06f01a463ad..b86c50b23687 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -383,8 +383,11 @@ public class ShortcutParser {
final int textResId = sa.getResourceId(R.styleable.Shortcut_shortcutLongLabel, 0);
final int disabledMessageResId = sa.getResourceId(
R.styleable.Shortcut_shortcutDisabledMessage, 0);
- final int splashScreenTheme = sa.getResourceId(
+ final int splashScreenThemeResId = sa.getResourceId(
R.styleable.Shortcut_splashScreenTheme, 0);
+ final String splashScreenThemeResName = splashScreenThemeResId != 0
+ ? service.mContext.getResources().getResourceName(splashScreenThemeResId)
+ : null;
if (TextUtils.isEmpty(id)) {
Log.w(TAG, "android:shortcutId must be provided. activity=" + activity);
@@ -407,7 +410,7 @@ public class ShortcutParser {
rank,
iconResId,
enabled,
- splashScreenTheme);
+ splashScreenThemeResName);
} finally {
sa.recycle();
}
@@ -416,7 +419,7 @@ public class ShortcutParser {
private static ShortcutInfo createShortcutFromManifest(ShortcutService service,
@UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
int titleResId, int textResId, int disabledMessageResId,
- int rank, int iconResId, boolean enabled, int splashScreenTheme) {
+ int rank, int iconResId, boolean enabled, @Nullable String splashScreenThemeResName) {
final int flags =
(enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED)
@@ -456,7 +459,7 @@ public class ShortcutParser {
disabledReason,
null /* persons */,
null /* locusId */,
- splashScreenTheme);
+ splashScreenThemeResName);
}
private static String parseCategory(ShortcutService service, AttributeSet attrs) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5f1027797292..fcbf40e29933 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3536,7 +3536,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
- public int getShortcutStartingThemeResId(int launcherUserId,
+ @Nullable
+ public String getShortcutStartingThemeResName(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull String shortcutId, int userId) {
Objects.requireNonNull(callingPackage, "callingPackage");
@@ -3553,11 +3554,11 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutPackage p = getUserShortcutsLocked(userId)
.getPackageShortcutsIfExists(packageName);
if (p == null) {
- return 0;
+ return null;
}
final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
- return shortcutInfo != null ? shortcutInfo.getStartingThemeResId() : 0;
+ return shortcutInfo != null ? shortcutInfo.getStartingThemeResName() : null;
}
}
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 94005b2c8361..22c370ef4dbe 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -33,14 +33,15 @@ import android.content.pm.ResolveInfo;
import android.location.LocationManagerInternal;
import android.net.Uri;
import android.os.IBinder;
+import android.os.PackageTagsList;
import android.os.Process;
import android.os.UserHandle;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.DecFunction;
@@ -52,9 +53,9 @@ import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.LocalServices;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -67,11 +68,10 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
"android:activity_recognition_allow_listed_tags";
private static final String ACTIVITY_RECOGNITION_TAGS_SEPARATOR = ";";
- private static ArraySet<String> sExpectedTags = new ArraySet<>(new String[] {
+ private static final ArraySet<String> sExpectedTags = new ArraySet<>(new String[] {
"awareness_provider", "activity_recognition_provider", "network_location_provider",
"network_location_calibration", "fused_location_provider", "geofencer_provider"});
-
@NonNull
private final Object mLock = new Object();
@@ -96,13 +96,22 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
*/
@GuardedBy("mLock - writes only - see above")
@NonNull
- private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
+ private final ConcurrentHashMap<Integer, PackageTagsList> mLocationTags =
new ConcurrentHashMap<>();
+ // location tags can vary per uid - but we merge all tags under an app id into the final data
+ // structure above
+ @GuardedBy("mLock")
+ private final SparseArray<PackageTagsList> mPerUidLocationTags = new SparseArray<>();
+
+ // activity recognition currently only grabs tags from the APK manifest. we know that the
+ // manifest is the same for all users, so there's no need to track variations in tags across
+ // different users. if that logic ever changes, this might need to behave more like location
+ // tags above.
@GuardedBy("mLock - writes only - see above")
@NonNull
- private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>>
- mActivityRecognitionTags = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap<Integer, PackageTagsList> mActivityRecognitionTags =
+ new ConcurrentHashMap<>();
public AppOpsPolicy(@NonNull Context context) {
mContext = context;
@@ -112,13 +121,28 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
final LocationManagerInternal locationManagerInternal = LocalServices.getService(
LocationManagerInternal.class);
- locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> {
- synchronized (mLock) {
- updateAllowListedTagsForPackageLocked(providerTagInfo.getUid(),
- providerTagInfo.getPackageName(), providerTagInfo.getTags(),
- mLocationTags);
- }
- });
+ locationManagerInternal.setLocationPackageTagsListener(
+ (uid, packageTagsList) -> {
+ synchronized (mLock) {
+ if (packageTagsList.isEmpty()) {
+ mPerUidLocationTags.remove(uid);
+ } else {
+ mPerUidLocationTags.set(uid, packageTagsList);
+ }
+
+ int appId = UserHandle.getAppId(uid);
+ PackageTagsList.Builder appIdTags = new PackageTagsList.Builder(1);
+ int size = mPerUidLocationTags.size();
+ for (int i = 0; i < size; i++) {
+ if (UserHandle.getAppId(mPerUidLocationTags.keyAt(i)) == appId) {
+ appIdTags.add(mPerUidLocationTags.valueAt(i));
+ }
+ }
+
+ updateAllowListedTagsForPackageLocked(appId, appIdTags.build(),
+ mLocationTags);
+ }
+ });
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -306,95 +330,30 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
}
final String tagsList = resolvedService.serviceInfo.metaData.getString(
ACTIVITY_RECOGNITION_TAGS);
- if (tagsList != null) {
- final String[] tags = tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR);
+ if (!TextUtils.isEmpty(tagsList)) {
+ PackageTagsList packageTagsList = new PackageTagsList.Builder(1).add(
+ resolvedService.serviceInfo.packageName,
+ Arrays.asList(tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR))).build();
synchronized (mLock) {
updateAllowListedTagsForPackageLocked(
- resolvedService.serviceInfo.applicationInfo.uid,
- resolvedService.serviceInfo.packageName, new ArraySet<>(tags),
+ UserHandle.getAppId(resolvedService.serviceInfo.applicationInfo.uid),
+ packageTagsList,
mActivityRecognitionTags);
}
}
}
- private static void updateAllowListedTagsForPackageLocked(int uid, String packageName,
- Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String,
- ArraySet<String>>> datastore) {
- final int appId = UserHandle.getAppId(uid);
- // We make a copy of the per UID state to limit our mutation to one
- // operation in the underlying concurrent data structure.
- ArrayMap<String, ArraySet<String>> appIdTags = datastore.get(appId);
- if (appIdTags != null) {
- appIdTags = new ArrayMap<>(appIdTags);
- }
-
- ArraySet<String> packageTags = (appIdTags != null) ? appIdTags.get(packageName) : null;
- if (packageTags != null) {
- packageTags = new ArraySet<>(packageTags);
- }
-
- if (allowListedTags != null && !allowListedTags.isEmpty()) {
- if (packageTags != null) {
- packageTags.clear();
- packageTags.addAll(allowListedTags);
- } else {
- packageTags = new ArraySet<>(allowListedTags);
- }
- if (appIdTags == null) {
- appIdTags = new ArrayMap<>();
- }
-
- // Remove any invalid tags
- boolean nullRemoved = packageTags.remove(null);
- boolean nullStrRemoved = packageTags.remove("null");
- boolean emptyRemoved = packageTags.remove("");
- if (nullRemoved || nullStrRemoved || emptyRemoved) {
- Log.e(LOG_TAG, "Attempted to add invalid source attribution tag, removed "
- + "null: " + nullRemoved + " removed \"null\": " + nullStrRemoved
- + " removed empty string: " + emptyRemoved);
- }
-
- appIdTags.put(packageName, packageTags);
- datastore.put(appId, appIdTags);
- } else if (appIdTags != null) {
- appIdTags.remove(packageName);
- if (!appIdTags.isEmpty()) {
- datastore.put(appId, appIdTags);
- } else {
- datastore.remove(appId);
- }
- }
+ private static void updateAllowListedTagsForPackageLocked(int appId,
+ PackageTagsList packageTagsList,
+ ConcurrentHashMap<Integer, PackageTagsList> datastore) {
+ datastore.put(appId, packageTagsList);
}
private static boolean isDatasourceAttributionTag(int uid, @NonNull String packageName,
- @NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String,
- ArraySet<String>>> mappedOps) {
+ @NonNull String attributionTag, @NonNull Map<Integer, PackageTagsList> mappedOps) {
// Only a single lookup from the underlying concurrent data structure
- final int appId = UserHandle.getAppId(uid);
- final ArrayMap<String, ArraySet<String>> appIdTags = mappedOps.get(appId);
- if (appIdTags != null) {
- final ArraySet<String> packageTags = appIdTags.get(packageName);
- if (packageTags != null && packageTags.contains(attributionTag)) {
- if (packageName.equals("com.google.android.gms")
- && !sExpectedTags.contains(attributionTag)) {
- Log.i("AppOpsDebugRemapping", packageName + " tag "
- + attributionTag + " in " + packageTags);
- }
- return true;
- }
- if (packageName.equals("com.google.android.gms")
- && sExpectedTags.contains(attributionTag)) {
- Log.i("AppOpsDebugRemapping", packageName + " tag " + attributionTag
- + " NOT in " + packageTags);
- }
- } else {
- if (packageName.equals("com.google.android.gms")) {
- Log.i("AppOpsDebugRemapping", "no package tags for uid " + uid
- + " package " + packageName);
- }
-
- }
- return false;
+ final PackageTagsList appIdTags = mappedOps.get(UserHandle.getAppId(uid));
+ return appIdTags != null && appIdTags.contains(packageName, attributionTag);
}
private static int resolveLocationOp(int code) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 71e31c3a10df..ac0665a37c0b 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -703,7 +703,7 @@ final class AccessibilityController {
Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
- mMagnifedViewport.onRotationChanged(displayContent.getPendingTransaction());
+ mMagnifedViewport.onRotationChanged();
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
}
@@ -858,7 +858,7 @@ final class AccessibilityController {
private final RectF mTempRectF = new RectF();
- private final Point mTempPoint = new Point();
+ private final Point mScreenSize = new Point();
private final Matrix mTempMatrix = new Matrix();
@@ -887,8 +887,8 @@ final class AccessibilityController {
if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
mCircularPath = new Path();
- mDisplay.getRealSize(mTempPoint);
- final int centerXY = mTempPoint.x / 2;
+ mDisplay.getRealSize(mScreenSize);
+ final int centerXY = mScreenSize.x / 2;
mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
} else {
mCircularPath = null;
@@ -917,9 +917,9 @@ final class AccessibilityController {
}
void recomputeBounds() {
- mDisplay.getRealSize(mTempPoint);
- final int screenWidth = mTempPoint.x;
- final int screenHeight = mTempPoint.y;
+ mDisplay.getRealSize(mScreenSize);
+ final int screenWidth = mScreenSize.x;
+ final int screenHeight = mScreenSize.y;
mMagnificationRegion.set(0, 0, 0, 0);
final Region availableBounds = mTempRegion1;
@@ -1052,7 +1052,7 @@ final class AccessibilityController {
|| windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
}
- void onRotationChanged(SurfaceControl.Transaction t) {
+ void onRotationChanged() {
// If we are showing the magnification border, hide it immediately so
// the user does not see strange artifacts during rotation. The screenshot
// used for rotation already has the border. After the rotation is complete
@@ -1066,7 +1066,7 @@ final class AccessibilityController {
mHandler.sendMessageDelayed(message, delay);
}
recomputeBounds();
- mWindow.updateSize(t);
+ mWindow.updateSize();
}
void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
@@ -1148,9 +1148,9 @@ final class AccessibilityController {
/* ignore */
}
mSurfaceControl = surfaceControl;
- mDisplay.getRealSize(mTempPoint);
+ mDisplay.getRealSize(mScreenSize);
mBlastBufferQueue = new BLASTBufferQueue(SURFACE_TITLE, mSurfaceControl,
- mTempPoint.x, mTempPoint.y, PixelFormat.RGBA_8888);
+ mScreenSize.x, mScreenSize.y, PixelFormat.RGBA_8888);
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
final int layer =
@@ -1224,10 +1224,11 @@ final class AccessibilityController {
}
}
- void updateSize(SurfaceControl.Transaction t) {
+ void updateSize() {
synchronized (mService.mGlobalLock) {
- mDisplay.getRealSize(mTempPoint);
- t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y);
+ mDisplay.getRealSize(mScreenSize);
+ mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y,
+ PixelFormat.RGBA_8888);
invalidate(mDirtyRect);
}
}
@@ -1296,8 +1297,8 @@ final class AccessibilityController {
pw.println(prefix
+ " mBounds= " + mBounds
+ " mDirtyRect= " + mDirtyRect
- + " mWidth= " + mSurfaceControl.getWidth()
- + " mHeight= " + mSurfaceControl.getHeight());
+ + " mWidth= " + mScreenSize.x
+ + " mHeight= " + mScreenSize.y);
}
private final class AnimationController extends Handler {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 45da45aafaba..8cc6a42be349 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -309,6 +309,7 @@ import android.view.animation.Animation;
import android.window.IRemoteTransition;
import android.window.SizeConfigurationBuckets;
import android.window.SplashScreen;
+import android.window.SplashScreenView;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -716,25 +717,29 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean startingMoved;
boolean mHandleExitSplashScreen;
- @TransferSplashScreenState int mTransferringSplashScreenState =
- TRANSFER_SPLASH_SCREEN_IDLE;
+ @TransferSplashScreenState
+ int mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
- // Idle, can be triggered to do transfer if needed.
+ /** Idle, can be triggered to do transfer if needed. */
static final int TRANSFER_SPLASH_SCREEN_IDLE = 0;
- // requesting a copy from shell.
+
+ /** Requesting a copy from shell. */
static final int TRANSFER_SPLASH_SCREEN_COPYING = 1;
- // attach the splash screen view to activity.
+
+ /** Attach the splash screen view to activity. */
static final int TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT = 2;
- // client has taken over splash screen view.
+
+ /** Client has taken over splash screen view. */
static final int TRANSFER_SPLASH_SCREEN_FINISH = 3;
- @IntDef(prefix = { "TRANSFER_SPLASH_SCREEN_" }, value = {
+ @IntDef(prefix = {"TRANSFER_SPLASH_SCREEN_"}, value = {
TRANSFER_SPLASH_SCREEN_IDLE,
TRANSFER_SPLASH_SCREEN_COPYING,
TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT,
TRANSFER_SPLASH_SCREEN_FINISH,
})
- @interface TransferSplashScreenState {}
+ @interface TransferSplashScreenState {
+ }
// How long we wait until giving up transfer splash screen.
private static final int TRANSFER_SPLASH_SCREEN_TIMEOUT = 2000;
@@ -2196,6 +2201,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
removeStartingWindowAnimation(false /* prepareAnimation */);
}
+ /**
+ * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} it should clean up any
+ * remaining reference to this {@link ActivityRecord}'s splash screen.
+ * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int)
+ * @see SplashScreenView#remove()
+ */
+ void cleanUpSplashScreen() {
+ // We only clean up the splash screen if we were supposed to handle it. If it was
+ // transferred to another activity, the next one will handle the clean up.
+ if (mHandleExitSplashScreen && !startingMoved
+ && (mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
+ || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_IDLE)) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Cleaning splash screen token=%s", this);
+ mAtmService.mTaskOrganizerController.onAppSplashScreenViewRemoved(getTask());
+ }
+ }
+
void removeStartingWindow() {
removeStartingWindowAnimation(true /* prepareAnimation */);
}
@@ -3343,6 +3365,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
task.cleanUpActivityReferences(this);
clearLastParentBeforePip();
+ // Clean up the splash screen if it was still displayed.
+ cleanUpSplashScreen();
+
deferRelaunchUntilPaused = false;
frozenBeforeDestroy = false;
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index eaebb6f1ce74..4acadb21b5e3 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -623,7 +623,11 @@ public class AppTransitionController {
siblings.add(current);
boolean canPromote = true;
- if (parent == null || !parent.canCreateRemoteAnimationTarget()) {
+ if (parent == null || !parent.canCreateRemoteAnimationTarget()
+ // We cannot promote the animation on Task's parent when the task is in
+ // clearing task in case the animating get stuck when performing the opening
+ // task that behind it.
+ || (current.asTask() != null && current.asTask().mInRemoveTask)) {
canPromote = false;
} else {
// In case a descendant of the parent belongs to the other group, we cannot promote
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 0112f797d937..f8238c1d154a 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -48,6 +48,7 @@ import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -163,16 +164,27 @@ class KeyguardController {
aodShowing ? 1 : 0,
mKeyguardGoingAway ? 1 : 0,
"setKeyguardShown");
+
+ // Update the task snapshot if the screen will not be turned off. To make sure that the
+ // unlocking animation can animate consistent content. The conditions are:
+ // - Either AOD or keyguard changes to be showing. So if the states change individually,
+ // the later one can be skipped to avoid taking snapshot again. While it still accepts
+ // if both of them change to show at the same time.
+ // - Keyguard was not going away. Because if it was, the closing transition is able to
+ // handle the snapshot.
+ // - The display state is ON. Because if AOD is not on or pulsing, the display state will
+ // be OFF or DOZE (the path of screen off may have handled it).
+ if (((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged))
+ && !mKeyguardGoingAway && Display.isOnState(
+ mRootWindowContainer.getDefaultDisplay().getDisplayInfo().state)) {
+ mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
+ }
+
mKeyguardShowing = keyguardShowing;
mAodShowing = aodShowing;
if (aodChanged) {
// Ensure the new state takes effect.
mWindowManager.mWindowPlacerLocked.performSurfacePlacement();
- // If the device can enter AOD and keyguard at the same time, the screen will not be
- // turned off, so the snapshot needs to be refreshed when these states are changed.
- if (aodShowing && keyguardShowing && keyguardChanged) {
- mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
- }
}
if (keyguardChanged) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7af8d8bb5200..565f99f80890 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6689,24 +6689,26 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- // TODO(185200798): Persist theme name instead of theme if
- int splashScreenThemeResId = options != null
- ? options.getSplashScreenThemeResId() : 0;
-
- // User can override the splashscreen theme. The theme name is used to persist
- // the setting, so if no theme is set in the ActivityOptions, we check if has
- // been persisted here.
- if (splashScreenThemeResId == 0) {
+ // Find the splash screen theme. User can override the persisted theme by
+ // ActivityOptions.
+ String splashScreenThemeResName = options != null
+ ? options.getSplashScreenThemeResName() : null;
+ if (splashScreenThemeResName == null || splashScreenThemeResName.isEmpty()) {
try {
- String themeName = mAtmService.getPackageManager()
+ splashScreenThemeResName = mAtmService.getPackageManager()
.getSplashScreenTheme(r.packageName, r.mUserId);
- if (themeName != null) {
- Context packageContext = mAtmService.mContext
- .createPackageContext(r.packageName, 0);
- splashScreenThemeResId = packageContext.getResources()
- .getIdentifier(themeName, null, null);
- }
- } catch (RemoteException | PackageManager.NameNotFoundException
+ } catch (RemoteException ignore) {
+ // Just use the default theme
+ }
+ }
+ int splashScreenThemeResId = 0;
+ if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) {
+ try {
+ final Context packageContext = mAtmService.mContext
+ .createPackageContext(r.packageName, 0);
+ splashScreenThemeResId = packageContext.getResources()
+ .getIdentifier(splashScreenThemeResName, null, null);
+ } catch (PackageManager.NameNotFoundException
| Resources.NotFoundException ignore) {
// Just use the default theme
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index f23028f6f67a..3a2ca80f2e12 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -43,6 +43,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
+import android.window.SplashScreenView;
import android.window.StartingWindowInfo;
import android.window.TaskAppearedInfo;
import android.window.TaskSnapshot;
@@ -215,6 +216,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
+ void onAppSplashScreenViewRemoved(Task task) {
+ try {
+ mTaskOrganizer.onAppSplashScreenViewRemoved(task.mTaskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onAppSplashScreenViewRemoved callback", e);
+ }
+ }
+
SurfaceControl prepareLeash(Task task, String reason) {
return new SurfaceControl(task.getSurfaceControl(), reason);
}
@@ -314,6 +323,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mOrganizer.copySplashScreenView(t);
}
+ public void onAppSplashScreenViewRemoved(Task t) {
+ mOrganizer.onAppSplashScreenViewRemoved(t);
+ }
+
/**
* Register this task with this state, but doesn't trigger the task appeared callback to
* the organizer.
@@ -566,6 +579,22 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return true;
}
+ /**
+ * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has
+ * removed the splash screen view.
+ * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int)
+ * @see SplashScreenView#remove()
+ */
+ public void onAppSplashScreenViewRemoved(Task task) {
+ final Task rootTask = task.getRootTask();
+ if (rootTask == null || rootTask.mTaskOrganizer == null) {
+ return;
+ }
+ final TaskOrganizerState state =
+ mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
+ state.onAppSplashScreenViewRemoved(task);
+ }
+
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
if (state != null && state.addTask(task)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c374f84781dd..2439760e3f67 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -169,6 +169,7 @@ import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
@@ -1748,6 +1749,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mTransferOwnershipMetadataManager = mInjector.newTransferOwnershipMetadataManager();
mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector);
+ // "Lite" interface is available even when the device doesn't have the feature
+ LocalServices.addService(DevicePolicyManagerLiteInternal.class, mLocalService);
if (!mHasFeature) {
// Skip the rest of the initialization
mSetupContentObserver = null;
@@ -12614,7 +12617,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@VisibleForTesting
- final class LocalService extends DevicePolicyManagerInternal {
+ final class LocalService extends DevicePolicyManagerInternal
+ implements DevicePolicyManagerLiteInternal {
private List<OnCrossProfileWidgetProvidersChangeListener> mWidgetProviderListeners;
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
index 86437a27a64d..1cbc634b7152 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
@@ -21,7 +21,7 @@ import static android.app.admin.DevicePolicyManager.operationToString;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
-import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.os.Handler;
import android.os.Looper;
@@ -80,8 +80,8 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker {
+ ", should be " + operationToString(mOperation));
}
String reasonName = operationSafetyReasonToString(reason);
- DevicePolicyManagerInternal dpmi = LocalServices
- .getService(DevicePolicyManagerInternal.class);
+ DevicePolicyManagerLiteInternal dpmi = LocalServices
+ .getService(DevicePolicyManagerLiteInternal.class);
Slog.i(TAG, "notifying " + reasonName + " is UNSAFE");
dpmi.notifyUnsafeOperationStateChanged(this, reason, /* isSafe= */ false);
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 21de7916e23d..280204dfd481 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -2309,12 +2309,14 @@ public class AlarmManagerServiceTest {
public void minWindowChangeDisabled() {
mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, false);
final long minWindow = 73;
+ final long futurity = 10_000;
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
// 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
for (int window = 1; window <= minWindow; window++) {
final PendingIntent pi = getNewMockPendingIntent();
- setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null);
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ TEST_CALLING_UID, null);
assertEquals(1, mService.mAlarmStore.size());
final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
@@ -2323,18 +2325,61 @@ public class AlarmManagerServiceTest {
}
@Test
+ public void minWindowExempted() {
+ mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true);
+ final long minWindow = 73;
+ final long futurity = 10_000;
+
+ setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
+
+ final int coreUid = 2312;
+ doReturn(true).when(() -> UserHandle.isCore(coreUid));
+
+ final int allowlisted = 54239;
+ when(mDeviceIdleInternal.isAppOnWhitelist(UserHandle.getAppId(allowlisted))).thenReturn(
+ true);
+
+ for (final int callingUid : new int[]{SYSTEM_UI_UID, coreUid, coreUid}) {
+ // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+ for (int window = 1; window <= minWindow; window++) {
+ final PendingIntent pi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ callingUid, null);
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(window, a.windowLength);
+ }
+ }
+
+ // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+ for (int window = 1; window <= minWindow; window++) {
+ final PendingIntent pi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ TEST_CALLING_UID, null);
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(minWindow, a.windowLength);
+ }
+ }
+
+ @Test
public void minWindowPriorityAlarm() {
mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true);
final long minWindow = 73;
+ final long futurity = 10_000;
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
// 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
for (int window = 1; window <= minWindow; window++) {
- setPrioritizedAlarm(ELAPSED_REALTIME, 0, window, new IAlarmListener.Stub() {
- @Override
- public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
- }
- });
+ setPrioritizedAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window,
+ new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(IAlarmCompleteListener callback)
+ throws RemoteException {
+ }
+ });
assertEquals(1, mService.mAlarmStore.size());
final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
assertEquals(window, a.windowLength);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index cf5db2e0db98..8532dbb7f38a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -158,7 +158,7 @@ public class MockableLocationProviderTest {
@Test
public void testSetState() {
- assertThat(mProvider.isAllowed()).isFalse();
+ assertThat(mProvider.getState().allowed).isFalse();
AbstractLocationProvider.State newState;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index fa3f45c08202..a2b1c1cb1a49 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -158,6 +159,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -271,6 +273,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -339,6 +342,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
// (Need clearCallingIdentity() to pass permission checks.)
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -499,6 +503,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
DevicePolicyManagerServiceTestable dpms;
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 5447a58a1643..cedf6361e33b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -84,6 +84,7 @@ import android.app.PendingIntent;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.PasswordMetrics;
import android.app.admin.SystemUpdatePolicy;
@@ -280,6 +281,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
private void initializeDpms() {
// Need clearCallingIdentity() to pass permission checks.
final long ident = mContext.binder.clearCallingIdentity();
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -369,10 +371,14 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.thenReturn(false);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
new DevicePolicyManagerServiceTestable(getServices(), mContext);
// If the device has no DPMS feature, it shouldn't register the local service.
assertThat(LocalServices.getService(DevicePolicyManagerInternal.class)).isNull();
+
+ // But should still register the lite one
+ assertThat(LocalServices.getService(DevicePolicyManagerLiteInternal.class)).isNotNull();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 7241fa00ecf7..90a127701505 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -253,6 +253,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
.setPerson(makePerson("person", "personKey", "personUri"))
.setLongLived(true)
.setExtras(pb)
+ .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen)
.build();
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
@@ -288,6 +289,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(null, si.getTextResName());
assertEquals(0, si.getDisabledMessageResourceId());
assertEquals(null, si.getDisabledMessageResName());
+ assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen",
+ si.getStartingThemeResName());
}
public void testShortcutInfoParcel_resId() {
@@ -308,6 +311,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
.setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
.setRank(123)
.setExtras(pb)
+ .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen)
.build();
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
@@ -339,6 +343,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(456, si.getIconResourceId());
assertEquals("string/r456", si.getIconResName());
assertEquals("test_uri", si.getIconUri());
+ assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen",
+ si.getStartingThemeResName());
}
public void testShortcutInfoClone() {
@@ -2210,6 +2216,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
android.R.drawable.alert_dark_frame, true, getTestContext().getPackageName()));
assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res,
android.R.string.cancel, false, getTestContext().getPackageName()));
+ assertEquals("" + android.R.style.Theme_Black_NoTitleBar_Fullscreen,
+ ShortcutInfo.lookUpResourceName(
+ res, android.R.style.Theme_Black_NoTitleBar_Fullscreen, true,
+ getTestContext().getPackageName()));
}
public void testLookUpResourceName_appResources() {
@@ -2236,6 +2246,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceId(res,
"" + android.R.drawable.alert_dark_frame, null,
getTestContext().getPackageName()));
+ assertEquals(android.R.style.Theme_Black_NoTitleBar_Fullscreen,
+ ShortcutInfo.lookUpResourceId(
+ res, "" + android.R.style.Theme_Black_NoTitleBar_Fullscreen,
+ null, getTestContext().getPackageName()));
}
// Test for a ShortcutInfo method.
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 5614aa2a165d..577e36c7d5db 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -394,7 +394,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
"disabledMessage", 0, "disabledMessageResName",
null, null, 0, null, 0, 0,
0, "iconResName", "bitmapPath", null, 0,
- null, null, 0);
+ null, null, null);
return si;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 3f1248a5fff7..a1b3159825fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -796,6 +796,9 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Override
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
}
+ @Override
+ public void onAppSplashScreenViewRemoved(int taskId) {
+ }
};
private ActivityRecord makePipableActivity() {